source: branches/nameserver/SL/AW_NAME/AW_rename.cxx

Last change on this file was 17595, checked in by westram, 6 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.6 KB
Line 
1// =============================================================== //
2//                                                                 //
3//   File      : AW_rename.cxx                                     //
4//   Purpose   :                                                   //
5//                                                                 //
6//   Institute of Microbiology (Technical University Munich)       //
7//   http://www.arb-home.de/                                       //
8//                                                                 //
9// =============================================================== //
10
11#include "AW_rename.hxx"
12
13#include <aw_awars.hxx>
14#include <aw_window.hxx>
15#include <aw_root.hxx>
16#include <aw_question.hxx>
17#include <aw_msg.hxx>
18#include <arb_progress.h>
19
20#include <names_client.h>
21#include <servercntrl.h>
22#include <client.h>
23
24#include <cctype>
25#include <ctime>
26
27static const char *get_addid(GBDATA *gb_main) {
28    GB_transaction ta(gb_main);
29    GBDATA *gb_addid = GB_search(gb_main, AWAR_NAMESERVER_ADDID, GB_FIND);
30    return gb_addid ? GB_read_char_pntr(gb_addid) : NULp;
31}
32
33static GB_ERROR set_addid(GBDATA *gb_main, const char *addid) {
34    GB_ERROR error    = GB_push_transaction(gb_main);
35    if (!error) error = GBT_write_string(gb_main, AWAR_NAMESERVER_ADDID, addid ? addid : "");
36    return GB_end_transaction(gb_main, error);
37}
38
39const char *AW_get_nameserver_addid(GBDATA *gb_main) {
40    // return the additional field used for nameserver connection
41    const char *addid = get_addid(gb_main);
42
43    aw_assert(addid); // the current DB has no entry AWAR_NAMESERVER_ADDID! (programmers error)
44    if (!addid) addid = ""; // NDEBUG fallback
45
46    return addid;
47}
48
49GB_ERROR AW_select_nameserver(GBDATA *gb_main, GBDATA *gb_other_main) {
50    // if entry AWAR_NAMESERVER_ADDID isn't defined yet, try to detect a reasonable value
51    // from arb_tcp.dat. Ask user if multiple servers are defined.
52    //
53    // if gb_other_main is defined try to use value from there.
54
55    const char *addid   = get_addid(gb_main);
56    GB_ERROR    error   = NULp;
57
58    if (!addid && gb_other_main && gb_other_main != gb_main) {
59        // look whether main DB has a defined addid
60        addid = get_addid(gb_other_main);
61        set_addid(gb_main, addid);
62    }
63
64    if (!addid) {
65        const char * const *nameservers = GBS_get_arb_tcp_entries("ARB_NAME_SERVER*");
66
67        if (!nameservers) error = GB_await_error();
68        else {
69            int serverCount = 0;
70
71            for (int c = 0; nameservers[c]; c++) serverCount++;
72
73            if (serverCount == 0) {
74                error = GBS_global_string("No nameserver defined.");
75            }
76            else {
77                char **fieldNames = ARB_alloc<char*>(serverCount);
78                for (int c = 0; c<serverCount; c++) {
79                    const char *ipport = GBS_read_arb_tcp(nameservers[c]);
80                    if (!ipport) {
81                        error = GB_await_error();
82                        fieldNames[c] = NULp;
83                    }
84                    else {
85                        fieldNames[c] = nulldup(GBS_scan_arb_tcp_param(ipport, "-f"));      // may return 0
86
87                        // parameter -f contains default value (e.g. '-fstart=1')
88                        if (fieldNames[c]) {
89                            char *equal = strchr(fieldNames[c], '=');
90                            if (equal) equal[0] = 0;
91                        }
92                    }
93                }
94
95                if (!error) {
96                    if (serverCount == 1) { // exactly 1 server defined -> don't ask
97                        error = set_addid(gb_main, fieldNames[0]);
98                    }
99                    else { // let the user select which nameserver to use
100                        aw_assert(serverCount>1);
101
102                        int         len     = serverCount; // commas+0term
103                        const char *nofield = "None (only 'acc')";
104
105                        for (int c = 0; c<serverCount; c++) {
106                            if (fieldNames[c]) len += strlen(fieldNames[c]);
107                            else len += strlen(nofield);
108                        }
109
110                        char *buttons = ARB_alloc<char>(len);
111                        buttons[0]    = 0;
112                        for (int c = 0; c<serverCount; c++) {
113                            if (c) strcat(buttons, ",");
114                            strcat(buttons, fieldNames[c] ? fieldNames[c] : nofield);
115                        }
116
117                        int answer = aw_question("nameserv_select",
118                                                 "Select if and which additional DB field you want to use",
119                                                 buttons, false, "namesadmin.hlp");
120
121                        error = set_addid(gb_main, fieldNames[answer]);
122
123                        free(buttons);
124                    }
125                }
126
127                for (int c = 0; c<serverCount; c++) free(fieldNames[c]);
128                free(fieldNames);
129            }
130        }
131    }
132
133    return error;
134}
135
136
137// ------------------------------------
138//      class NameServerConnection
139
140class NameServerConnection : virtual Noncopyable {
141    aisc_com   *link;
142    T_AN_LOCAL  locs;
143    T_AN_NAMED  com;
144    int         persistent;     // if true -> connection will not be closed
145    time_t      linktime;       // time, when link has been established
146
147    int init_local_com_names()
148    {
149        if (!link) return 1;    //!* create and init local com structure **
150        if (aisc_create(link, AN_NAMED, com,
151                        NAMED_LOCAL, AN_LOCAL, locs,
152                        LOCAL_WHOAMI, "i bin der arb_tree",
153                        NULp)) {
154            return 1;
155        }
156        return 0;
157    }
158
159    GB_ERROR reconnect(GBDATA *gb_main) { // reconnect ignoring consistency
160        int old_persistent = persistent;
161
162        printf("Reconnecting name server\n");
163
164        persistent = 0; // otherwise disconnect() won't disconnect
165        disconnect();
166        persistent = old_persistent; // restore previous persistence
167
168        return connect(gb_main);
169    }
170
171    char *fieldUsedByServer(GB_ERROR& err) {
172        char *field = NULp;
173        if (aisc_get(link, AN_NAMED, com,
174                     NAMED_ADD_FIELD, &field,
175                     NULp)) {
176            err = "Connection Problems with the NAME_SERVER";
177            aw_assert(!field);
178        }
179        return field;
180    }
181
182    GB_ERROR expectServerUsesField(const char *expected_field) {
183        GB_ERROR  err          = NULp;
184        char     *server_field = fieldUsedByServer(err);
185
186        if (!err && strcmp(expected_field, server_field) != 0) {
187            err = GBS_global_string("Additional field doesn't match (expected='%s', server uses='%s')", expected_field, server_field);
188        }
189        free(server_field);
190        return err;
191    }
192
193public:
194
195    NameServerConnection() {
196        link       = NULp;
197        locs.clear();
198        com.clear();
199        persistent = 0;
200    }
201    virtual ~NameServerConnection() {
202        aw_assert(persistent == 0); // forgot to remove persistence ?
203        disconnect();
204    }
205
206    GB_ERROR connect(GBDATA *gb_main) {
207        aw_assert(!GB_have_error());
208
209        arb_progress::show_comment("Connecting to name server");
210
211        GB_ERROR err = NULp;
212        if (!link) {
213            const char *add_field = AW_get_nameserver_addid(gb_main);
214            const char *server_id = GBS_nameserver_tag(add_field);
215
216            err = arb_look_and_start_server(AN::AISC_MAGIC_NUMBER, server_id);
217
218            if (!err) {
219                const char *ipport = GBS_read_arb_tcp(server_id);
220                if (!ipport) err = GB_await_error();
221                else {
222                    link     = aisc_open(ipport, com, AN::AISC_MAGIC_NUMBER, &err);
223                    linktime = time(NULp);
224
225                    if (!err) {
226                        if (init_local_com_names()) {
227                            err = GBS_global_string("Can't connect %s %s", server_id, ipport);
228                        }
229                        else {
230                            err = expectServerUsesField(add_field);
231                        }
232                    }
233                }
234            }
235        }
236        else {
237            long linkAge     = int(time(NULp)-linktime);
238            bool doReconnect = false;
239
240#if defined(DEBUG) && 0
241            // print information about name-server link age
242            static long lastage = -1;
243            if (linkAge != lastage) {
244                printf("Age of NameServerConnection: %li\n", linkAge);
245                lastage = linkAge;
246            }
247#endif // DEBUG
248
249            if (linkAge > (5*60)) { // perform a reconnect after 5 minutes
250                // Reason : The pipe to the name server breaks after some time
251                doReconnect = true;
252            }
253            else {
254                const char *add_field = AW_get_nameserver_addid(gb_main);
255                GB_ERROR    error     = expectServerUsesField(add_field);
256
257                if (error) {
258                    printf("Error: %s\n", error);
259                    doReconnect = true;
260                }
261            }
262
263            if (doReconnect) {
264                err = reconnect(gb_main);
265            }
266        }
267        aw_assert(!GB_have_error());
268        return err;
269    }
270
271    void disconnect() {
272        if (persistent == 0) {
273            if (link) {
274                aisc_close(link, com);
275                locs.clear();
276                com.clear();
277            }
278            link = NULp;
279        }
280    }
281
282    void persistence(bool persist) {
283        if (persist) {
284            ++persistent;
285        }
286        else {
287            --persistent;
288            if (persistent <= 0) {
289                persistent = 0;
290                disconnect();
291            }
292        }
293    }
294
295
296    aisc_com *getLink() { return link; }
297    const T_AN_LOCAL& getLocs() const { return locs; } 
298};
299
300static NameServerConnection name_server;
301
302PersistentNameServerConnection::PersistentNameServerConnection() {
303    name_server.persistence(true);
304}
305PersistentNameServerConnection::~PersistentNameServerConnection() {
306    name_server.persistence(false);
307}
308
309// --------------------------------------------------------------------------------
310
311GB_ERROR AW_test_nameserver(GBDATA *gb_main) {
312    return name_server.connect(gb_main);
313}
314
315// --------------------------------------------------------------------------------
316
317GB_ERROR AWTC_generate_one_name(GBDATA *gb_main, const char *full_name, const char *acc, const char *addid, char*& new_name) {
318    // create a unique short name for 'full_name'
319    // the result is written into 'new_name' (as malloc-copy)
320    // if fails: GB_ERROR!=0 && new_name==0
321    // acc and addid may be 0
322
323    new_name = NULp;
324    if (!acc) acc = "";
325
326    arb_progress progress("Generating species ID");
327
328    GB_ERROR err = name_server.connect(gb_main);
329    if (err) return err;
330
331    static char *shrt = NULp;
332    if (strlen(full_name)) {
333        UNCOVERED();
334
335        // @@@ DRY aisc_nput-calls into function -> then avoid LOCAL_FULL_NAME+LOCAL_ACCESSION longer than 1024 chars
336        if (aisc_nput(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
337                      LOCAL_FULL_NAME,  full_name,
338                      LOCAL_ACCESSION,  acc,
339                      LOCAL_ADDID,      addid ? addid : "",
340                      LOCAL_ADVICE,     "",
341                      NULp)) {
342            err = "Connection Problems with the NAME_SERVER";
343        }
344        if (aisc_get(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
345                     LOCAL_GET_SHORT,   &shrt,
346                     NULp)) {
347            err = "Connection Problems with the NAME_SERVER";
348        }
349    }
350
351    if (err) {
352        free(shrt);
353    }
354    else {
355        if (shrt) {
356            new_name = shrt;
357            shrt = NULp;
358        }
359        else {
360            err = GB_export_errorf("Failed to generate species ID (shortname) for '%s'", full_name);
361        }
362    }
363
364    name_server.disconnect();
365
366    return err;
367}
368
369GB_ERROR AWTC_recreate_name(GBDATA *gb_species) {
370    GBDATA       *gb_main = GB_get_root(gb_species);
371    arb_progress  progress("Recreating species ID");
372    GB_ERROR      error   = name_server.connect(gb_main);
373
374    if (!error) {
375        const char *add_field = AW_get_nameserver_addid(gb_main);
376        char       *ali_name  = GBT_get_default_alignment(gb_main);
377
378        // @@@ DRY section here versus .@OTHERSECTION
379        UNCOVERED();
380
381        GBDATA *gb_name      = GB_entry(gb_species, "name");
382        GBDATA *gb_full_name = GB_entry(gb_species, "full_name");
383        GBDATA *gb_acc       = GBT_gen_accession_number(gb_species, ali_name);
384        GBDATA *gb_addfield  = add_field[0] ? GB_entry(gb_species, add_field) : NULp;
385
386        char *name      = gb_name ?     GB_read_string   (gb_name)     : strdup("");
387        char *full_name = gb_full_name ? GB_read_string  (gb_full_name) : strdup("");
388        char *acc       = gb_acc ?      GB_read_string   (gb_acc)      : strdup("");
389        char *addid     = gb_addfield ? GB_read_as_string(gb_addfield) : strdup("");
390
391        long  deleted = 0;
392        char *shrt    = NULp;
393
394        if (aisc_nput(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
395                      LOCAL_FULL_NAME,  full_name,
396                      LOCAL_ACCESSION,  acc,
397                      LOCAL_ADDID,      addid,
398                      LOCAL_ADVICE,     "",
399                      NULp) != 0 ||
400            aisc_get(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
401                     LOCAL_DEL_SHORT,   &deleted,
402                     NULp)  != 0 ||
403            aisc_get(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
404                     LOCAL_GET_SHORT,   &shrt,
405                     NULp)  != 0)
406        {
407            error = "Connection Problems with the NAME_SERVER";
408        }
409        name_server.disconnect();
410
411        if (!error) {
412            GBT_begin_rename_session(gb_main, 0);
413            error = GBT_rename_species(name, shrt, true);
414            if (error) {
415                if (GBT_find_species(gb_main, shrt)) { // it was a rename error
416                    int done = 0;
417                    error    = NULp;
418                    for (int count = 2; !done && !error && count<10; count++) {
419                        const char *other_short = GBS_global_string("%s.%i", shrt, count);
420                        if (!GBT_find_species(gb_main, other_short)) {
421                            error            = GBT_rename_species(name, other_short, true);
422                            if (!error) done = 1;
423                        }
424                    }
425
426                    if (!done && !error) {
427                        error = "Failed to regenerate species ID. Please use 'Species/Synchronize IDs'";
428                    }
429                }
430            }
431
432            if (error) GBT_abort_rename_session();
433            else error = GBT_commit_rename_session();
434        }
435
436        free(shrt);
437        free(addid);
438        free(acc);
439        free(full_name);
440        free(name);
441    }
442
443    return error;
444}
445
446char *AWTC_create_numbered_suffix(GB_HASH *species_name_hash, const char *shortname, GB_ERROR& warning) {
447    char *newshort = NULp;
448    if (GBS_read_hash(species_name_hash, shortname)) {
449        int i;
450        ARB_alloc(newshort, strlen(shortname)+20);
451        for (i = 1; ; i++) {
452            sprintf(newshort, "%s.%i", shortname, i);
453            if (!GBS_read_hash(species_name_hash, newshort))break;
454        }
455
456        warning =
457            "There are duplicated species!\n"
458            "The IDs of these species ('name') contain a '.' character followed by a number.\n"
459            "We strongly recommend you try understand and solve this problem\n"
460            "(see HELP in 'Species/Synchronize IDs' window)";
461    }
462    return newshort;
463}
464
465GB_ERROR AWTC_pars_names(GBDATA *gb_main, bool *isWarningPtr) {
466    // rename species according to name_server
467    // 'isWarning' is set to true, in case of duplicates-warning
468
469    arb_progress gen_progress("Generating new shortnames (IDs)");
470    GB_ERROR     err       = name_server.connect(gb_main);
471    bool         isWarning = false;
472
473    if (!err) {
474        err = GBT_begin_rename_session(gb_main, 1);
475        if (!err) {
476            char     *ali_name = GBT_get_default_alignment(gb_main);
477            long      spcount  = GBT_get_species_count(gb_main);
478            GB_HASH  *hash     = GBS_create_hash(spcount, GB_IGNORE_CASE);
479            GB_ERROR  warning  = NULp;
480
481            if (spcount) {
482                arb_progress  progress("Renaming species", spcount);
483                const char   *add_field = AW_get_nameserver_addid(gb_main);
484
485                for (GBDATA *gb_species = GBT_first_species(gb_main);
486                     gb_species && !err;
487                     gb_species = GBT_next_species(gb_species))
488                {
489                    // @@@ DRY section here versus .@OTHERSECTION
490                    UNCOVERED();
491
492                    GBDATA *gb_name      = GB_entry(gb_species, "name");
493                    GBDATA *gb_full_name = GB_entry(gb_species, "full_name");
494                    GBDATA *gb_acc       = GBT_gen_accession_number(gb_species, ali_name);
495                    GBDATA *gb_addfield  = add_field[0] ? GB_entry(gb_species, add_field) : NULp;
496
497                    char *name      = gb_name      ? GB_read_string   (gb_name)     : strdup("");
498                    char *full_name = gb_full_name ? GB_read_string   (gb_full_name) : strdup("");
499                    char *acc       = gb_acc       ? GB_read_string   (gb_acc)      : strdup("");
500                    char *addid     = gb_addfield  ? GB_read_as_string(gb_addfield) : strdup(""); // empty value will be set to default by nameserver
501
502                    char *shrt = NULp;
503
504                    if (full_name[0] || acc[0] || addid[0]) {
505                        if (aisc_nput(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
506                                      LOCAL_FULL_NAME,  full_name,
507                                      LOCAL_ACCESSION,  acc,
508                                      LOCAL_ADDID,      addid,
509                                      LOCAL_ADVICE,     name,
510                                      NULp)) {
511                            err = "Connection Problems with the NAME_SERVER";
512                        }
513                        if (aisc_get(name_server.getLink(), AN_LOCAL, name_server.getLocs(),
514                                     LOCAL_GET_SHORT,   &shrt,
515                                     NULp)) {
516                            err = "Connection Problems with the NAME_SERVER";
517                        }
518                    }
519                    else {
520                        shrt = strdup(name);
521                    }
522                    if (!err) {
523                        char *newshrt = AWTC_create_numbered_suffix(hash, shrt, warning);
524                        if (newshrt) freeset(shrt, newshrt);
525
526                        GBS_incr_hash(hash, shrt);
527                        err = GBT_rename_species(name, shrt, true);
528                    }
529
530                    free(shrt);
531                    free(addid);
532                    free(acc);
533                    free(full_name);
534                    free(name);
535
536                    progress.inc_and_check_user_abort(err);
537                }
538            }
539            else {
540                gen_progress.sub_progress_skipped(); // trigger skipped subcounter
541            }
542
543            if (err) GBT_abort_rename_session();
544            else err = GBT_commit_rename_session();
545
546            GBS_free_hash(hash);
547            free(ali_name);
548
549            if (!err) {
550                err = warning;
551                if (warning) isWarning = true;
552            }
553        }
554        name_server.disconnect();
555    }
556
557    if (isWarningPtr) *isWarningPtr = isWarning;
558    gen_progress.done(); // needed if err
559
560    return err;
561}
562
563
564static void awt_rename_cb(AW_window *aww, GBDATA *gb_main) {
565    GB_ERROR error = AWTC_pars_names(gb_main, NULp);
566    if (error) aw_message(error);
567    aww->get_root()->awar(AWAR_TREE_REFRESH)->touch();
568}
569
570
571AW_window *AWTC_create_rename_window(AW_root *root, GBDATA *gb_main) {
572    AW_window_simple *aws = new AW_window_simple;
573    aws->init(root, "AUTORENAME_SPECIES", "Synchronize species IDs");
574
575    aws->load_xfig("awtc/autoren.fig");
576
577    aws->at("close");
578    aws->callback(AW_POPDOWN);
579    aws->create_button("CLOSE", "CLOSE", "C");
580
581    aws->at("help");
582    aws->callback(makeHelpCallback("rename.hlp"));
583    aws->create_button("HELP", "HELP", "H");
584
585    aws->at("go");
586    aws->highlight();
587    aws->callback(makeWindowCallback(awt_rename_cb, gb_main));
588    aws->create_button("GO", "GO", "G");
589
590    return aws;
591}
592
593UniqueNameDetector::UniqueNameDetector(GBDATA *gb_item_data, long additionalEntries) {
594    hash = GBS_create_hash(GB_number_of_subentries(gb_item_data)+additionalEntries, GB_IGNORE_CASE);
595
596    for (GBDATA *gb_item = GB_child(gb_item_data); gb_item; gb_item = GB_nextChild(gb_item)) {
597        GBDATA *gb_name = GB_entry(gb_item, "name");
598        if (gb_name) { // item has name -> insert to hash
599            GBS_write_hash(hash, GB_read_char_pntr(gb_name), 1);
600        }
601    }
602}
603
604UniqueNameDetector::~UniqueNameDetector() { GBS_free_hash(hash); }
605
606static char *makeUniqueShortName(const char *prefix, UniqueNameDetector& existing) {
607    // generates a non-existing short-name (name starts with prefix)
608    //
609    // returns NULp if it fails
610
611    char *result     = NULp;
612    int   prefix_len = strlen(prefix);
613
614    aw_assert(prefix_len<8); // prefix has to be shorter than 8 chars!
615    if (prefix_len<8) {
616        const int max_nums[8] = { 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10 };
617        static int next_try[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
618
619        int  max_num = max_nums[prefix_len];
620        char short_name[9];
621        strcpy(short_name, prefix);
622
623        char *dig_pos = short_name+prefix_len;
624        int   num     = next_try[prefix_len];
625        int   stop    = num ? num-1 : max_num;
626
627        while (num != stop) {
628            sprintf(dig_pos, "%i", num);
629            ++num;
630            if (!existing.name_known(short_name)) {
631                result = strdup(short_name);
632                break;
633            }
634            if (num == max_num && stop != max_num) num = 0;
635        }
636        if (num == max_num) num = 0;
637        next_try[prefix_len] = num;
638    }
639    return result;
640}
641
642char *AWTC_makeUniqueShortName(const char *prefix, UniqueNameDetector& existingNames) {
643    // generates a unique species name from prefix
644    // (prefix will be fillup with zero digits and then shortened down to first char)
645    //
646    // returns NULp if failed (and exports error)
647
648    int  len = strlen(prefix);
649    char p[9];
650    strncpy(p, prefix, 8);
651
652    if (len>8) len = 8;
653    else {
654        if (len == 0) p[len++] = 'x'; // don't use digit as first character
655        while (len<8) p[len++] = '0';
656    }
657
658    p[len] = 0;
659
660    char *result = NULp;
661
662    for (int l = len-1; l>0 && !result; --l) {
663        p[l]   = 0;
664        result = makeUniqueShortName(p, existingNames);
665    }
666
667    aw_assert(!result || strlen(result) <= 8);
668    if (!result) GB_export_errorf("Failed to create unique species ID (prefix='%s')", prefix);
669
670    return result;
671}
672
673char *AWTC_generate_random_name(UniqueNameDetector& existingNames) {
674    char *new_species_name = NULp;
675    char  short_name[9];
676    int   count            = 10000;
677
678    short_name[8] = 0;
679    while (count--) {
680        short_name[0] = 'a'+GB_random(26); // first character has to be alpha
681
682        for (int x=1; x<8; ++x) {
683            int r = GB_random(36); // rest may be alphanumeric
684            short_name[x] = r<10 ? ('0'+r) : ('a'+r-10);
685        }
686
687        if (!existingNames.name_known(short_name)) {
688            new_species_name = strdup(short_name);
689            break;
690        }
691    }
692
693    if (!new_species_name) {
694        aw_message("Failed to generate a random name - retrying (this might hang forever)");
695        return AWTC_generate_random_name(existingNames);
696    }
697
698    return new_species_name;
699}
700
701int AWTC_name_quality(const char *short_name) {
702    // result 0 = ok for external tools
703    //        1 = ok for ARB
704    //        2 = not ok
705
706    int len         = -1;
707    int alnum_count = 0;
708    int ascii_count = 0;
709
710    while (char c = short_name[++len]) {
711        alnum_count += (isalnum(c) != 0);
712        ascii_count += (c > 32 && c < 127);
713    }
714
715    if (len>0) {
716        if (len <= 8) {
717            if (len == alnum_count) return 0; // ok for external programs
718        }
719        if (len == ascii_count) return 1; // ok for ARB
720    }
721    return 2; // not ok
722}
723
724// --------------------------------------------------------------------------------
725
726#ifdef UNIT_TESTS
727#ifndef TEST_UNIT_H
728#include <test_unit.h>
729#endif
730
731void TEST_nameserver() {
732    // @@@ use environment 'nameserver' here
733}
734
735#endif // UNIT_TESTS
736
737// --------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.