source: tags/ms_r17q3/NAMES/names.cxx

Last change on this file was 16552, checked in by westram, 7 years ago
  • partial merge from 'io' into 'trunk'
    • GB_rename_fileGB_move_file
    • add GB_copy_file
    • add safe versions of both
  • adds: log:branches/io@16527:16532
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.7 KB
Line 
1// ============================================================= //
2//                                                               //
3//   File      : names.cxx                                       //
4//   Purpose   :                                                 //
5//                                                               //
6//   Institute of Microbiology (Technical University Munich)     //
7//   http://www.arb-home.de/                                     //
8//                                                               //
9// ============================================================= //
10
11#include <names_server.h>
12#include <names_client.h>
13#include "names.h"
14
15#include <arbdb.h>
16#include <arb_file.h>
17#include <arb_sleep.h>
18
19#include <names_prototypes.h>
20#include <server.h>
21#include <client.h>
22#include <servercntrl.h>
23#include <struct_man.h>
24
25#include <unistd.h>
26#include <cctype>
27#include <list>
28#include <string>
29
30#define na_assert(cond) arb_assert(cond)
31
32#define FULLNAME_LEN_MAX 64
33#define NAME_LEN_MIN 2
34
35using namespace std;
36
37// --------------------------------------------------------------------------------
38
39// overloaded functions to avoid problems with type-punning:
40inline long aisc_find_lib(dll_public *dll, char *key) { return aisc_find_lib(reinterpret_cast<dllpublic_ext*>(dll), key); }
41
42inline void aisc_link(dll_public *dll, AN_shorts *shorts)   { aisc_link(reinterpret_cast<dllpublic_ext*>(dll), reinterpret_cast<dllheader_ext*>(shorts)); }
43inline void aisc_link(dll_public *dll, AN_revers *revers)   { aisc_link(reinterpret_cast<dllpublic_ext*>(dll), reinterpret_cast<dllheader_ext*>(revers)); }
44
45// --------------------------------------------------------------------------------
46
47#if defined(DEBUG)
48// #define DUMP_NAME_CREATION
49#endif // DEBUG
50
51#define UPPERCASE(c) do { (c) = toupper(c); } while (0)
52
53// --------------------------------------------------------------------------------
54
55struct AN_gl_struct {
56    aisc_com  *cl_link;
57    Hs_struct *server_communication;
58    T_AN_MAIN  cl_main;
59    char      *server_name;
60};
61
62
63static struct AN_gl_struct  AN_global;
64AN_main             *aisc_main; // muss so heissen
65
66const int SERVER_VERSION = 5;
67
68// --------------------------------------------------------------------------------
69
70inline char *an_strlwr(char *str) {
71    for (int i = 0; str[i]; i++) {
72        str[i] = tolower(str[i]);
73    }
74    return str;
75}
76
77inline int an_strnicmp(const char *s1, const char *s2, int len) {
78    int cmp = 0;
79
80    for (int i = 0; i<len; i++) {
81        cmp = tolower(s1[i])-tolower(s2[i]);
82        if (cmp || !s1[i]) break;      // different or string-end reached in both strings
83    }
84
85    return cmp;
86}
87
88inline int an_stricmp(const char *s1, const char *s2) {
89    int cmp = 0;
90
91    for (int i = 0; ; i++) {
92        cmp = tolower(s1[i])-tolower(s2[i]);
93        if (cmp || !s1[i]) break;      // different or string-end reached in both strings
94    }
95
96    return cmp;
97}
98
99
100static AN_revers *lookup_an_revers(AN_main *main, const char *shortname) {
101    char      *key        = an_strlwr(ARB_strdup(shortname));
102    AN_revers *an_reverse = (AN_revers*)aisc_find_lib(&main->prevers, key);
103
104    free(key);
105
106    return an_reverse;
107}
108
109static AN_shorts *lookup_an_shorts(AN_main *main, const char *identifier) {
110    // 'identifier' is either '*acc*add_id' or 'name1*name2*S' (see get_short() for details)
111    // 'add_id' is the value of an additional DB field and may be empty.
112
113    char      *key       = an_strlwr(ARB_strdup(identifier));
114    AN_shorts *an_shorts = (AN_shorts*)aisc_find_lib(&main->pnames, key);
115
116    free(key);
117
118    return an_shorts;
119}
120
121// ----------------------------------------
122// prefix hash
123
124static size_t an_shorts_elems(AN_shorts *sin) {
125    size_t count = 0;
126    while (sin) {
127        sin = sin->next;
128        count++;
129    }
130    return count;
131}
132
133#define PREFIXLEN 3
134
135static GB_HASH *an_get_prefix_hash() {
136    if (!aisc_main->prefix_hash) {
137        AN_shorts *sin       = aisc_main->shorts1;
138        size_t     elems     = an_shorts_elems(sin);
139        if (elems<100) elems = 100;
140
141        GB_HASH *hash = GBS_create_hash(elems, GB_IGNORE_CASE);
142
143        while (sin) {
144            GBS_write_hash_no_strdup(hash, ARB_strndup(sin->shrt, PREFIXLEN), (long)sin);
145            sin = sin->next;
146        }
147
148        aisc_main->prefix_hash = (long)hash;
149    }
150    return (GB_HASH*)aisc_main->prefix_hash;
151}
152
153static const char *an_make_prefix(const char *str) {
154    static char buf[] = "xxx";
155
156    buf[0] = str[0];
157    buf[1] = str[1];
158    buf[2] = str[2];
159
160    return buf;
161}
162
163static AN_shorts *an_find_shrt_prefix(const char *search) {
164    return (AN_shorts*)GBS_read_hash(an_get_prefix_hash(), an_make_prefix(search));
165}
166
167// ----------------------------------------
168
169static void an_add_short(const AN_local */*locs*/, const char *new_name,
170                         const char *parsed_name, const char *parsed_sym,
171                         const char *shrt, const char *acc, const char *add_id)
172{
173    AN_shorts *an_shorts;
174    AN_revers *an_revers;
175    char      *full_name;
176
177    if (strlen(parsed_sym)) {
178        ARB_calloc(full_name, strlen(parsed_name) + strlen(" sym")+1);
179        sprintf(full_name, "%s sym", parsed_name);
180    }
181    else {
182        full_name = ARB_strdup(parsed_name);
183    }
184
185    an_shorts = create_AN_shorts();
186    an_revers = create_AN_revers();
187
188    an_shorts->mh.ident  = an_strlwr(ARB_strdup(new_name));
189    an_shorts->shrt      = ARB_strdup(shrt);
190    an_shorts->full_name = ARB_strdup(full_name);
191    an_shorts->acc       = ARB_strdup(acc);
192    an_shorts->add_id    = ARB_strdup(add_id);
193
194    aisc_link(&aisc_main->pnames, an_shorts);
195
196    an_revers->mh.ident  = an_strlwr(ARB_strdup(shrt));
197    an_revers->full_name = full_name;
198    an_revers->acc       = ARB_strdup(acc);
199    an_revers->add_id    = ARB_strdup(add_id);
200
201    aisc_link(&aisc_main->prevers, an_revers);
202
203    GB_HASH *phash = an_get_prefix_hash();
204    GBS_write_hash(phash, an_make_prefix(an_shorts->shrt), (long)an_shorts); // add an_shorts to hash
205    GBS_optimize_hash(phash);
206
207    aisc_main->touched = 1;
208}
209
210static void an_remove_short(AN_shorts *an_shorts) {
211    /* this should only be used to remove illegal entries from name-server.
212       normally removing names does make problems - so use it very rarely */
213
214    GBS_write_hash(an_get_prefix_hash(), an_make_prefix(an_shorts->shrt), 0); // delete an_shorts from hash
215
216    AN_revers *an_revers = lookup_an_revers(aisc_main, an_shorts->shrt);
217
218    if (an_revers) {
219        aisc_unlink((dllheader_ext*)an_revers);
220
221        free(an_revers->mh.ident);
222        free(an_revers->full_name);
223        free(an_revers->acc);
224        free(an_revers);
225    }
226
227    aisc_unlink((dllheader_ext*)an_shorts);
228
229    free(an_shorts->mh.ident);
230    free(an_shorts->shrt);
231    free(an_shorts->full_name);
232    free(an_shorts->acc);
233    free(an_shorts);
234}
235
236static char *nas_string_2_name(const char *str) {
237    // converts a string to a valid name
238#if defined(DUMP_NAME_CREATION)
239    const char *org_str = str;
240#endif // DUMP_NAME_CREATION
241
242    char buf[FULLNAME_LEN_MAX+1];
243    int  i;
244    int  c;
245    for (i=0; i<FULLNAME_LEN_MAX;) {
246        c                        = *(str++);
247        if (!c) break;
248        if (isalpha(c)) buf[i++] = c;
249    }
250    for (; i<NAME_LEN_MIN; i++) buf[i] = '0';
251    buf[i] = 0;
252#if defined(DUMP_NAME_CREATION)
253    printf("nas_string_2_name('%s') = '%s'\n", org_str, buf);
254#endif // DUMP_NAME_CREATION
255    return ARB_strdup(buf);
256}
257
258static char *nas_remove_small_vocals(const char *str) {
259#if defined(DUMP_NAME_CREATION)
260    const char *org_str = str;
261#endif // DUMP_NAME_CREATION
262    char buf[FULLNAME_LEN_MAX+1];
263    int i;
264    int c;
265
266    for (i=0; i<FULLNAME_LEN_MAX;) {
267        c = *str++;
268        if (!c) break;
269        if (strchr("aeiouy", c)==0) {
270            buf[i++] = c;
271        }
272    }
273    for (; i<NAME_LEN_MIN; i++) buf[i] = '0';
274    buf[i] = 0;
275#if defined(DUMP_NAME_CREATION)
276    printf("nas_remove_small_vocals('%s') = '%s'\n", org_str, buf);
277#endif // DUMP_NAME_CREATION
278    return ARB_strdup(buf);
279}
280
281static void an_complete_shrt(char *shrt, const char *rest_of_full) {
282    int len = strlen(shrt);
283
284    while (len<5) {
285        char c = *rest_of_full++;
286
287        if (!c) break;
288        shrt[len++] = c;
289    }
290
291    while (len<NAME_LEN_MIN) {
292        shrt[len++] = '0';
293    }
294
295    shrt[len] = 0;
296}
297
298static void an_autocaps(char *str) {
299    // automatically capitalizes a string if it is completely up- or downcase
300
301    bool is_single_case = true;
302    {
303        bool seen_upper_case = false;
304        bool seen_lower_case = false;
305        for (int i = 0; str[i] && is_single_case; i++) {
306            char c = str[i];
307            if (isalpha(c)) {
308                if (islower(c)) seen_lower_case = true;
309                else seen_upper_case            = true;
310
311                if (seen_lower_case == seen_upper_case) { // both cases occurred
312                    is_single_case = false;
313                }
314            }
315        }
316    }
317
318    if (is_single_case) {
319        bool next_is_capital = true;
320
321        for (int i = 0; str[i]; i++) {
322            char c = str[i];
323            if (isalnum(c)) {
324                if (next_is_capital) {
325                    str[i]          = toupper(c);
326                    next_is_capital = false;
327                }
328                else {
329                    str[i] = tolower(c);
330                }
331            }
332            else {
333                next_is_capital = true;
334            }
335        }
336    }
337}
338
339static char *an_get_short(AN_shorts *IF_ASSERTION_USED(shorts), dll_public *parent, const char *full) {
340    AN_shorts *look;
341
342    na_assert(full);
343    na_assert(shorts == aisc_main->shorts1); // otherwise prefix_hash does not work!
344
345    if (full[0]==0) return ARB_strdup("ZZZ");
346
347    const char *result = 0;
348    char *full1  = ARB_strdup(full);
349    an_autocaps(full1);
350
351    char *full2 = nas_string_2_name(full1);
352
353    look = (AN_shorts *)aisc_find_lib((dllpublic_ext*)parent, full2);
354    if (look) {                 // name is already known
355        free(full2);
356        free(full1);
357        return ARB_strdup(look->shrt);
358    }
359
360    char *full3 = 0;
361    char  shrt[10];
362    int   len2, len3;
363    int   p1, p2, p3;
364
365    // try first three letters:
366
367    strncpy(shrt, full2, 3);
368    UPPERCASE(shrt[0]);
369    shrt[3] = 0;
370
371    look = an_find_shrt_prefix(shrt);
372    if (!look) {
373        len2   = strlen(full2);
374        an_complete_shrt(shrt, len2>=3 ? full2+3 : "");
375        goto found_short;
376    }
377
378    // generate names from first char + consonants:
379
380    full3 = nas_remove_small_vocals(full2);
381    len3 = strlen(full3);
382
383    for (p1=1; p1<(len3-1); p1++) {
384        shrt[1] = full3[p1];
385        for (p2=p1+1; p2<len3; p2++) {
386            shrt[2] = full3[p2];
387            look = an_find_shrt_prefix(shrt);
388            if (!look) {
389                an_complete_shrt(shrt, full3+p2+1);
390                goto found_short;
391            }
392        }
393    }
394
395    // generate names from first char + rest characters:
396
397    len2 = strlen(full2);
398    for (p1=1; p1<(len2-1); p1++) {
399        shrt[1] = full2[p1];
400        for (p2=p1+1; p2<len2; p2++) {
401            shrt[2] = full2[p2];
402            look = an_find_shrt_prefix(shrt);
403            if (!look) {
404                an_complete_shrt(shrt, full2+p2+1);
405                goto found_short;
406            }
407        }
408    }
409
410    // generate names containing first char + character from name + one digit:
411
412    for (p1=1; p1<len2; p1++) {
413        shrt[1] = full2[p1];
414        for (p2=0; p2<=9; p2++) {
415            shrt[2] = '0'+p2;
416            look = an_find_shrt_prefix(shrt);
417            if (!look) {
418                an_complete_shrt(shrt, full2+p1+1);
419                goto found_short;
420            }
421        }
422    }
423
424    // generate names containing first char + two digits:
425
426    for (p1=1; p1<=99; p1++) {
427        shrt[1] = '0'+(p1/10);
428        shrt[2] = '0'+(p1%10);
429        look = an_find_shrt_prefix(shrt);
430        if (!look) {
431            an_complete_shrt(shrt, full2+1);
432            goto found_short;
433        }
434    }
435
436    // failed to produce sth with given name, generate something random now
437
438    {
439        // use digits first, then use upper-case alpha (methods above use lower-case alpha)
440        const char *allowed = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
441        const int   len     = 36;
442
443        for (p1='A'; p1<='Z'; p1++) { // first character has to be alpha
444            shrt[0] = p1;
445            for (p2 = 0; p2<len; p2++) {
446                shrt[1] = allowed[p2];
447                for (p3 = 0; p3<len; p3++) {
448                    shrt[2] = allowed[p3];
449                    look = an_find_shrt_prefix(shrt);
450                    if (!look) {
451                        an_complete_shrt(shrt, full2);
452                        goto found_short;
453                    }
454                }
455            }
456        }
457    }
458
459    shrt[0] = 0; // erase result
460   
461 found_short :
462    result = shrt;
463
464    if (result && result[0]) {
465#if defined(DUMP_NAME_CREATION)
466        if (isdigit(result[0]) || isdigit(result[1])) {
467            printf("generated new short-name '%s' for full-name '%s' full2='%s' full3='%s'\n", shrt, full, full2, full3);
468        }
469#endif    // DUMP_NAME_CREATION
470
471        look           = create_AN_shorts();
472        look->mh.ident = ARB_strdup(full2);
473        look->shrt     = ARB_strdup(result);
474        aisc_link((dllpublic_ext*)parent, (dllheader_ext*)look);
475
476        aisc_main->touched = 1;
477    }
478    else {
479        result = "ZZZ";
480#if defined(DEBUG)
481        printf("ARB_name_server: Failed to find a unique short prefix for word '%s' (using '%s')\n", full, result);
482#endif
483    }
484
485    free(full3);
486    free(full2);
487    free(full1);
488
489    return ARB_strdup(result);
490}
491
492// --------------------------------------------------------------------------------
493
494static const char *default_full_name = "No name";
495
496class NameInformation : virtual Noncopyable {
497    const char *full_name;
498
499    char *parsed_name;
500    char *parsed_sym;
501    char *parsed_acc;
502    char *parsed_add_id;
503
504    char *first_name;
505    char *rest_of_name;
506
507    char *id;
508
509public:
510    NameInformation(const AN_local *locs);
511    ~NameInformation();
512
513    const char *get_id() const { return id; }
514    const char *get_full_name() const { return full_name; }
515    const char *get_first_name() const { return first_name; }
516    const char *get_rest_of_name() const { return rest_of_name; }
517
518    void add_short(const AN_local *locs, const char *shrt) const {
519        an_add_short(locs, id, parsed_name, parsed_sym, shrt, parsed_acc, parsed_add_id);
520    }
521};
522
523static bool contains_non_alphanumeric(const char *str) {
524    bool nonalnum = false;
525    for (char c = *str++; c; c = *str++) {
526        if (!isalnum(c)) {
527            nonalnum = true;
528            break;
529        }
530    }
531    return nonalnum;
532}
533
534static char *make_alnum(const char *str) {
535    // returns a heap-copy containing all alphanumeric characters of 'str'
536
537    char *newStr = ARB_alloc<char>(strlen(str)+1);
538    int   n      = 0;
539
540    for (int p = 0; str[p]; ++p) {
541        if (isalnum(str[p])) newStr[n++] = str[p];
542    }
543    newStr[n] = 0;
544    return newStr;
545}
546static char *make_alpha(const char *str) {
547    // returns a heap-copy containing all alpha characters of 'str'
548
549    char *newStr = ARB_alloc<char>(strlen(str)+1);
550    int   n      = 0;
551
552    for (int p = 0; str[p]; ++p) {
553        if (isalpha(str[p])) newStr[n++] = str[p];
554    }
555    newStr[n] = 0;
556    return newStr;
557}
558
559#if defined(DEBUG)
560#define assert_alphanumeric(s) na_assert(!contains_non_alphanumeric(s))
561#else
562#define assert_alphanumeric(s)
563#endif // DEBUG
564
565NameInformation::NameInformation(const AN_local *locs) {
566    full_name = locs->full_name;
567    if (!full_name || !full_name[0]) full_name = default_full_name;
568
569    parsed_name = GBS_string_eval(full_name,
570                                  "\t= :\"= :'= :" // replace TABs and quotes by space
571                                  "sp.=species:spec.=species:SP.=SPECIES:SPEC.=SPECIES:" // replace common abbreviations of 'species'
572                                  ".= :" // replace dots by spaces
573                                  "  = :"); // multiple spaces -> 1 space
574
575    {
576        int leading_spaces = strspn(parsed_name, " ");
577        int len            = strlen(parsed_name)-leading_spaces;
578        memmove(parsed_name, parsed_name+leading_spaces, len);
579
580        char *first_space = strchr(parsed_name, ' ');
581        if (first_space) {
582            char *second_space = strchr(first_space+1, ' ');
583            if (second_space) {
584                second_space[0] = 0; // skip all beyond 2nd word
585            }
586        }
587    }
588
589    an_autocaps(parsed_name);
590
591    parsed_sym = GBS_string_eval(full_name, "\t= :* * *sym*=S");
592    if (strlen(parsed_sym)>1) freedup(parsed_sym, "");
593
594    const char *add_id = locs->add_id[0] ? locs->add_id : aisc_main->add_field_default;
595
596    parsed_acc    = make_alnum(locs->acc);
597    parsed_add_id = make_alnum(add_id);
598    first_name    = GBS_string_eval(parsed_name, "* *=*1");
599    rest_of_name  = make_alnum(parsed_name+strlen(first_name));
600
601    freeset(first_name, make_alnum(first_name));
602
603    assert_alphanumeric(parsed_acc);
604    assert_alphanumeric(first_name);
605    assert_alphanumeric(rest_of_name);
606
607    UPPERCASE(rest_of_name[0]);
608
609    // build id
610
611    id = (strlen(parsed_acc)+strlen(parsed_add_id))
612        ? GBS_global_string_copy("*%s*%s", parsed_acc, parsed_add_id)
613        : GBS_global_string_copy("%s*%s*%s", first_name, rest_of_name, parsed_sym);
614}
615
616NameInformation::~NameInformation() {
617    free(id);
618
619    free(rest_of_name);
620    free(first_name);
621
622    free(parsed_add_id);
623    free(parsed_acc);
624    free(parsed_sym);
625    free(parsed_name);
626}
627
628// --------------------------------------------------------------------------------
629// AISC functions
630
631int del_short(const AN_local *locs) {
632    // forget about a short name
633    NameInformation  info(locs);
634    int              removed   = 0;
635    AN_shorts       *an_shorts = lookup_an_shorts(aisc_main, info.get_id());
636
637    if (an_shorts) {
638        an_remove_short(an_shorts);
639        removed = 1;
640    }
641
642    return removed;
643}
644
645static GB_HASH *nameModHash = 0; // key = default name; value = max. counter tested
646
647aisc_string get_short(const AN_local *locs) {
648    // get the short name from the previously set names
649    static char *shrt = 0;
650
651    freenull(shrt);
652
653    NameInformation  info(locs);
654    AN_shorts       *an_shorts = lookup_an_shorts(aisc_main, info.get_id());
655
656    if (an_shorts) {            // we already have a short name
657        bool recreate = false;
658
659        if (contains_non_alphanumeric(an_shorts->shrt)) {
660            recreate = true;
661        }
662        else if (strcmp(an_shorts->full_name, default_full_name) == 0 && // fullname in name server is default_full_name
663                 strcmp(info.get_full_name(), an_shorts->full_name) != 0) // and differs from current
664        {
665            recreate = true;
666        }
667        if (recreate) {
668            an_remove_short(an_shorts);
669            an_shorts = 0;
670        }
671        else {
672            shrt = ARB_strdup(an_shorts->shrt);
673        }
674    }
675    if (!shrt) { // now there is no short name (or an illegal one)
676        char *first_advice=0, *second_advice=0;
677
678        if (locs->advice[0] && contains_non_alphanumeric(locs->advice)) { // bad advice
679            locs->advice[0] = 0; // delete it
680        }
681
682        if (locs->advice[0]) {
683            char *advice = make_alpha(locs->advice);
684
685            first_advice = ARB_strdup(advice);
686            if (strlen(advice) > 3) {
687                second_advice = ARB_strdup(advice+3);
688                first_advice[3] = 0;
689            }
690        }
691
692        if (!first_advice) first_advice = ARB_strdup("ZZZ");
693        if (!second_advice) second_advice = ARB_strdup("ZZZZZ");
694
695        char *first_short;
696        int   first_len;
697        {
698            const char *first_name = info.get_first_name();
699            first_short      = first_name[0]
700                ? an_get_short(aisc_main->shorts1, &(aisc_main->pshorts1), first_name)
701                : ARB_strdup(first_advice);
702
703            na_assert(first_short);
704
705            if (first_short[0] == 0) { // empty?
706                freedup(first_short, "ZZZ");
707            }
708            first_len = strlen(first_short);
709        }
710
711        char *second_short = ARB_calloc<char>(10);
712        int   second_len;
713        {
714            const char *rest_of_name = info.get_rest_of_name();
715            int         restlen      = strlen(rest_of_name);
716
717            if (!restlen) {
718                rest_of_name = second_advice;
719                restlen      = strlen(rest_of_name);
720                if (!restlen) {
721                    rest_of_name = "ZZZZZ";
722                    restlen      = 5;
723                }
724            }
725
726            second_short[0] = 0;
727
728            if (restlen<5 && first_len>3) {
729                strcpy(second_short, first_short+3); // take additional characters from first_short
730                second_short[5-restlen] = 0; // but not too many
731            }
732
733            char *strend = strchr(second_short, 0);
734            strncpy(strend, rest_of_name, 8);
735            second_len   = strlen(second_short);
736        }
737
738        if (first_len>3) {
739            first_short[3] = 0;
740            first_len      = 3;
741        }
742
743        int both_len = first_len+second_len;
744        if (both_len<8) {
745            freeset(second_short, GBS_global_string_copy("%s00000000", second_short));
746            second_len += 8;
747            both_len   += 8;
748        }
749
750        if (both_len>8) {
751            second_len               = 8-first_len;
752            second_short[second_len] = 0;
753            both_len                 = 8;
754        }
755
756        char test_short[9];
757        sprintf(test_short, "%s%s", first_short, second_short);
758
759        na_assert(size_t(both_len) == strlen(test_short));
760        na_assert(second_len>=5 && second_len <= 8);
761
762        if (lookup_an_revers(aisc_main, test_short)) {
763            if (!nameModHash) nameModHash = GBS_create_hash(100, GB_IGNORE_CASE);
764
765            char *test_short_dup = ARB_strdup(test_short);
766
767            long count    = 2; // start numbering with 'SomName2' (not 'SomName1')
768            test_short[7] = 0; // store, max. 7 chars in nameModHash (at least one digit is used)
769            count         = std::max(count, GBS_read_hash(nameModHash, test_short));
770
771            const long NUMBERS = 100000;
772
773            int  printOffset = both_len;
774            bool foundUnused = false;
775
776            // first attempt to create alternate name using 1-5 digits at the end of the name
777            if (count<NUMBERS) {
778                int digLimit[6] = { 0, 9, 99, 999, 9999, 99999 };
779                for (int digits = 1; !foundUnused && digits <= 5; ++digits) {
780                    int maxOffset = 8-digits;
781                    int limit     = digLimit[digits];
782
783                    if (printOffset>maxOffset) printOffset = maxOffset;
784
785                    char *printAt = test_short+printOffset;
786                    if (digits>1) {
787                        printAt[0] = 0;
788                        count      = std::max(count, GBS_read_hash(nameModHash, test_short)); // check shorter prefix
789                    }
790
791                    for (; !foundUnused && count <= limit; ++count) {
792                        IF_ASSERTION_USED(int printed =) sprintf(printAt, "%li", count);
793                        na_assert((printed+printOffset) <= 8);
794                        if (!lookup_an_revers(aisc_main, test_short)) foundUnused = true; // name does not exist
795                    }
796                }
797            }
798            else {
799                test_short[3] = 0;
800                count         = std::max(count, GBS_read_hash(nameModHash, test_short)); // check prefix with len==3
801            }
802
803            const long base36_limit5 = 60466176;      //    60466176 = 36^5 (while using 3-letter-prefix)
804            const int64_t base36_limit7 = 78364164096LL;   // 78364164096 = 36^7 (while using 1-letter-prefix)
805
806            bool store_in_nameModHash = true;
807
808            // if no unused name found, create one with mixed alphanumeric-chars (last 5 characters of the name)
809            if (!foundUnused) {
810                strcpy(test_short, test_short_dup);
811
812                long        count2  = count-NUMBERS; // 100000 numbers were used above
813                char       *printAt = test_short+3;
814                const char *base36  = "0123456789abcdefghijklmnopqrstuvwxyz";
815
816                printAt[5] = 0;
817
818                for (; !foundUnused && count2<base36_limit5; ++count2) {
819                    // now print count2 converted to base 36
820
821                    long c = count2;
822                    for (int pos = 0; pos<5; ++pos) {
823                        long nextc = c/36;
824                        int  rest  = c-36*nextc;
825
826                        printAt[4-pos] = base36[rest];
827                        c              = nextc;
828
829                        na_assert(pos != 4 || c == 0);
830                    }
831
832                    if (!lookup_an_revers(aisc_main, test_short)) foundUnused = true; // name does not exist
833                }
834
835                if (!foundUnused) {
836                    // loop over ALL possible short-name (=1-letter-prefix + 7-letter-alnum-suffix)
837                    na_assert(count2>base36_limit5);
838                    store_in_nameModHash = false; // is directly stored for each 1-letter-prefix
839
840                    // @@@ try original starting character first?
841
842                    for (int pc = 'a'; pc<='z' && !foundUnused;  ++pc) {
843                        char key[2] = { char(pc), 0 };
844
845                        int64_t count3 = GBS_read_hash(nameModHash, key);
846
847                        test_short[0] = pc;
848                        printAt       = test_short+1;
849
850                        for (; !foundUnused && count3<base36_limit7; ++count3) {
851                            // now print count3 converted to base 36
852
853                            int64_t c = count3;
854                            for (int pos = 0; pos<7; ++pos) {
855                                int64_t nextc = c/36;
856                                int     rest  = c-36*nextc;
857
858                                printAt[6-pos] = base36[rest];
859                                c              = nextc;
860
861                                na_assert(pos != 6 || c == 0);
862                            }
863
864                            if (!lookup_an_revers(aisc_main, test_short)) foundUnused = true; // name does not exist
865                        }
866
867                        GBS_write_hash(nameModHash, key, count3);
868                    }
869
870                    if (!foundUnused) {
871                        const int64_t names_limit = 26*base36_limit7;
872                        GBK_terminatef("Fatal error: reached short-name-limit ("
873#if defined(ARB_64)
874                                       "%li"
875#else // !defined(ARB_64)
876                                       "%lli"
877#endif
878                                       ")", names_limit);
879                    }
880                }
881                count = count2+NUMBERS;
882            }
883
884            na_assert(foundUnused);
885
886            if (store_in_nameModHash) {
887                test_short_dup[7] = 0;
888                GBS_write_hash(nameModHash, test_short_dup, count);
889                if (count>9) {
890                    test_short_dup[6] = 0;
891                    GBS_write_hash(nameModHash, test_short_dup, count);
892                    if (count>99) {
893                        test_short_dup[5] = 0;
894                        GBS_write_hash(nameModHash, test_short_dup, count);
895                        if (count>999) {
896                            test_short_dup[4] = 0;
897                            GBS_write_hash(nameModHash, test_short_dup, count);
898                            if (count>9999) {
899                                test_short_dup[3] = 0;
900                                GBS_write_hash(nameModHash, test_short_dup, count);
901                            }
902                        }
903                    }
904                }
905            }
906
907            free(test_short_dup);
908            GBS_optimize_hash(nameModHash);
909        }
910
911        assert_alphanumeric(test_short);
912
913        shrt = ARB_strdup(test_short);
914        info.add_short(locs, shrt);
915
916        free(first_short);
917        free(second_short);
918        free(first_advice);
919        free(second_advice);
920    }
921
922    assert_alphanumeric(shrt);
923    return shrt;
924}
925
926int server_save(AN_main *main, int) {
927    if (main->touched) {
928        int server_date = GB_time_of_file(main->server_file);
929        if (server_date>main->server_filedate) {
930            printf("Another nameserver changed '%s' - your changes are lost.\n", main->server_file);
931        }
932        else {
933            char *sec_name = ARB_calloc<char>(strlen(main->server_file)+2);
934            sprintf(sec_name, "%s%%", main->server_file);
935            printf("Saving '%s'..\n", main->server_file);
936
937            FILE *file = fopen(sec_name, "w");
938            if (!file) {
939                fprintf(stderr, "ERROR cannot save file '%s'\n", sec_name);
940            }
941            else {
942                save_AN_main(main, file);
943                if (fclose(file) == 0) {
944                    GB_ERROR mv_error = GB_move_file(sec_name, main->server_file);
945                    if (mv_error) GB_warning(mv_error);
946                    else main->touched = 0;
947                }
948                else {
949                    GB_ERROR save_error = GB_IO_error("saving", sec_name);
950                    fprintf(stderr, "Error: %s\n", save_error);
951                    unlink(sec_name);
952                }
953            }
954            free(sec_name);
955            main->server_filedate = GB_time_of_file(main->server_file);
956        }
957    }
958    else {
959        printf("No changes to ARB_name_server data.\n");
960    }
961
962    return 0;
963}
964
965#if defined(DEBUG) && 0
966static void check_list(AN_shorts *start) {
967    int count = 0;
968    while (++count) {
969        start = start->next;
970        if (!start) {
971            fprintf(stderr, "<list has %i elements>\n", count);
972            return;
973        }
974    }
975
976    fprintf(stderr, "<ERROR - list is looped>\n");
977    na_assert(0);
978}
979#endif // DEBUG
980
981static void check_for_case_error(AN_main *main) {
982    // test for duplicated names or name parts (only differing in case)
983    // such names were created by old name server versions
984
985    bool case_error_occurred = false;
986    int  idents_changed      = 0;
987    // first check name parts
988    for (AN_shorts *shrt = main->shorts1; shrt;) {
989        AN_shorts *next  = shrt->next;
990        AN_shorts *found = an_find_shrt_prefix(shrt->shrt);
991        if (found != shrt) {
992            fprintf(stderr, "- Correcting error in name-database: '%s' equals '%s'\n",
993                    found->shrt, shrt->shrt);
994            an_remove_short(shrt);
995            case_error_occurred = true;
996        }
997        shrt = next;
998    }
999
1000    // then check full short-names
1001    for (AN_shorts *shrt = main->names; shrt;) {
1002        AN_shorts *next  = shrt->next;
1003        AN_revers *found = lookup_an_revers(main, shrt->shrt);
1004
1005        if (found && (shrt->acc && found->acc && an_stricmp(shrt->acc, found->acc) != 0)) {
1006            fprintf(stderr, "- Correcting error in name-database: '%s' equals '%s' (but acc differs)\n",
1007                    found->mh.ident, shrt->shrt);
1008
1009            an_remove_short(shrt);
1010            case_error_occurred = true;
1011        }
1012        else if (found && (shrt->add_id && found->add_id && an_stricmp(shrt->add_id, found->add_id) != 0)) {
1013            fprintf(stderr, "- Correcting error in name-database: '%s' equals '%s' (but add_id differs)\n",
1014                    found->mh.ident, shrt->shrt);
1015
1016            an_remove_short(shrt);
1017            case_error_occurred = true;
1018        }
1019        else {
1020            AN_shorts *self_find = lookup_an_shorts(main, shrt->mh.ident);
1021            if (!self_find) { // stored with wrong key (not lowercase)
1022                aisc_unlink((dllheader_ext*)shrt);
1023                an_strlwr(shrt->mh.ident);
1024                aisc_link(&main->pnames, shrt);
1025                main->touched = 1;
1026
1027                case_error_occurred = true;
1028                idents_changed++;
1029            }
1030            else if (self_find != shrt) {
1031                fprintf(stderr, "- Correcting error in name-database: '%s' equals '%s' (case-difference in full_name or acc)\n",
1032                        shrt->mh.ident, self_find->mh.ident);
1033                an_remove_short(shrt);
1034                case_error_occurred = true;
1035            }
1036        }
1037
1038        shrt = next;
1039    }
1040
1041    if (case_error_occurred) {
1042        int regen_name_parts = 0;
1043        int deleted_names    = 0;
1044
1045        // now capitalize all name parts
1046        {
1047            list<string> idents_to_recreate;
1048
1049            for (AN_shorts *shrt =  main->shorts1; shrt;) {
1050                char      *cap_name = ARB_strdup(shrt->shrt);
1051                an_autocaps(cap_name);
1052
1053                if (strcmp(cap_name, shrt->shrt) != 0) {
1054                    idents_to_recreate.push_back(shrt->mh.ident);
1055                }
1056                free(cap_name);
1057
1058                AN_shorts *next = shrt->next;
1059                an_remove_short(shrt);
1060                shrt = next;
1061            }
1062
1063            list<string>::const_iterator end = idents_to_recreate.end();
1064            for (list<string>::const_iterator i = idents_to_recreate.begin(); i != end; ++i) {
1065                const char *ident = i->c_str();
1066                free(an_get_short(main->shorts1, &(main->pshorts1), ident));
1067                regen_name_parts++;
1068            }
1069        }
1070
1071        // now capitalize all short names
1072        for (AN_shorts *shrt =  main->names; shrt;) {
1073            AN_shorts *next     = shrt->next;
1074            char      *cap_name = ARB_strdup(shrt->shrt);
1075            an_autocaps(cap_name);
1076
1077            if (strcmp(cap_name, shrt->shrt) != 0) {
1078                an_remove_short(shrt);
1079                deleted_names++;
1080            }
1081            shrt = next;
1082            free(cap_name);
1083        }
1084
1085        if (idents_changed)   fprintf(stderr, "* Changed case of %i identifiers.\n", idents_changed);
1086        if (regen_name_parts) fprintf(stderr, "* Regenerated %i prefix names.\n", regen_name_parts);
1087        if (deleted_names)    fprintf(stderr, "* Removed %i names with wrong case.\n"
1088                                      "=> This leads to name changes when generating new names (which is recommended now).\n", deleted_names);
1089    }
1090}
1091
1092static void check_for_illegal_chars(AN_main *main) {
1093    // test for names containing illegal characters
1094    int illegal_names = 0;
1095
1096    // first check name parts
1097    for (AN_shorts *shrt = main->shorts1; shrt;) {
1098        AN_shorts *next = shrt->next;
1099        if (contains_non_alphanumeric(shrt->shrt)) {
1100            fprintf(stderr, "- Fixing illegal chars in '%s'\n", shrt->shrt);
1101            an_remove_short(shrt);
1102            illegal_names++;
1103        }
1104        shrt = next;
1105    }
1106    // then check full short-names
1107    for (AN_shorts *shrt = main->names; shrt;) {
1108        AN_shorts *next  = shrt->next;
1109        if (contains_non_alphanumeric(shrt->shrt)) {
1110            fprintf(stderr, "- Fixing illegal chars in '%s'\n", shrt->shrt);
1111            an_remove_short(shrt);
1112            illegal_names++;
1113        }
1114        shrt = next;
1115    }
1116
1117    if (illegal_names>0) {
1118        fprintf(stderr, "* Removed %i names containing illegal characters.\n"
1119                "=> This leads to name changes when generating new names (which is recommended now).\n", illegal_names);
1120    }
1121}
1122
1123static void set_empty_addids(AN_main *main) {
1124    // fill all empty add.ids with default value
1125
1126    if (main->add_field_default[0]) {
1127        // if we use a non-empty default, old entries need to be changed
1128        // (empty default was old behavior)
1129
1130        long count = 0;
1131        for (AN_shorts *shrt = main->names; shrt;) {
1132            AN_shorts *next  = shrt->next;
1133            if (!shrt->add_id[0]) {
1134                aisc_unlink((dllheader_ext*)shrt);
1135
1136                freedup(shrt->add_id, main->add_field_default);
1137                na_assert(strchr(shrt->mh.ident, 0)[-1] == '*');
1138                freeset(shrt->mh.ident, GBS_global_string_copy("%s%s", shrt->mh.ident, main->add_field_default));
1139
1140                aisc_link(&main->pnames, shrt);
1141
1142                count++;
1143            }
1144            shrt = next;
1145        }
1146        if (count>0) {
1147            printf("  Filled in default value '%s' for %li names\n", main->add_field_default, count);
1148            main->touched = 1;
1149        }
1150    }
1151}
1152
1153static GB_ERROR server_load(AN_main *main)
1154{
1155    FILE      *file;
1156    GB_ERROR   error = 0;
1157    AN_shorts *shrt;
1158    AN_revers *revers;
1159
1160    fprintf(stderr, "Starting ARB_name_server..\n");
1161
1162    file = fopen(main->server_file, "r");
1163    if (!file) {
1164        error = GBS_global_string("No such file '%s'", main->server_file);
1165    }
1166    else {
1167        fprintf(stderr, "* Loading %s\n", main->server_file);
1168        int err_code = load_AN_main(main, file);
1169        if (err_code) {
1170            error = GBS_global_string("Error #%i while loading '%s'", err_code, main->server_file);
1171        }
1172    }
1173
1174    if (!error) {
1175        fprintf(stderr, "* Parsing data\n");
1176        long nameCount =  0;
1177        for (shrt = main->names; shrt; shrt = shrt->next) {
1178            revers            = create_AN_revers();
1179            revers->mh.ident  = an_strlwr(ARB_strdup(shrt->shrt));
1180            revers->full_name = ARB_strdup(shrt->full_name);
1181            revers->acc       = ARB_strdup(shrt->acc);
1182            revers->add_id    = shrt->add_id ? ARB_strdup(shrt->add_id) : 0;
1183            aisc_link(&main->prevers, revers);
1184            nameCount++;
1185        }
1186
1187        int namesDBversion = main->dbversion; // version used to save names.dat
1188        fprintf(stderr, "* Loaded NAMEDB v%i (contains %li names)\n", namesDBversion, nameCount);
1189
1190        if (namesDBversion < SERVER_VERSION) {
1191            if (namesDBversion<4) {
1192                fprintf(stderr, "* Checking for case-errors\n");
1193                check_for_case_error(main);
1194
1195                fprintf(stderr, "* Checking for illegal characters\n");
1196                check_for_illegal_chars(main);
1197            }
1198
1199            fprintf(stderr, "* NAMEDB version upgrade %i -> %i\n", namesDBversion, SERVER_VERSION);
1200            main->dbversion = SERVER_VERSION;
1201            main->touched   = 1; // force save
1202        }
1203        if (namesDBversion > SERVER_VERSION) {
1204            error = GBS_global_string("NAMEDB is from version %i, but your nameserver can only handle version %i",
1205                                      namesDBversion, SERVER_VERSION);
1206        }
1207        else {
1208            fprintf(stderr, "ARB_name_server is up.\n");
1209            main->server_filedate = GB_time_of_file(main->server_file);
1210        }
1211    }
1212    else {
1213        main->server_filedate = -1;
1214    }
1215    return error;
1216}
1217
1218int names_server_save() {
1219    server_save(aisc_main, 0);
1220    return 0;
1221}
1222
1223int server_shutdown(AN_main */*pm*/, aisc_string passwd) {
1224    // password check
1225    bool authorized = strcmp(passwd, "ldfiojkherjkh") == 0;
1226    free(passwd);
1227    if (!authorized) return 1;
1228
1229    fflush(stderr); fflush(stdout);
1230    printf("\narb_name_server: I got the shutdown message.\n");
1231
1232    // shutdown clients
1233    aisc_broadcast(AN_global.server_communication, 0, "Used nameserver has been shut down");
1234
1235    // shutdown server
1236    printf("ARB_name_server: server shutdown by administrator\n");
1237    aisc_server_shutdown(AN_global.server_communication);
1238    exit(EXIT_SUCCESS);
1239
1240    return 0;
1241}
1242
1243static int usage(const char *exeName, const char *err) {
1244    printf("ARB nameserver v%i\n"
1245           "Usage: %s command server-parameters\n"
1246           "command = -boot\n"
1247           "          -kill\n"
1248           "          -look\n"
1249           , SERVER_VERSION, exeName);
1250    arb_print_server_params();
1251    if (err) printf("Error: %s\n", err);
1252    return EXIT_FAILURE;
1253}
1254
1255int ARB_main(int argc, char *argv[]) {
1256    char       *name;
1257    int         i;
1258    Hs_struct  *so;
1259    arb_params *params;
1260
1261    params                 = arb_trace_argv(&argc, (const char **)argv);
1262    const char *executable = argv[0];
1263
1264    if (!params->default_file) {
1265        return usage(executable, "Missing default file");
1266    }
1267
1268    if (argc==1) { // default command is '-look'
1269        char flag[]="-look";
1270        argv[1] = flag;
1271        argc = 2;
1272    }
1273
1274    if (argc!=2) {
1275        return usage(executable, "Too many parameters");
1276    }
1277
1278    aisc_main = create_AN_main();
1279
1280    // try to open com with running name server
1281    if (params->tcp) {
1282        name = params->tcp;
1283    }
1284    else {
1285        const char *cname = GBS_read_arb_tcp(GBS_nameserver_tag(NULL));
1286
1287        if (!cname) {
1288            GB_print_error();
1289            return EXIT_FAILURE;
1290        }
1291        name = ARB_strdup(cname);
1292    }
1293
1294    GB_ERROR error    = NULL;
1295    AN_global.cl_link = aisc_open(name, AN_global.cl_main, AISC_MAGIC_NUMBER, &error);
1296
1297    if (AN_global.cl_link) {
1298        if (!strcmp(argv[1], "-look")) {
1299            printf("ARB_name_server: No client - terminating.\n");
1300            aisc_close(AN_global.cl_link, AN_global.cl_main); AN_global.cl_link = 0;
1301            return EXIT_SUCCESS;
1302        }
1303
1304        printf("There is another active nameserver. I try to kill it..\n");
1305        aisc_nput(AN_global.cl_link, AN_MAIN, AN_global.cl_main,
1306                  MAIN_SHUTDOWN, "ldfiojkherjkh",
1307                  NULL);
1308        aisc_close(AN_global.cl_link, AN_global.cl_main); AN_global.cl_link=0;
1309        GB_sleep(1, SEC);
1310    }
1311
1312    if (error) {
1313        printf("ARB_name_server: %s\n", error);
1314        return EXIT_FAILURE;
1315    }
1316
1317    if (((strcmp(argv[1], "-kill") == 0)) ||
1318        ((argc==3) && (strcmp(argv[2], "-kill")==0))) {
1319        printf("ARB_name_server: Now I kill myself!\n");
1320        return EXIT_SUCCESS;
1321    }
1322    for (i=0, so=0; (i<MAX_TRY) && (!so); i++) {
1323        so = open_aisc_server(name, NAME_SERVER_SLEEP*1000L, 0);
1324        if (!so) GB_sleep(RETRY_SLEEP, SEC);
1325    }
1326    if (!so) {
1327        printf("AN_SERVER: Gave up on opening the communication socket!\n");
1328        return EXIT_FAILURE;
1329    }
1330    AN_global.server_communication = so;
1331
1332    aisc_main->server_file     = ARB_strdup(params->default_file);
1333    aisc_main->server_filedate = GB_time_of_file(aisc_main->server_file);
1334
1335    error = server_load(aisc_main);
1336
1337    if (!error) {
1338        const char *field         = params->field;
1339        const char *field_default = params->field_default;
1340
1341        if (field == 0) {
1342            field         = "";                     // default to no field
1343            field_default = "";
1344        }
1345        else {
1346            if (!params->field_default) {
1347                error = GBS_global_string("Missing default value for add.field (option has to be -f%s=defaultValue)", field);
1348            }
1349        }
1350
1351        if (!error) {
1352            if (aisc_main->add_field == 0) {        // add. field was not defined yet
1353                if (field[0]) {
1354                    printf("* add. field not defined yet -> using '%s'\n", field);
1355                }
1356                else {
1357                    fputs("* using no add. field\n", stdout);
1358                }
1359                aisc_main->add_field = ARB_strdup(field);
1360                aisc_main->touched   = 1;
1361            }
1362            else {
1363                if (strcmp(aisc_main->add_field, field) != 0) { // add. field changed
1364                    if (aisc_main->add_field[0]) {
1365                        error = GBS_global_string("This names-DB has to be started with -f%s", aisc_main->add_field);
1366                    }
1367                    else {
1368                        error = "This names-DB has to be started w/o -f option";
1369                    }
1370                }
1371            }
1372        }
1373
1374        if (!error) {
1375            char *field_default_alnum = make_alnum(field_default);
1376
1377            if (aisc_main->add_field_default == 0) { // previously no default was defined for add.field
1378                reassign(aisc_main->add_field_default, field_default_alnum);
1379                set_empty_addids(aisc_main);
1380                aisc_main->touched           = 1;
1381            }
1382            else {
1383                if (strcmp(aisc_main->add_field_default, field_default_alnum) != 0) {
1384                    error = GBS_global_string("Default for add.field previously was '%s' (called with '%s')\n"
1385                                              "If you really need to change this - delete the names DB",
1386                                              aisc_main->add_field_default, field_default_alnum);
1387                }
1388            }
1389            free(field_default_alnum);
1390        }
1391    }
1392
1393    long accept_calls_init = NAME_SERVER_TIMEOUT/long(NAME_SERVER_SLEEP);
1394    long accept_calls      = accept_calls_init;
1395    bool isTimeout         = true;
1396
1397    if (!error && aisc_main->touched) server_save(aisc_main, 0);
1398
1399
1400    while (!error && accept_calls>0) {
1401        aisc_accept_calls(so);
1402
1403        if (aisc_main->ploc_st.cnt <= 0) { // timeout passed and no clients
1404            accept_calls--;
1405
1406            long server_date = GB_time_of_file(aisc_main->server_file);
1407            if (server_date == 0 && aisc_main->server_filedate != 0) {
1408                fprintf(stderr, "ARB_name_server data has been deleted.\n");
1409                accept_calls = 0;
1410                isTimeout    = false;
1411            }
1412            if (server_date>aisc_main->server_filedate) {
1413                fprintf(stderr, "ARB_name_server data changed on disc.\n");
1414                accept_calls = 0;
1415                isTimeout    = false;
1416            }
1417            else if (aisc_main->touched) {
1418                server_save(aisc_main, 0);
1419                accept_calls = accept_calls_init;
1420            }
1421        }
1422        else { // got a client
1423            accept_calls = accept_calls_init;
1424        }
1425    }
1426
1427    if (error) {
1428        char *fullErrorMsg   = GBS_global_string_copy("Error in ARB_name_server: %s", error);
1429        char *quotedErrorMsg = GBK_singlequote(fullErrorMsg);
1430
1431        fprintf(stderr, "%s\n", fullErrorMsg);                                     // log error to console
1432        error = GBK_system(GBS_global_string("arb_message %s &", quotedErrorMsg)); // send async to avoid deadlock
1433        if (error) fprintf(stderr, "Error: %s\n", error);
1434        free(quotedErrorMsg);
1435        free(fullErrorMsg);
1436    }
1437    else if (accept_calls == 0) {
1438        if (isTimeout) {
1439            fprintf(stderr, "Been idle for %i minutes.\n", int(NAME_SERVER_TIMEOUT/60));
1440        }
1441    }
1442
1443    printf("ARB_name_server terminating...\n");
1444    if (nameModHash) GBS_free_hash(nameModHash);
1445    aisc_server_shutdown(AN_global.server_communication);
1446    int exitcode = error ? EXIT_FAILURE : EXIT_SUCCESS;
1447    printf("Server terminates with code %i.\n", exitcode);
1448    return exitcode;
1449}
Note: See TracBrowser for help on using the repository browser.