2 * This module imports an "unpacked" system. The unpacked data may come from
3 * an older version of Citadel, or a different hardware architecture, or
4 * whatever. You should only run an import when your installed system is
5 * brand new and _empty_ !!
18 #include <sys/types.h>
29 #include "sysdep_decls.h"
30 #include "citserver.h"
33 #include "dynloader.h"
40 extern struct CitContext *ContextList;
43 #define MODULE_NAME "Import an unpacked system"
44 #define MODULE_AUTHOR "Art Cancro"
45 #define MODULE_EMAIL "ajc@uncnsrd.mt-kisco.ny.us"
46 #define MAJOR_VERSION 1
47 #define MINOR_VERSION 0
49 static struct DLModule_Info info = {
59 void fpgetfield(FILE *fp, char *string)
76 void import_message(long msgnum, long msglen) {
79 msgtext = mallok(msglen);
80 if (msgtext == NULL) {
81 lprintf(3, "ERROR: cannot allocate memory\n");
82 lprintf(3, "Your data files are now corrupt.\n");
87 fread(msgtext, msglen, 1, imfp);
88 cdb_store(CDB_MSGMAIN, &msgnum, sizeof(long), msgtext, msglen);
92 void imp_floors(void) {
93 char key[256], tag[256], tval[256];
98 while(fpgetfield(imfp, key), strcasecmp(key, "endsection")) {
100 if (!strcasecmp(key, "floor")) {
101 memset(&fl, 0, sizeof(struct floor));
103 while(fpgetfield(imfp, tag),
104 strcasecmp(tag, "endfloor")) {
105 fpgetfield(imfp, tval);
107 if (!strcasecmp(tag, "f_flags"))
108 fl.f_flags = atoi(tval);
109 if (!strcasecmp(tag, "f_name")) {
110 lprintf(9, "Floor <%s>\n", tval);
111 strcpy(fl.f_name, tval);
113 if (!strcasecmp(tag, "f_ref_count"))
114 fl.f_ref_count = atoi(tval);
115 if (!strcasecmp(tag, "f_expire_mode"))
116 fl.f_ep.expire_mode = atoi(tval);
117 if (!strcasecmp(tag, "f_expire_value"))
118 fl.f_ep.expire_value = atoi(tval);
121 putfloor(&fl, floornum);
125 lprintf(3, "ERROR: invalid floor section.\n");
126 lprintf(3, "Your data files are now corrupt.\n");
135 void imp_rooms(void) {
137 char tag[256], tval[256];
143 while(fpgetfield(imfp, key), strcasecmp(key, "endsection")) {
144 if (!strcasecmp(key, "room")) {
145 memset(&qr, 0, sizeof(struct quickroom));
150 while(fpgetfield(imfp, tag),
151 strcasecmp(tag, "endroom")) {
152 if (strcasecmp(tag, "message")) {
153 fpgetfield(imfp, tval);
159 if (!strcasecmp(tag, "qrname")) {
160 strcpy(qr.QRname, tval);
161 lprintf(9, "<%s> ", qr.QRname);
163 if (!strcasecmp(tag, "qrnumber"))
164 qr.QRnumber = atol(tval);
165 if (!strcasecmp(tag, "qrpasswd"))
166 strcpy(qr.QRpasswd, tval);
167 if (!strcasecmp(tag, "qrroomaide"))
168 qr.QRroomaide = atol(tval);
169 if (!strcasecmp(tag, "qrhighest"))
170 qr.QRhighest = atol(tval);
171 if (!strcasecmp(tag, "qrgen"))
172 qr.QRgen = atol(tval);
173 if (!strcasecmp(tag, "qrflags"))
174 qr.QRflags = atoi(tval);
175 if (!strcasecmp(tag, "qrdirname"))
176 strcpy(qr.QRdirname, tval);
177 if (!strcasecmp(tag, "qrinfo"))
178 qr.QRinfo = atol(tval);
179 if (!strcasecmp(tag, "qrfloor"))
180 qr.QRfloor = atoi(tval);
181 if (!strcasecmp(tag, "qrmtime"))
182 qr.QRmtime = atol(tval);
183 if (!strcasecmp(tag, "qrepmode"))
184 qr.QRep.expire_mode = atoi(tval);
185 if (!strcasecmp(tag, "qrepvalue"))
186 qr.QRep.expire_value = atoi(tval);
187 if (!strcasecmp(tag, "message")) {
188 fpgetfield(imfp, tval);
190 fpgetfield(imfp, tval);
192 import_message(msgnum, msglen);
194 msglist = reallok(msglist,
195 (sizeof(long)*num_msgs) );
196 msglist[num_msgs - 1] = msgnum;
201 lprintf(9, "(%d messages)\n", num_msgs);
202 if (qr.QRflags&QR_INUSE) {
203 putroom(&qr, qr.QRname);
207 if (qr.QRflags&QR_INUSE) {
208 CC->msglist = msglist;
209 CC->num_msgs = num_msgs;
217 lprintf(3, "ERROR: invalid room section.\n");
218 lprintf(3, "Your data files are now corrupt.\n");
227 void import_a_user(void) {
228 char key[256], value[256];
231 memset(&us, 0, sizeof(struct usersupp));
232 while(fpgetfield(imfp, key), strcasecmp(key, "enduser")) {
233 if (strcasecmp(key, "mail")) {
234 fpgetfield(imfp, value);
240 if (!strcasecmp(key, "usuid"))
241 us.USuid = atoi(value);
242 if (!strcasecmp(key, "password")) {
243 strcpy(us.password, value);
246 if (!strcasecmp(key, "flags"))
247 us.flags = atoi(value);
248 if (!strcasecmp(key, "timescalled"))
249 us.timescalled = atoi(value);
250 if (!strcasecmp(key, "posted"))
251 us.posted = atoi(value);
252 if (!strcasecmp(key, "fullname")) {
253 strcpy(us.fullname, value);
254 lprintf(9, "User <%s> ", us.fullname);
256 if (!strcasecmp(key, "axlevel"))
257 us.axlevel = atoi(value);
258 if (!strcasecmp(key, "usscreenwidth"))
259 us.USscreenwidth = atoi(value);
260 if (!strcasecmp(key, "usscreenheight"))
261 us.USscreenheight = atoi(value);
262 if (!strcasecmp(key, "usernum")) {
263 us.usernum = atol(value);
264 lprintf(9, "<#%ld> ", us.usernum);
266 if (!strcasecmp(key, "lastcall"))
267 us.lastcall = atol(value);
268 if (!strcasecmp(key, "usname"))
269 strcpy(us.USname, value);
270 if (!strcasecmp(key, "usaddr"))
271 strcpy(us.USaddr, value);
272 if (!strcasecmp(key, "uscity"))
273 strcpy(us.UScity, value);
274 if (!strcasecmp(key, "usstate"))
275 strcpy(us.USstate, value);
276 if (!strcasecmp(key, "uszip"))
277 strcpy(us.USzip, value);
278 if (!strcasecmp(key, "usphone"))
279 strcpy(us.USphone, value);
280 if (!strcasecmp(key, "usemail"))
281 strcpy(us.USemail, value);
282 if (!strcasecmp(key, "ususerpurge")) {
283 us.USuserpurge = atoi(value);
287 putuser(&us, us.fullname);
293 void imp_usersupp(void) {
294 char key[256], value[256];
296 while(fpgetfield(imfp, key), strcasecmp(key, "endsection")) {
297 if (strcasecmp(key, "user")) {
298 fpgetfield(imfp, value);
304 if (!strcasecmp(key, "user")) {
311 void imp_globals(void) {
312 char key[256], value[256];
315 while(fpgetfield(imfp, key), strcasecmp(key, "endsection")) {
316 fpgetfield(imfp, value);
317 lprintf(9, " %s = %s\n", key, value);
319 if (!strcasecmp(key, "mmhighest"))
320 CitControl.MMhighest = atol(value);
321 if (!strcasecmp(key, "mmnextuser"))
322 CitControl.MMnextuser = atol(value);
323 if (!strcasecmp(key, "mmnextroom"))
324 CitControl.MMnextroom = atol(value);
332 void imp_config(void) {
333 char key[256], value[256];
337 while(fpgetfield(imfp, key), strcasecmp(key, "endsection")) {
338 fpgetfield(imfp, value);
339 lprintf(9, " %s = %s\n", key, value);
341 if (!strcasecmp(key, "c_nodename"))
342 strcpy(config.c_nodename, value);
343 if (!strcasecmp(key, "c_fqdn"))
344 strcpy(config.c_fqdn, value);
345 if (!strcasecmp(key, "c_humannode"))
346 strcpy(config.c_humannode, value);
347 if (!strcasecmp(key, "c_phonenum"))
348 strcpy(config.c_phonenum, value);
349 if (!strcasecmp(key, "c_phonenum"))
350 strcpy(config.c_phonenum, value);
351 if (!strcasecmp(key, "c_bbsuid"))
352 config.c_bbsuid = atoi(value);
353 if (!strcasecmp(key, "c_creataide"))
354 config.c_creataide = atoi(value);
355 if (!strcasecmp(key, "c_sleeping"))
356 config.c_sleeping = atoi(value);
357 if (!strcasecmp(key, "c_initax"))
358 config.c_initax = atoi(value);
359 if (!strcasecmp(key, "c_regiscall"))
360 config.c_regiscall = atoi(value);
361 if (!strcasecmp(key, "c_twitdetect"))
362 config.c_twitdetect = atoi(value);
363 if (!strcasecmp(key, "c_twitroom"))
364 strcpy(config.c_twitroom, value);
365 if (!strcasecmp(key, "c_moreprompt"))
366 strcpy(config.c_moreprompt, value);
367 if (!strcasecmp(key, "c_restrict"))
368 config.c_restrict = atoi(value);
369 if (!strcasecmp(key, "c_bbs_city"))
370 strcpy(config.c_bbs_city, value);
371 if (!strcasecmp(key, "c_sysadm"))
372 strcpy(config.c_sysadm, value);
373 if (!strcasecmp(key, "c_bucket_dir"))
374 strcpy(config.c_bucket_dir, value);
375 if (!strcasecmp(key, "c_setup_level"))
376 config.c_setup_level = atoi(value);
377 if (!strcasecmp(key, "c_maxsessions"))
378 config.c_maxsessions = atoi(value);
379 if (!strcasecmp(key, "c_net_password"))
380 strcpy(config.c_net_password, value);
381 if (!strcasecmp(key, "c_port_number"))
382 config.c_port_number = atoi(value);
383 if (!strcasecmp(key, "c_expire_policy"))
384 config.c_ep.expire_mode = atoi(value);
385 if (!strcasecmp(key, "c_expire_value"))
386 config.c_ep.expire_value = atoi(value);
387 if (!strcasecmp(key, "c_userpurge"))
388 config.c_userpurge = atoi(value);
389 if (!strcasecmp(key, "c_roompurge"))
390 config.c_roompurge = atoi(value);
393 fd = open("citadel.config", O_WRONLY | O_TRUNC);
394 fp = fdopen(fd, "wb");
395 fwrite(&config, sizeof(struct config), 1, fp);
404 char key[256], value[256];
405 int ssv_maxfloors = MAXFLOORS;
407 while(fpgetfield(imfp, key), strcasecmp(key, "endsection")) {
408 fpgetfield(imfp, value);
409 lprintf(9, " %s = %s\n", key, value);
411 if (!strcasecmp(key, "maxfloors")) {
412 ssv_maxfloors = atol(value);
413 if (ssv_maxfloors > MAXFLOORS) {
414 lprintf(3, "ERROR: maxfloors is %d, need %d\n",
415 ssv_maxfloors, MAXFLOORS);
423 void imp_visits(void) {
424 char key[256], value[256];
429 while(fpgetfield(imfp, key), strcasecmp(key, "endsection")) {
430 lprintf(9, "%s: ", key);
431 while(fpgetfield(imfp, key),
432 strcasecmp(key, "endvisit")) {
433 fpgetfield(imfp, value);
434 if (!strcasecmp(key, "vrnum")) {
435 qr.QRnumber = atol(value);
436 visit.v_roomnum = atol(value);
438 else if (!strcasecmp(key, "vgen")) {
439 qr.QRgen = atol(value);
440 visit.v_roomgen = atol(value);
442 else if (!strcasecmp(key, "vunum")) {
443 us.usernum = atol(value);
444 visit.v_usernum = atol(value);
446 else if (!strcasecmp(key, "lastseen")) {
447 visit.v_lastseen = atol(value);
449 else if (!strcasecmp(key, "flags")) {
450 visit.v_flags = atol(value);
453 lprintf(9, "<%ld><%ld><%ld> <%d> <%ld>\n",
454 visit.v_roomnum, visit.v_roomgen,
456 visit.v_flags, visit.v_lastseen);
457 CtdlSetRelationship(&visit, &us, &qr);
463 void import_databases(void) {
466 lprintf(9, " ** IMPORTING ** \n");
467 while (fpgetfield(imfp, section), strcasecmp(section, "endfile")) {
468 lprintf(9, "Section: <%s>\n", section);
470 if (!strcasecmp(section, "ssv")) imp_ssv();
471 else if (!strcasecmp(section, "config")) imp_config();
472 else if (!strcasecmp(section, "globals")) imp_globals();
473 else if (!strcasecmp(section, "usersupp")) imp_usersupp();
474 else if (!strcasecmp(section, "rooms")) imp_rooms();
475 else if (!strcasecmp(section, "floors")) imp_floors();
476 else if (!strcasecmp(section, "visits")) imp_visits();
478 lprintf(3, "ERROR: invalid import section.\n");
479 lprintf(3, "Your data files are now corrupt.\n");
490 void do_import(char *argbuf) {
491 char import_filename[PATH_MAX];
493 if (CC->internal_pgm == 0) {
494 cprintf("%d This command is for internal programs only.\n",
499 extract(import_filename, argbuf, 0);
500 imfp = fopen(import_filename, "rb");
502 lprintf(9, "Cannot open %s: %s\n",
503 import_filename, strerror(errno));
504 cprintf("%d Cannot open file\n", ERROR);
509 lprintf(9, "Defragmenting databases (this may take a while)...\n");
511 lprintf(1, "Import is finished. Shutting down Citadel...\n");
512 cprintf("%d Import finished. Shutting down Citadel...\n", OK);
517 void dump_message(long msg_num) {
518 struct cdbdata *dmsgtext;
520 dmsgtext = cdb_fetch(CDB_MSGMAIN, &msg_num, sizeof(long));
522 if (dmsgtext == NULL) {
523 lprintf(9, "%d Can't find message %ld\n", msg_num);
527 fprintf(exfp, "message%c%ld%c", 0, msg_num, 0);
528 fprintf(exfp, "%ld%c", (long)dmsgtext->len, 0);
529 fwrite(dmsgtext->ptr, dmsgtext->len, 1, exfp);
535 void export_a_room(struct quickroom *qr) {
539 lprintf(9,"<%s>\n", qr->QRname);
540 fprintf(exfp, "room%c", 0);
541 fprintf(exfp, "qrname%c%s%c", 0, qr->QRname, 0);
542 fprintf(exfp, "qrnumber%c%ld%c", 0, qr->QRnumber, 0);
543 fprintf(exfp, "qrpasswd%c%s%c", 0, qr->QRpasswd, 0);
544 fprintf(exfp, "qrroomaide%c%ld%c", 0, qr->QRroomaide, 0);
545 fprintf(exfp, "qrhighest%c%ld%c", 0, qr->QRhighest, 0);
546 fprintf(exfp, "qrgen%c%ld%c", 0, (long)qr->QRgen, 0);
547 fprintf(exfp, "qrflags%c%d%c", 0, qr->QRflags, 0);
548 fprintf(exfp, "qrdirname%c%s%c", 0, qr->QRdirname, 0);
549 fprintf(exfp, "qrinfo%c%ld%c", 0, qr->QRinfo, 0);
550 fprintf(exfp, "qrfloor%c%d%c", 0, qr->QRfloor, 0);
551 fprintf(exfp, "qrmtime%c%ld%c", 0, (long)qr->QRmtime, 0);
552 fprintf(exfp, "qrepmode%c%d%c", 0, qr->QRep.expire_mode, 0);
553 fprintf(exfp, "qrepvalue%c%d%c", 0, qr->QRep.expire_value, 0);
556 if (CC->num_msgs > 0) for (b=0; b<(CC->num_msgs); ++b) {
558 lprintf(9, "Message #%ld\n", MessageFromList(b));
559 dump_message(MessageFromList(b));
562 fprintf(exfp, "endroom%c", 0);
566 void export_rooms(void) {
567 lprintf(9,"Rooms\n");
568 fprintf(exfp, "rooms%c", 0);
569 ForEachRoom(export_a_room);
570 fprintf(exfp, "endsection%c", 0);
575 void export_floors(void) {
579 fprintf(exfp, "floors%c", 0);
580 for (floornum=0; floornum<MAXFLOORS; ++floornum) {
581 getfloor(&fl, floornum);
582 fprintf(exfp, "floor%c", 0);
583 fprintf(exfp, "f_flags%c%d%c", 0, fl.f_flags, 0);
584 fprintf(exfp, "f_name%c%s%c", 0, fl.f_name, 0);
585 fprintf(exfp, "f_ref_count%c%d%c", 0, fl.f_ref_count, 0);
586 fprintf(exfp, "f_expire_mode%c%d%c", 0, fl.f_ep.expire_mode, 0);
587 fprintf(exfp, "f_expire_value%c%d%c", 0,
588 fl.f_ep.expire_value, 0);
589 fprintf(exfp, "endfloor%c", 0);
591 fprintf(exfp, "endsection%c", 0);
597 void export_a_user(struct usersupp *us) {
599 lprintf(9, "User <%s> ", us->fullname);
601 fprintf(exfp, "user%c", 0);
602 fprintf(exfp, "usuid%c%d%c", 0, us->USuid, 0);
603 fprintf(exfp, "password%c%s%c", 0, us->password, 0);
604 fprintf(exfp, "flags%c%d%c", 0, us->flags, 0);
605 fprintf(exfp, "timescalled%c%d%c", 0, us->timescalled, 0);
606 fprintf(exfp, "posted%c%d%c", 0, us->posted, 0);
607 fprintf(exfp, "fullname%c%s%c", 0, us->fullname, 0);
608 fprintf(exfp, "axlevel%c%d%c", 0, us->axlevel, 0);
609 fprintf(exfp, "usscreenwidth%c%d%c", 0, us->USscreenwidth, 0);
610 fprintf(exfp, "usscreenheight%c%d%c", 0, us->USscreenheight, 0);
611 fprintf(exfp, "usernum%c%ld%c", 0, us->usernum, 0);
612 fprintf(exfp, "lastcall%c%ld%c", 0, (long)us->lastcall, 0);
613 fprintf(exfp, "usname%c%s%c", 0, us->USname, 0);
614 fprintf(exfp, "usaddr%c%s%c", 0, us->USaddr, 0);
615 fprintf(exfp, "uscity%c%s%c", 0, us->UScity, 0);
616 fprintf(exfp, "usstate%c%s%c", 0, us->USstate, 0);
617 fprintf(exfp, "uszip%c%s%c", 0, us->USzip, 0);
618 fprintf(exfp, "usphone%c%s%c", 0, us->USphone, 0);
619 fprintf(exfp, "usemail%c%s%c", 0, us->USemail, 0);
620 fprintf(exfp, "ususerpurge%c%d%c", 0, us->USuserpurge, 0);
623 fprintf(exfp, "enduser%c", 0);
627 void export_usersupp(void) {
628 lprintf(9, "Users\n");
629 fprintf(exfp, "usersupp%c", 0);
631 ForEachUser(export_a_user);
633 fprintf(exfp, "endsection%c", 0);
637 void export_visits(void) {
638 struct cdbdata *cdbvisit;
641 lprintf(9, "Visits\n");
642 fprintf(exfp, "visits%c", 0);
644 cdb_rewind(CDB_VISIT);
645 while(cdbvisit = cdb_next_item(CDB_VISIT), cdbvisit != NULL) {
646 memset(&vbuf, 0, sizeof(struct visit));
647 memcpy(&vbuf, cdbvisit->ptr,
648 ( (cdbvisit->len > sizeof(struct visit)) ?
649 sizeof(struct visit) : cdbvisit->len) );
652 fprintf(exfp, "visit%c", 0);
653 fprintf(exfp, "vrnum%c%ld%c", 0, vbuf.v_roomnum, 0);
654 fprintf(exfp, "vgen%c%ld%c", 0, vbuf.v_roomgen, 0);
655 fprintf(exfp, "vunum%c%ld%c", 0, vbuf.v_usernum, 0);
656 fprintf(exfp, "flags%c%d%c", 0, vbuf.v_flags, 0);
657 fprintf(exfp, "lastseen%c%ld%c", 0, vbuf.v_lastseen, 0);
658 fprintf(exfp, "endvisit%c", 0);
661 fprintf(exfp, "endsection%c", 0);
664 void do_export(char *argbuf) {
665 char export_filename[PATH_MAX];
667 if (CC->internal_pgm == 0) {
668 cprintf("%d This command is for internal programs only.\n",
673 extract(export_filename, argbuf, 0);
674 exfp = fopen(export_filename, "wb");
676 lprintf(9, "Cannot open %s: %s\n",
677 export_filename, strerror(errno));
678 cprintf("%d Cannot open file\n", ERROR);
682 /* structure size variables */
683 lprintf(9, "Structure size variables\n");
684 fprintf(exfp, "ssv%c", 0);
685 fprintf(exfp, "maxfloors%c%d%c", 0, MAXFLOORS, 0);
686 fprintf(exfp, "endsection%c", 0);
688 /* Write out the server config */
689 lprintf(9,"Server config\n");
690 fprintf(exfp, "config%c", 0);
691 fprintf(exfp, "c_nodename%c%s%c", 0, config.c_nodename, 0);
692 fprintf(exfp, "c_fqdn%c%s%c", 0, config.c_fqdn, 0);
693 fprintf(exfp, "c_humannode%c%s%c", 0, config.c_humannode, 0);
694 fprintf(exfp, "c_phonenum%c%s%c", 0, config.c_phonenum, 0);
695 fprintf(exfp, "c_bbsuid%c%d%c", 0, config.c_bbsuid, 0);
696 fprintf(exfp, "c_creataide%c%d%c", 0, config.c_creataide, 0);
697 fprintf(exfp, "c_sleeping%c%d%c", 0, config.c_sleeping, 0);
698 fprintf(exfp, "c_initax%c%d%c", 0, config.c_initax, 0);
699 fprintf(exfp, "c_regiscall%c%d%c", 0, config.c_regiscall, 0);
700 fprintf(exfp, "c_twitdetect%c%d%c", 0, config.c_twitdetect, 0);
701 fprintf(exfp, "c_twitroom%c%s%c", 0, config.c_twitroom, 0);
702 fprintf(exfp, "c_moreprompt%c%s%c", 0, config.c_moreprompt, 0);
703 fprintf(exfp, "c_restrict%c%d%c", 0, config.c_restrict, 0);
704 fprintf(exfp, "c_bbs_city%c%s%c", 0, config.c_bbs_city, 0);
705 fprintf(exfp, "c_sysadm%c%s%c", 0, config.c_sysadm, 0);
706 fprintf(exfp, "c_bucket_dir%c%s%c", 0, config.c_bucket_dir, 0);
707 fprintf(exfp, "c_setup_level%c%d%c", 0, config.c_setup_level, 0);
708 fprintf(exfp, "c_maxsessions%c%d%c", 0, config.c_maxsessions, 0);
709 fprintf(exfp, "c_net_password%c%s%c", 0, config.c_net_password, 0);
710 fprintf(exfp, "c_port_number%c%d%c", 0, config.c_port_number, 0);
711 fprintf(exfp, "c_expire_policy%c%d%c", 0, config.c_ep.expire_mode, 0);
712 fprintf(exfp, "c_expire_value%c%d%c", 0, config.c_ep.expire_value, 0);
713 fprintf(exfp, "c_userpurge%c%d%c", 0, config.c_userpurge, 0);
714 fprintf(exfp, "c_roompurge%c%d%c", 0, config.c_roompurge, 0);
715 fprintf(exfp, "endsection%c", 0);
717 /* Now some global stuff */
718 lprintf(9, "Globals\n");
720 fprintf(exfp, "globals%c", 0);
721 fprintf(exfp, "mmhighest%c%ld%c", 0, CitControl.MMhighest, 0);
722 fprintf(exfp, "mmnextuser%c%ld%c", 0, CitControl.MMnextuser, 0);
723 fprintf(exfp, "mmnextroom%c%ld%c", 0, CitControl.MMnextroom, 0);
724 fprintf(exfp, "mmflags%c%d%c", 0, CitControl.MMflags, 0);
725 fprintf(exfp, "endsection%c", 0);
727 /* Export all of the databases */
733 fprintf(exfp, "endfile%c", 0);
735 lprintf(1, "Export is finished.\n");
736 cprintf("%d Export is finished.\n", OK);
742 struct DLModule_Info *Dynamic_Module_Init(void) {
743 CtdlRegisterProtoHook(do_import,
745 "Import an unpacked system");
746 CtdlRegisterProtoHook(do_export,
748 "Export the system");