]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/lib/util.c
Last major change for 1.27 code -- see kes13Nov02
[bacula/bacula] / bacula / src / lib / util.c
1 /*
2  *   util.c  miscellaneous utility subroutines for Bacula
3  * 
4  *    Kern Sibbald, MM
5  *
6  *   Version $Id$
7  */
8
9 /*
10    Copyright (C) 2000, 2001, 2002 Kern Sibbald and John Walker
11
12    This program is free software; you can redistribute it and/or
13    modify it under the terms of the GNU General Public License as
14    published by the Free Software Foundation; either version 2 of
15    the License, or (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20    General Public License for more details.
21
22    You should have received a copy of the GNU General Public
23    License along with this program; if not, write to the Free
24    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25    MA 02111-1307, USA.
26
27  */
28
29 #include "bacula.h"
30 #include "jcr.h"
31 #include "findlib/find.h"
32
33 /*
34  * Various Bacula Utility subroutines
35  *
36  */
37
38 /* Return true of buffer has all zero bytes */
39 int is_buf_zero(char *buf, int len)
40 {
41    uint64_t *ip = (uint64_t *)buf;
42    char *p;
43    int i, len64, done, rem;
44
45    /* Optimize by checking uint64_t for zero */
46    len64 = len >> sizeof(uint64_t);
47    for (i=0; i < len64; i++) {
48       if (ip[i] != 0) {
49          return 0;
50       }
51    }
52    done = len64 << sizeof(uint64_t);  /* bytes already checked */
53    p = buf + done;
54    rem = len - done;
55    for (i = 0; i < rem; i++) {
56       if (p[i] != 0) {
57          return 0;
58       }
59    }
60    return 1;
61 }
62
63 /*
64  * Convert a string duration to utime_t (64 bit seconds)
65  * Returns 0: if error
66            1: if OK, and value stored in value
67  */
68 int duration_to_utime(char *str, utime_t *value)
69 {
70    int i, ch, len;
71    double val;
72    static int  mod[] = {'*', 's', 'n', 'h', 'd', 'w', 'm', 'q', 'y', 0};
73    static int mult[] = {1,    1,  60, 60*60, 60*60*24, 60*60*24*7, 60*60*24*30, 
74                   60*60*24*91, 60*60*24*365};
75
76    /* Look for modifier */
77    len = strlen(str);
78    ch = str[len - 1];
79    i = 0;
80    if (B_ISALPHA(ch)) {
81       if (B_ISUPPER(ch)) {
82          ch = tolower(ch);
83       }
84       while (mod[++i] != 0) {
85          if (ch == mod[i]) {
86             len--;
87             str[len] = 0; /* strip modifier */
88             break;
89          }
90       }
91    }
92    if (mod[i] == 0 || !is_a_number(str)) {
93       return 0;
94    }
95    val = strtod(str, NULL);
96    if (errno != 0 || val < 0) {
97       return 0;
98    }
99    *value = (utime_t)(val * mult[i]);
100    return 1;
101
102 }
103
104 /*
105  * Edit a utime "duration" into ASCII
106  */
107 char *edit_utime(utime_t val, char *buf)
108 {
109    char mybuf[30];
110    static int mult[] = {60*60*24*365, 60*60*24*30, 60*60*24, 60*60, 60};
111    static char *mod[]  = {"year",  "month",  "day", "hour", "min"};
112    int i;
113    uint32_t times;
114
115    *buf = 0;
116    for (i=0; i<5; i++) {
117       times = val / mult[i];
118       if (times > 0) {
119          val = val - (utime_t)times * mult[i];
120          sprintf(mybuf, "%d %s%s ", times, mod[i], times>1?"s":"");
121          strcat(buf, mybuf);
122       }
123    }
124    if (val == 0 && strlen(buf) == 0) {     
125       strcat(buf, "0 secs");
126    } else if (val != 0) {
127       sprintf(mybuf, "%d sec%s", (uint32_t)val, val>1?"s":"");
128       strcat(buf, mybuf);
129    }
130    return buf;
131 }
132
133 /*
134  * Check if specified string is a number or not.
135  *  Taken from SQLite, cool, thanks.
136  */
137 int is_a_number(const char *n)
138 {
139    int digit_seen = 0;
140
141    if( *n == '-' || *n == '+' ) {
142       n++;
143    }
144    while (B_ISDIGIT(*n)) {
145       digit_seen = 1;
146       n++;
147    }
148    if (digit_seen && *n == '.') {
149       n++;
150       while (B_ISDIGIT(*n)) { n++; }
151    }
152    if (digit_seen && (*n == 'e' || *n == 'E')
153        && (B_ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && B_ISDIGIT(n[2])))) {
154       n += 2;                         /* skip e- or e+ or e digit */
155       while (B_ISDIGIT(*n)) { n++; }
156    }
157    return digit_seen && *n==0;
158 }
159
160
161 /*
162  * Edit an integer number with commas, the supplied buffer
163  * must be at least 27 bytes long.  The incoming number
164  * is always widened to 64 bits.
165  */
166 char *edit_uint64_with_commas(uint64_t val, char *buf)
167 {
168    sprintf(buf, "%" lld, val);
169    return add_commas(buf, buf);
170 }
171
172 /*
173  * Edit an integer number, the supplied buffer
174  * must be at least 27 bytes long.  The incoming number
175  * is always widened to 64 bits.
176  */
177 char *edit_uint64(uint64_t val, char *buf)
178 {
179    sprintf(buf, "%" lld, val);
180    return buf;
181 }
182
183
184 /*
185  * Add commas to a string, which is presumably
186  * a number.  
187  */
188 char *add_commas(char *val, char *buf)
189 {
190    int len, nc;
191    char *p, *q;
192    int i;
193
194    if (val != buf) {
195       strcpy(buf, val);
196    }
197    len = strlen(buf);
198    if (len < 1) {
199       len = 1;
200    }
201    nc = (len - 1) / 3;
202    p = buf+len;
203    q = p + nc;
204    *q-- = *p--;
205    for ( ; nc; nc--) {
206       for (i=0; i < 3; i++) {
207           *q-- = *p--;
208       }
209       *q-- = ',';
210    }   
211    return buf;
212 }
213
214
215 /* Convert a string in place to lower case */
216 void lcase(char *str)
217 {
218    while (*str) {
219       if (B_ISUPPER(*str))
220          *str = tolower((int)(*str));
221        str++;
222    }
223 }
224
225 /* Convert spaces to non-space character. 
226  * This makes scanf of fields containing spaces easier.
227  */
228 void
229 bash_spaces(char *str)
230 {
231    while (*str) {
232       if (*str == ' ')
233          *str = 0x1;
234       str++;
235    }
236 }
237
238 /* Convert non-space characters (0x1) back into spaces */
239 void
240 unbash_spaces(char *str)
241 {
242    while (*str) {
243      if (*str == 0x1)
244         *str = ' ';
245      str++;
246    }
247 }
248
249 /* Strip any trailing junk from the command */
250 void strip_trailing_junk(char *cmd)
251 {
252    char *p;
253    p = cmd + strlen(cmd) - 1;
254
255    /* strip trailing junk from command */
256    while ((p >= cmd) && (*p == '\n' || *p == '\r' || *p == ' '))
257       *p-- = 0;
258 }
259
260 /* Strip any trailing slashes from a directory path */
261 void strip_trailing_slashes(char *dir)
262 {
263    char *p;
264    p = dir + strlen(dir) - 1;
265
266    /* strip trailing slashes */
267    while ((p >= dir) && (*p == '/'))
268       *p-- = 0;
269 }
270
271 /*
272  * Skip spaces
273  *  Returns: 0 on failure (EOF)             
274  *           1 on success
275  *           new address in passed parameter 
276  */
277 int skip_spaces(char **msg)
278 {
279    char *p = *msg;
280    if (!p) {
281       return 0;
282    }
283    while (*p && *p == ' ') {
284       p++;
285    }
286    *msg = p;
287    return *p ? 1 : 0;
288 }
289
290 /*
291  * Skip nonspaces
292  *  Returns: 0 on failure (EOF)             
293  *           1 on success
294  *           new address in passed parameter 
295  */
296 int skip_nonspaces(char **msg)
297 {
298    char *p = *msg;
299
300    if (!p) {
301       return 0;
302    }
303    while (*p && *p != ' ') {
304       p++;
305    }
306    *msg = p;
307    return *p ? 1 : 0;
308 }
309
310 /* folded search for string - case insensitive */
311 int
312 fstrsch(char *a, char *b)   /* folded case search */
313 {
314    register char *s1,*s2;
315    register char c1, c2;
316
317    s1=a;
318    s2=b;
319    while (*s1) {                      /* do it the fast way */
320       if ((*s1++ | 0x20) != (*s2++ | 0x20))
321          return 0;                    /* failed */
322    }
323    while (*a) {                       /* do it over the correct slow way */
324       if (B_ISUPPER(c1 = *a)) {
325          c1 = tolower((int)c1);
326       }
327       if (B_ISUPPER(c2 = *b)) {
328          c2 = tolower((int)c2);
329       }
330       if (c1 != c2) {
331          return 0;
332       }
333       a++;
334       b++;
335    }
336    return 1;
337 }
338
339
340 char *encode_time(time_t time, char *buf)
341 {
342    struct tm tm;
343    int n = 0;
344
345    if (localtime_r(&time, &tm)) {
346       n = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
347                    tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
348                    tm.tm_hour, tm.tm_min, tm.tm_sec);
349    }
350    return buf+n;
351 }
352
353 /*
354  * Concatenate a string (str) onto a pool memory buffer pm
355  */
356 void pm_strcat(POOLMEM **pm, char *str)
357 {
358    int pmlen = strlen(*pm);
359    int len = strlen(str) + 1;
360
361    *pm = check_pool_memory_size(*pm, pmlen + len);
362    memcpy(*pm+pmlen, str, len);
363 }
364
365
366 /*
367  * Copy a string (str) into a pool memory buffer pm
368  */
369 void pm_strcpy(POOLMEM **pm, char *str)
370 {
371    int len = strlen(str) + 1;
372
373    *pm = check_pool_memory_size(*pm, len);
374    memcpy(*pm, str, len);
375 }
376
377
378 /*
379  * Convert a JobStatus code into a human readable form
380  */
381 void jobstatus_to_ascii(int JobStatus, char *msg, int maxlen)
382 {
383    char *termstat, jstat[2];
384
385    switch (JobStatus) {
386       case JS_Terminated:
387          termstat = _("OK");
388          break;
389      case JS_FatalError:
390      case JS_ErrorTerminated:
391          termstat = _("Error");
392          break;
393      case JS_Error:
394          termstat = _("Non-fatal error");
395          break;
396      case JS_Cancelled:
397          termstat = _("Cancelled");
398          break;
399      case JS_Differences:
400          termstat = _("Verify differences");
401          break;
402      default:
403          jstat[0] = last_job.JobStatus;
404          jstat[1] = 0;
405          termstat = jstat;
406          break;
407    }
408    strncpy(msg, termstat, maxlen);
409    msg[maxlen-1] = 0;
410 }
411
412 /*
413  * Convert Job Termination Status into a string
414  */
415 char *job_status_to_str(int stat) 
416 {
417    char *str;
418
419    switch (stat) {
420    case JS_Terminated:
421       str = _("OK");
422       break;
423    case JS_ErrorTerminated:
424    case JS_Error:
425       str = _("Error");
426       break;
427    case JS_FatalError:
428       str = _("Fatal Error");
429       break;
430    case JS_Cancelled:
431       str = _("Cancelled");
432       break;
433    case JS_Differences:
434       str = _("Differences");
435       break;
436    default:
437       str = _("Unknown term code");
438       break;
439    }
440    return str;
441 }
442
443
444 /*
445  * Convert Job Type into a string
446  */
447 char *job_type_to_str(int type) 
448 {
449    char *str;
450
451    switch (type) {
452    case JT_BACKUP:
453       str = _("Backup");
454       break;
455    case JT_VERIFY:
456       str = _("Verify");
457       break;
458    case JT_RESTORE:
459       str = _("Restore");
460       break;
461    case JT_ADMIN:
462       str = _("Admin");
463       break;
464    default:
465       str = _("Unknown Type");
466       break;
467    }
468    return str;
469 }
470
471 /*
472  * Convert Job Level into a string
473  */
474 char *job_level_to_str(int level) 
475 {
476    char *str;
477
478    switch (level) {
479    case L_FULL:
480       str = _("Full");
481       break;
482    case L_INCREMENTAL:
483       str = _("Incremental");
484       break;
485    case L_DIFFERENTIAL:
486       str = _("Differential");
487       break;
488    case L_LEVEL:
489       str = _("Level");
490       break;
491    case L_SINCE:
492       str = _("Since");
493       break;
494    case L_VERIFY_CATALOG:
495       str = _("Verify Catalog");
496       break;
497    case L_VERIFY_INIT:
498       str = _("Verify Init Catalog");
499       break;
500    case L_VERIFY_VOLUME_TO_CATALOG:
501       str = _("Verify Volume to Catalog");
502       break;
503    case L_VERIFY_DATA:
504       str = _("Verify Data");
505       break;
506    default:
507       str = _("Unknown Job Level");
508       break;
509    }
510    return str;
511 }
512
513
514 /***********************************************************************
515  * Encode the mode bits into a 10 character string like LS does
516  ***********************************************************************/
517
518 char *encode_mode(mode_t mode, char *buf)
519 {
520   char *cp = buf;  
521
522   *cp++ = S_ISDIR(mode) ? 'd' : S_ISBLK(mode) ? 'b' : S_ISCHR(mode) ? 'c' :
523           S_ISLNK(mode) ? 'l' : '-';
524   *cp++ = mode & S_IRUSR ? 'r' : '-';
525   *cp++ = mode & S_IWUSR ? 'w' : '-';
526   *cp++ = (mode & S_ISUID
527                ? (mode & S_IXUSR ? 's' : 'S')
528                : (mode & S_IXUSR ? 'x' : '-'));
529   *cp++ = mode & S_IRGRP ? 'r' : '-';
530   *cp++ = mode & S_IWGRP ? 'w' : '-';
531   *cp++ = (mode & S_ISGID
532                ? (mode & S_IXGRP ? 's' : 'S')
533                : (mode & S_IXGRP ? 'x' : '-'));
534   *cp++ = mode & S_IROTH ? 'r' : '-';
535   *cp++ = mode & S_IWOTH ? 'w' : '-';
536   *cp++ = (mode & S_ISVTX
537                ? (mode & S_IXOTH ? 't' : 'T')
538                : (mode & S_IXOTH ? 'x' : '-'));
539   *cp = '\0';
540   return cp;
541 }
542
543
544 int do_shell_expansion(char *name)
545 {
546 /*  ****FIXME***** this should work for Win32 too */
547 #define UNIX
548 #ifdef UNIX
549 #ifndef PATH_MAX
550 #define PATH_MAX 512
551 #endif
552
553    int pid, wpid, stat;
554    int waitstatus;
555    char *shellcmd;
556    void (*istat)(int), (*qstat)(int);
557    int i;
558    char echout[PATH_MAX + 256];
559    int pfd[2];
560    static char meta[] = "~\\$[]*?`'<>\"";
561    int found = FALSE;
562    int len;
563
564    /* Check if any meta characters are present */
565    len = strlen(meta);
566    for (i = 0; i < len; i++) {
567       if (strchr(name, meta[i])) {
568          found = TRUE;
569          break;
570       }
571    }
572    stat = 0;
573    if (found) {
574 #ifdef nt
575        /* If the filename appears to be a DOS filename,
576           convert all backward slashes \ to Unix path
577           separators / and insert a \ infront of spaces. */
578        len = strlen(name);
579        if (len >= 3 && name[1] == ':' && name[2] == '\\') {
580           for (i=2; i<len; i++)
581              if (name[i] == '\\')
582                 name[i] = '/';
583        }
584 #else
585        /* Pass string off to the shell for interpretation */
586        if (pipe(pfd) == -1)
587           return 0;
588        switch(pid = fork()) {
589        case -1:
590           break;
591
592        case 0:                            /* child */
593           /* look for shell */
594           if ((shellcmd = getenv("SHELL")) == NULL)
595              shellcmd = "/bin/sh";
596           close(1); dup(pfd[1]);          /* attach pipes to stdin and stdout */
597           close(2); dup(pfd[1]);
598           for (i = 3; i < 32; i++)        /* close everything else */
599              close(i);
600           strcpy(echout, "echo ");        /* form echo command */
601           strcat(echout, name);
602           execl(shellcmd, shellcmd, "-c", echout, NULL); /* give to shell */
603           exit(127);                      /* shouldn't get here */
604
605        default:                           /* parent */
606           /* read output from child */
607           i = read(pfd[0], echout, sizeof echout);
608           echout[--i] = 0;                /* set end of string */
609           /* look for first word or first line. */
610           while (--i >= 0) {
611              if (echout[i] == ' ' || echout[i] == '\n')
612                 echout[i] = 0;            /* keep only first one */
613           }
614           istat = signal(SIGINT, SIG_IGN);
615           qstat = signal(SIGQUIT, SIG_IGN);
616           /* wait for child to exit */
617           while ((wpid = wait(&waitstatus)) != pid && wpid != -1)
618              { ; }
619           signal(SIGINT, istat);
620           signal(SIGQUIT, qstat);
621           strcpy(name, echout);
622           stat = 1;
623           break;
624        }
625        close(pfd[0]);                     /* close pipe */
626        close(pfd[1]);
627 #endif /* nt */
628    }
629    return stat;
630
631 #endif /* UNIX */
632
633 #if  MSC | MSDOS | __WATCOMC__
634
635    char prefix[100], *env, *getenv();
636
637    /* Home directory reference? */
638    if (*name == '~' && (env=getenv("HOME"))) {
639       strcpy(prefix, env);            /* copy HOME directory name */
640       name++;                         /* skip over ~ in name */
641       strcat(prefix, name);
642       name--;                         /* get back to beginning */
643       strcpy(name, prefix);           /* move back into name */
644    }
645    return 1;
646 #endif
647
648 }
649
650 #define MAX_ARGV 100
651 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_arg);
652
653 /*
654  * Run an external program. Optionally wait a specified number
655  *   of seconds. Program killed if wait exceeded. Optionally
656  *   return the output from the program (normally a single line).
657  */
658 int run_program(char *prog, int wait, POOLMEM *results)
659 {
660    int stat = ETIME;
661    int chldstatus = 0;
662    pid_t pid1, pid2 = 0;
663    int pfd[2];
664    char *bargv[MAX_ARGV];
665    int bargc;
666
667    
668    build_argc_argv(prog, &bargc, bargv, MAX_ARGV);
669 #ifdef xxxxxxxxxx
670    printf("argc=%d\n", bargc);
671    int i;
672    for (i=0; i<bargc; i++) {
673       printf("argc=%d argv=%s\n", i, bargv[i]);
674    }
675 #endif
676
677    if (results && pipe(pfd) == -1) {
678       return errno;
679    }
680    /* Start worker process */
681    switch (pid1 = fork()) {
682    case -1:
683       break;
684
685    case 0:                            /* child */
686 //    printf("execl of %s\n", prog);
687       if (results) {
688          close(1); dup(pfd[1]);       /* attach pipes to stdin and stdout */
689          close(2); dup(pfd[1]);
690       }
691       execvp(bargv[0], bargv);
692       exit(errno);                   /* shouldn't get here */
693
694    default:                           /* parent */
695       /* start timer process */
696       if (wait > 0) {
697          switch (pid2=fork()) {
698          case -1:
699             break;
700          case 0:                         /* child 2 */
701             /* Time the worker process */  
702             sleep(wait);
703             if (kill(pid1, SIGTERM) == 0) { /* time expired kill it */
704                exit(0);
705             }
706             sleep(3);
707             kill(pid1, SIGKILL);
708             exit(0);
709          default:                        /* parent */
710             break;
711          }
712       }
713
714       /* Parent continues here */
715       int i;
716       if (results) {
717          i = read(pfd[0], results, sizeof_pool_memory(results) - 1);
718          if (--i < 0) {
719             i = 0;
720          }
721          results[i] = 0;                /* set end of string */
722       }
723       /* wait for worker child to exit */
724       for ( ;; ) {
725          pid_t wpid;
726          wpid = waitpid(pid1, &chldstatus, 0);         
727          if (wpid == pid1 || (errno != EINTR)) {
728             break;
729          }
730       }
731       if (WIFEXITED(chldstatus))
732          stat = WEXITSTATUS(chldstatus);
733
734       if (wait > 0) {
735          kill(pid2, SIGKILL);           /* kill off timer process */
736          waitpid(pid2, &chldstatus, 0); /* reap timer process */
737       }
738       if (results) { 
739          close(pfd[0]);              /* close pipe */
740          close(pfd[1]);
741       }
742       break;
743    }
744    return stat;
745 }
746
747 /*
748  * Build argc and argv from a string
749  */
750 static void build_argc_argv(char *cmd, int *bargc, char *bargv[], int max_argv)
751 {
752    int i, quote;
753    char *p, *q;
754    int argc = 0;
755
756    argc = 0;
757    for (i=0; i<max_argv; i++)
758       bargv[i] = NULL;
759
760    p = cmd;
761    quote = 0;
762    while  (*p && (*p == ' ' || *p == '\t'))
763       p++;
764    if (*p == '\"') {
765       quote = 1;
766       p++;
767    }
768    if (*p) {
769       while (*p && argc < MAX_ARGV) {
770          q = p;
771          if (quote) {
772             while (*q && *q != '\"')
773             q++;
774             quote = 0;
775          } else {
776             while (*q && *q != ' ')
777             q++;
778          }
779          if (*q)
780             *(q++) = '\0';
781          bargv[argc++] = p;
782          p = q;
783          while (*p && (*p == ' ' || *p == '\t'))
784             p++;
785          if (*p == '\"') {
786             quote = 1;
787             p++;
788          }
789       }
790    }
791    *bargc = argc;
792 }
793
794 /*  MAKESESSIONKEY  --  Generate session key with optional start
795                         key.  If mode is TRUE, the key will be
796                         translated to a string, otherwise it is
797                         returned as 16 binary bytes.
798
799     from SpeakFreely by John Walker */
800
801 void makeSessionKey(char *key, char *seed, int mode)
802 {
803      int j, k;
804      struct MD5Context md5c;
805      unsigned char md5key[16], md5key1[16];
806      char s[1024];
807
808      s[0] = 0;
809      if (seed != NULL) {
810         strcat(s, seed);
811      }
812
813      /* The following creates a seed for the session key generator
814         based on a collection of volatile and environment-specific
815         information unlikely to be vulnerable (as a whole) to an
816         exhaustive search attack.  If one of these items isn't
817         available on your machine, replace it with something
818         equivalent or, if you like, just delete it. */
819
820      sprintf(s + strlen(s), "%lu", (unsigned long) getpid());
821      sprintf(s + strlen(s), "%lu", (unsigned long) getppid());
822      getcwd(s + strlen(s), 256);
823      sprintf(s + strlen(s), "%lu", (unsigned long) clock());
824      sprintf(s + strlen(s), "%lu", (unsigned long) time(NULL));
825 #ifdef Solaris
826      sysinfo(SI_HW_SERIAL,s + strlen(s), 12);
827 #endif
828 #ifdef HAVE_GETHOSTID
829      sprintf(s + strlen(s), "%lu", (unsigned long) gethostid());
830 #endif
831 #ifdef HAVE_GETDOMAINNAME
832      getdomainname(s + strlen(s), 256);
833 #endif
834      gethostname(s + strlen(s), 256);
835      sprintf(s + strlen(s), "%u", (unsigned)getuid());
836      sprintf(s + strlen(s), "%u", (unsigned)getgid());
837      MD5Init(&md5c);
838      MD5Update(&md5c, (unsigned char *)s, strlen(s));
839      MD5Final(md5key, &md5c);
840      sprintf(s + strlen(s), "%lu", (unsigned long) ((time(NULL) + 65121) ^ 0x375F));
841      MD5Init(&md5c);
842      MD5Update(&md5c, (unsigned char *)s, strlen(s));
843      MD5Final(md5key1, &md5c);
844 #define nextrand    (md5key[j] ^ md5key1[j])
845      if (mode) {
846         for (j = k = 0; j < 16; j++) {
847             unsigned char rb = nextrand;
848
849 #define Rad16(x) ((x) + 'A')
850             key[k++] = Rad16((rb >> 4) & 0xF);
851             key[k++] = Rad16(rb & 0xF);
852 #undef Rad16
853             if (j & 1) {
854                  key[k++] = '-';
855             }
856         }
857         key[--k] = 0;
858      } else {
859         for (j = 0; j < 16; j++) {
860             key[j] = nextrand;
861         }
862      }
863 }
864 #undef nextrand