source: branches/nameserver/NAMES/names.cxx

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