source: tags/svn.1.5.4/NAMES/names.cxx

Last change on this file was 8309, checked in by westram, 14 years ago
  • moved much code into static scope

(partly reverted by [8310])

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