PageRenderTime 100ms CodeModel.GetById 4ms app.highlight 82ms RepoModel.GetById 1ms app.codeStats 1ms

/src/common/hostlist.c

https://code.google.com/
C | 2508 lines | 1658 code | 472 blank | 378 comment | 459 complexity | 15540c5efd6b0cb1da882e391ad4b16d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/*****************************************************************************\
   2 *  $Id: hostlist.c 9950 2010-08-31 21:57:35Z grondo $
   3 *****************************************************************************
   4 *  $LSDId: hostlist.c 9950 2010-08-31 21:57:35Z grondo $
   5 *****************************************************************************
   6 *  Copyright (C) 2002 The Regents of the University of California.
   7 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
   8 *  Written by Mark Grondona <mgrondona@llnl.gov>
   9 *  UCRL-CODE-2002-040.
  10 *  
  11 *  This file is part of SLURM, a resource management program.
  12 *  For details, see <http://www.llnl.gov/linux/slurm/>.
  13 *  
  14 *  SLURM is free software; you can redistribute it and/or modify it under
  15 *  the terms of the GNU General Public License as published by the Free
  16 *  Software Foundation; either version 2 of the License, or (at your option)
  17 *  any later version.
  18 *  
  19 *  SLURM is distributed in the hope that it will be useful, but WITHOUT ANY
  20 *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  21 *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
  22 *  details.
  23 *  
  24 *  You should have received a copy of the GNU General Public License along
  25 *  with SLURM; if not, write to the Free Software Foundation, Inc.,
  26 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
  27\*****************************************************************************/
  28
  29#ifdef HAVE_CONFIG_H
  30#  include "config.h"
  31#  if HAVE_STRING_H
  32#    include <string.h>
  33#  endif
  34#  if HAVE_PTHREAD_H
  35#    include <pthread.h>
  36#  endif
  37#else                /* !HAVE_CONFIG_H */
  38#  include <string.h>
  39#  include <pthread.h>
  40#endif                /* HAVE_CONFIG_H */
  41
  42#include <stdio.h>
  43#include <stdlib.h>
  44#include <stdarg.h>
  45#include <assert.h>
  46#include <errno.h>
  47#include <ctype.h>
  48#include <sys/param.h>
  49#include <unistd.h>
  50
  51#include "hostlist.h"
  52
  53/*
  54 * lsd_fatal_error : fatal error macro
  55 */
  56#ifdef WITH_LSD_FATAL_ERROR_FUNC
  57#  undef lsd_fatal_error
  58   extern void lsd_fatal_error(char *file, int line, char *mesg);
  59#else /* !WITH_LSD_FATAL_ERROR_FUNC */
  60#  ifndef lsd_fatal_error
  61#    define lsd_fatal_error(file, line, mesg)                                \
  62       do {                                                                  \
  63           fprintf(stderr, "ERROR: [%s:%d] %s: %s\n",                        \
  64           file, line, mesg, strerror(errno));                               \
  65       } while (0)
  66#  endif /* !lsd_fatal_error */
  67#endif /* !WITH_LSD_FATAL_ERROR_FUNC */
  68
  69/*
  70 * lsd_nomem_error
  71 */
  72#ifdef WITH_LSD_NOMEM_ERROR_FUNC
  73#  undef lsd_nomem_error
  74   extern void * lsd_nomem_error(char *file, int line, char *mesg);
  75#else /* !WITH_LSD_NOMEM_ERROR_FUNC */
  76#  ifndef lsd_nomem_error
  77#    define lsd_nomem_error(file, line, mesg) (NULL)
  78#  endif /* !lsd_nomem_error */
  79#endif /* !WITH_LSD_NOMEM_ERROR_FUNC */
  80
  81/*
  82 * OOM helper function
  83 *  Automatically call lsd_nomem_error with appropriate args
  84 *  and set errno to ENOMEM
  85 */
  86#define out_of_memory(mesg)                                                  \
  87    do {                                                                     \
  88        errno = ENOMEM;                                                      \
  89        return(lsd_nomem_error(__FILE__, __LINE__, mesg));                   \
  90    } while (0)
  91
  92/* 
  93 * Some constants and tunables:
  94 */
  95
  96/* number of elements to allocate when extending the hostlist array */
  97#define HOSTLIST_CHUNK    16
  98
  99/* max host range: anything larger will be assumed to be an error */
 100#define MAX_RANGE    16384    /* 16K Hosts */
 101
 102/* max host suffix value */
 103#define MAX_HOST_SUFFIX 1<<25
 104
 105/* max number of ranges that will be processed between brackets */
 106#define MAX_RANGES    10240    /* 10K Ranges */
 107
 108/* size of internal hostname buffer (+ some slop), hostnames will probably
 109 * be truncated if longer than MAXHOSTNAMELEN */
 110#ifndef MAXHOSTNAMELEN
 111#define MAXHOSTNAMELEN    64
 112#endif
 113
 114/* max size of internal hostrange buffer */
 115#define MAXHOSTRANGELEN 1024
 116
 117/* ----[ Internal Data Structures ]---- */
 118
 119/* hostname type: A convenience structure used in parsing single hostnames */
 120struct hostname_components {
 121    char *hostname;         /* cache of initialized hostname        */
 122    char *prefix;           /* hostname prefix                      */
 123    unsigned long num;      /* numeric suffix                       */
 124
 125    /* string representation of numeric suffix
 126     * points into `hostname'                                       */
 127    char *suffix;
 128};
 129
 130typedef struct hostname_components *hostname_t;
 131
 132/* hostrange type: A single prefix with `hi' and `lo' numeric suffix values */
 133struct hostrange_components {
 134    char *prefix;        /* alphanumeric prefix: */
 135
 136    /* beginning (lo) and end (hi) of suffix range */
 137    unsigned long lo, hi;
 138
 139    /* width of numeric output format
 140     * (pad with zeros up to this width) */
 141    int width;
 142
 143    /* If singlehost is 1, `lo' and `hi' are invalid */
 144    unsigned singlehost:1;
 145};
 146
 147typedef struct hostrange_components *hostrange_t;
 148
 149/* The hostlist type: An array based list of hostrange_t's */
 150struct hostlist {
 151#ifndef NDEBUG
 152#define HOSTLIST_MAGIC    57005
 153    int magic;
 154#endif
 155#if    WITH_PTHREADS
 156    pthread_mutex_t mutex;
 157#endif                /* WITH_PTHREADS */
 158
 159    /* current number of elements available in array */
 160    int size;
 161
 162    /* current number of ranges stored in array */
 163    int nranges;
 164
 165    /* current number of hosts stored in hostlist */
 166    int nhosts;
 167
 168    /* pointer to hostrange array */
 169    hostrange_t *hr;
 170
 171    /* list of iterators */
 172    struct hostlist_iterator *ilist;
 173
 174};
 175
 176
 177/* a hostset is a wrapper around a hostlist */
 178struct hostset {
 179    hostlist_t hl;
 180};
 181
 182struct hostlist_iterator {
 183#ifndef NDEBUG
 184    int magic;
 185#endif
 186    /* hostlist we are traversing */
 187    hostlist_t hl;
 188
 189    /* current index of iterator in hl->hr[] */
 190    int idx;
 191
 192    /* current hostrange object in list hl, i.e. hl->hr[idx] */
 193    hostrange_t hr;
 194
 195    /* current depth we've traversed into range hr */
 196    int depth;
 197
 198    /* next ptr for lists of iterators */
 199    struct hostlist_iterator *next;
 200};
 201
 202
 203/* ---- ---- */
 204
 205/* ------[ static function prototypes ]------ */
 206
 207static void _error(char *file, int line, char *mesg, ...);
 208static char * _next_tok(char *, char **);
 209static int    _zero_padded(unsigned long, int);
 210static int    _width_equiv(unsigned long, int *, unsigned long, int *);
 211
 212static int           host_prefix_end(const char *);
 213static hostname_t    hostname_create(const char *);
 214static void          hostname_destroy(hostname_t);
 215static int           hostname_suffix_is_valid(hostname_t);
 216static int           hostname_suffix_width(hostname_t);
 217
 218static hostrange_t   hostrange_new(void);
 219static hostrange_t   hostrange_create_single(const char *);
 220static hostrange_t   hostrange_create(char *, unsigned long, unsigned long, int);
 221static unsigned long hostrange_count(hostrange_t);
 222static hostrange_t   hostrange_copy(hostrange_t);
 223static void          hostrange_destroy(hostrange_t);
 224static hostrange_t   hostrange_delete_host(hostrange_t, unsigned long);
 225static int           hostrange_cmp(hostrange_t, hostrange_t);
 226static int           hostrange_prefix_cmp(hostrange_t, hostrange_t);
 227static int           hostrange_within_range(hostrange_t, hostrange_t);
 228static int           hostrange_width_combine(hostrange_t, hostrange_t);
 229static int           hostrange_empty(hostrange_t);
 230static char *        hostrange_pop(hostrange_t);
 231static char *        hostrange_shift(hostrange_t);
 232static int           hostrange_join(hostrange_t, hostrange_t);
 233static hostrange_t   hostrange_intersect(hostrange_t, hostrange_t);
 234static int           hostrange_hn_within(hostrange_t, hostname_t);
 235static size_t        hostrange_to_string(hostrange_t hr, size_t, char *, char *);
 236static size_t        hostrange_numstr(hostrange_t, size_t, char *);
 237
 238static hostlist_t  hostlist_new(void);
 239static hostlist_t _hostlist_create_bracketed(const char *, char *, char *);
 240static int         hostlist_resize(hostlist_t, size_t);
 241static int         hostlist_expand(hostlist_t);
 242static int         hostlist_push_range(hostlist_t, hostrange_t);
 243static int         hostlist_push_hr(hostlist_t, char *, unsigned long,
 244                                    unsigned long, int);
 245static int         hostlist_insert_range(hostlist_t, hostrange_t, int);
 246static void        hostlist_delete_range(hostlist_t, int n);
 247static void        hostlist_coalesce(hostlist_t hl);
 248static void        hostlist_collapse(hostlist_t hl);
 249static hostlist_t _hostlist_create(const char *, char *, char *);
 250static void        hostlist_shift_iterators(hostlist_t, int, int, int);
 251static int        _attempt_range_join(hostlist_t, int);
 252static int        _is_bracket_needed(hostlist_t, int);
 253
 254static hostlist_iterator_t hostlist_iterator_new(void);
 255static void               _iterator_advance(hostlist_iterator_t);
 256static void               _iterator_advance_range(hostlist_iterator_t);
 257
 258static int hostset_find_host(hostset_t, const char *);
 259
 260/* ------[ macros ]------ */
 261
 262#ifdef WITH_PTHREADS
 263#  define mutex_init(mutex)                                                  \
 264     do {                                                                    \
 265        int e = pthread_mutex_init(mutex, NULL);                             \
 266        if (e) {                                                             \
 267            errno = e;                                                       \
 268            lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex init:");     \
 269            abort();                                                         \
 270        }                                                                    \
 271     } while (0)
 272
 273#  define mutex_lock(mutex)                                                  \
 274     do {                                                                    \
 275        int e = pthread_mutex_lock(mutex);                                   \
 276        if (e) {                                                             \
 277           errno = e;                                                        \
 278           lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex lock:");      \
 279           abort();                                                          \
 280        }                                                                    \
 281     } while (0)
 282
 283#  define mutex_unlock(mutex)                                                \
 284     do {                                                                    \
 285        int e = pthread_mutex_unlock(mutex);                                 \
 286        if (e) {                                                             \
 287            errno = e;                                                       \
 288            lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex unlock:");   \
 289            abort();                                                         \
 290        }                                                                    \
 291     } while (0)
 292
 293#  define mutex_destroy(mutex)                                               \
 294     do {                                                                    \
 295        int e = pthread_mutex_destroy(mutex);                                \
 296        if (e) {                                                             \
 297            errno = e;                                                       \
 298            lsd_fatal_error(__FILE__, __LINE__, "hostlist mutex destroy:");  \
 299            abort();                                                         \
 300        }                                                                    \
 301     } while (0)
 302
 303#else                /* !WITH_PTHREADS */
 304
 305#  define mutex_init(mutex)
 306#  define mutex_lock(mutex)
 307#  define mutex_unlock(mutex)
 308#  define mutex_destroy(mutex)
 309
 310#endif                /* WITH_PTHREADS */
 311
 312#define LOCK_HOSTLIST(_hl)                                                   \
 313      do {                                                                   \
 314          assert(_hl != NULL);                                               \
 315          mutex_lock(&(_hl)->mutex);                                         \
 316          assert((_hl)->magic == HOSTLIST_MAGIC);                            \
 317      } while (0)
 318
 319#define UNLOCK_HOSTLIST(_hl)                                                 \
 320      do {                                                                   \
 321          mutex_unlock(&(_hl)->mutex);                                       \
 322      } while (0)                       
 323
 324#define seterrno_ret(_errno, _rc)                                            \
 325      do {                                                                   \
 326          errno = _errno;                                                    \
 327          return _rc;                                                        \
 328      } while (0)
 329
 330/* ------[ Function Definitions ]------ */
 331
 332/* ----[ general utility functions ]---- */
 333
 334
 335/*
 336 *  Varargs capable error reporting via lsd_fatal_error()
 337 */
 338static void _error(char *file, int line, char *msg, ...)
 339{
 340    va_list ap;
 341    char    buf[1024];
 342    int     len = 0;
 343    va_start(ap, msg);
 344
 345    len = vsnprintf(buf, 1024, msg, ap);
 346    if ((len < 0) || (len > 1024)) 
 347        buf[1023] = '\0';
 348
 349    lsd_fatal_error(file, line, buf);
 350
 351    va_end(ap);
 352    return;
 353}
 354
 355
 356/*
 357 * Helper function for host list string parsing routines
 358 * Returns a pointer to the next token; additionally advance *str
 359 * to the next separator.
 360 *
 361 * next_tok was taken directly from pdsh courtesy of Jim Garlick.
 362 * (with modifications to support bracketed hostlists, i.e.:
 363 *  xxx[xx,xx,xx] is a single token)
 364 *
 365 * _next_tok now handles multiple brackets within the same token,
 366 * e.g.  node[01-30]-[1-2,6].
 367 */
 368static char * _next_tok(char *sep, char **str)
 369{
 370    char *tok;
 371    int level = 0;
 372
 373    /* push str past any leading separators */
 374    while (**str != '\0' && strchr(sep, **str) != '\0')
 375        (*str)++;
 376
 377    if (**str == '\0')
 378        return NULL;
 379
 380    /* assign token ptr */
 381    tok = *str;
 382
 383    while ( **str != '\0' &&
 384	    (level != 0 || strchr(sep, **str) == NULL) ) {
 385      if ( **str == '[' ) level++;
 386      else if ( **str == ']' ) level--;
 387      (*str)++;
 388    }
 389
 390   /* nullify consecutive separators and push str beyond them */
 391    while (**str != '\0' && strchr(sep, **str) != '\0')
 392        *(*str)++ = '\0';
 393
 394    return tok;
 395}
 396
 397
 398/* return the number of zeros needed to pad "num" to "width"
 399 */
 400static int _zero_padded(unsigned long num, int width)
 401{
 402    int n = 1;
 403    while (num /= 10L)
 404        n++;
 405    return width > n ? width - n : 0;
 406}
 407
 408/* test whether two format `width' parameters are "equivalent"
 409 * The width arguments "wn" and "wm" for integers "n" and "m" 
 410 * are equivalent if:
 411 *  
 412 *  o  wn == wm  OR
 413 *
 414 *  o  applying the same format width (either wn or wm) to both of  
 415 *     'n' and 'm' will not change the zero padding of *either* 'm' nor 'n'.
 416 *
 417 *  If this function returns 1 (or true), the appropriate width value
 418 *  (either 'wm' or 'wn') will have been adjusted such that both format
 419 *  widths are equivalent.
 420 */
 421static int _width_equiv(unsigned long n, int *wn, unsigned long m, int *wm)
 422{
 423    int npad, nmpad, mpad, mnpad;
 424
 425    if (wn == wm)
 426        return 1;
 427
 428    npad = _zero_padded(n, *wn);
 429    nmpad = _zero_padded(n, *wm);
 430    mpad = _zero_padded(m, *wm);
 431    mnpad = _zero_padded(m, *wn);
 432
 433    if (npad != nmpad && mpad != mnpad)
 434        return 0;
 435
 436    if (npad != nmpad) {
 437        if (mpad == mnpad) {
 438            *wm = *wn;
 439            return 1;
 440        } else
 441            return 0;
 442    } else {        /* mpad != mnpad */
 443        if (npad == nmpad) {
 444            *wn = *wm;
 445            return 1;
 446        } else
 447            return 0;
 448    }
 449
 450    /* not reached */
 451}
 452
 453
 454/* ----[ hostname_t functions ]---- */
 455
 456/* 
 457 * return the location of the last char in the hostname prefix
 458 */
 459static int host_prefix_end(const char *hostname)
 460{
 461    int idx = strlen(hostname) - 1;
 462
 463    while (idx >= 0 && isdigit((char) hostname[idx])) 
 464        idx--;
 465    return idx;
 466}
 467
 468static hostname_t hostname_create_with_suffix (const char *hostname, int idx)
 469{
 470    hostname_t hn = NULL;
 471    char *p = '\0';
 472
 473    assert(hostname != NULL);
 474
 475    if (!(hn = (hostname_t) malloc(sizeof(*hn))))
 476        out_of_memory("hostname create");
 477
 478    if (!(hn->hostname = strdup(hostname))) {
 479        free(hn);
 480        out_of_memory("hostname create");
 481    }
 482
 483    hn->num = 0;
 484    hn->prefix = NULL;
 485    hn->suffix = NULL;
 486
 487    if (idx == strlen(hostname) - 1) {
 488        if ((hn->prefix = strdup(hostname)) == NULL) {
 489            hostname_destroy(hn);
 490            out_of_memory("hostname prefix create");
 491        }
 492        return hn;
 493    }
 494
 495    hn->suffix = hn->hostname + idx + 1;
 496    hn->num = strtoul(hn->suffix, &p, 10);
 497
 498    if ((*p == '\0') && (hn->num <= MAX_HOST_SUFFIX)) {
 499        if (!(hn->prefix = malloc((idx + 2) * sizeof(char)))) {
 500            hostname_destroy(hn);
 501            out_of_memory("hostname prefix create");
 502        }
 503        memcpy(hn->prefix, hostname, idx + 1);
 504        hn->prefix[idx + 1] = '\0';
 505    } else {
 506        if (!(hn->prefix = strdup(hostname))) {
 507            hostname_destroy(hn);
 508            out_of_memory("hostname prefix create");
 509        }
 510        hn->suffix = NULL;
 511    }
 512
 513    return hn;
 514
 515}
 516
 517
 518/* 
 519 * create a hostname_t object from a string hostname
 520 */
 521static hostname_t hostname_create(const char *hostname)
 522{
 523    int idx = host_prefix_end (hostname);
 524    return hostname_create_with_suffix (hostname, idx);
 525}
 526
 527/* free a hostname object
 528 */
 529static void hostname_destroy(hostname_t hn)
 530{
 531    if (hn == NULL)
 532        return;
 533    hn->suffix = NULL;
 534    if (hn->hostname)
 535        free(hn->hostname);
 536    if (hn->prefix)
 537        free(hn->prefix);
 538    free(hn);
 539}
 540
 541/* return true if the hostname has a valid numeric suffix 
 542 */
 543static int hostname_suffix_is_valid(hostname_t hn)
 544{
 545    return hn->suffix != NULL;
 546}
 547
 548/* return the width (in characters) of the numeric part of the hostname
 549 */
 550static int hostname_suffix_width(hostname_t hn)
 551{
 552    assert(hn->suffix != NULL);
 553    return (int) strlen(hn->suffix);
 554}
 555
 556
 557/* ----[ hostrange_t functions ]---- */
 558
 559/* allocate a new hostrange object 
 560 */
 561static hostrange_t hostrange_new(void)
 562{
 563    hostrange_t new = (hostrange_t) malloc(sizeof(*new));
 564    if (!new) 
 565        out_of_memory("hostrange create");
 566    return new;
 567}
 568
 569/* Create a hostrange_t containing a single host without a valid suffix
 570 * hr->prefix will represent the entire hostname.
 571 */
 572static hostrange_t hostrange_create_single(const char *prefix)
 573{
 574    hostrange_t new;
 575
 576    assert(prefix != NULL);
 577
 578    if ((new = hostrange_new()) == NULL)
 579        goto error1;
 580
 581    if ((new->prefix = strdup(prefix)) == NULL)
 582        goto error2;
 583
 584    new->singlehost = 1;
 585    new->lo = 0L;
 586    new->hi = 0L;
 587    new->width = 0;
 588
 589    return new;
 590
 591  error2:
 592    free(new);
 593  error1:
 594    out_of_memory("hostrange create single");
 595}
 596
 597
 598/* Create a hostrange object with a prefix, hi, lo, and format width
 599 */
 600static hostrange_t
 601hostrange_create(char *prefix, unsigned long lo, unsigned long hi, int width)
 602{
 603    hostrange_t new;
 604
 605    assert(prefix != NULL);
 606
 607    if ((new = hostrange_new()) == NULL)
 608        goto error1;
 609
 610    if ((new->prefix = strdup(prefix)) == NULL)
 611        goto error2;
 612
 613    new->lo = lo;
 614    new->hi = hi;
 615    new->width = width;
 616
 617    new->singlehost = 0;
 618
 619    return new;
 620
 621  error2:
 622    free(new);
 623  error1:
 624    out_of_memory("hostrange create");
 625}
 626
 627
 628/* Return the number of hosts stored in the hostrange object
 629 */
 630static unsigned long hostrange_count(hostrange_t hr)
 631{
 632    assert(hr != NULL);
 633    if (hr->singlehost)
 634        return 1;
 635    else
 636        return hr->hi - hr->lo + 1;
 637}
 638
 639/* Copy a hostrange object
 640 */
 641static hostrange_t hostrange_copy(hostrange_t hr)
 642{
 643    assert(hr != NULL);
 644
 645    if (hr->singlehost)
 646        return hostrange_create_single(hr->prefix);
 647    else
 648        return hostrange_create(hr->prefix, hr->lo, hr->hi,
 649                    hr->width);
 650}
 651
 652
 653/* free memory allocated by the hostrange object
 654 */
 655static void hostrange_destroy(hostrange_t hr)
 656{
 657    if (hr == NULL)
 658        return;
 659    if (hr->prefix)
 660        free(hr->prefix);
 661    free(hr);
 662}
 663
 664/* hostrange_delete_host() deletes a specific host from the range.
 665 * If the range is split into two, the greater range is returned,
 666 * and `hi' of the lesser range is adjusted accordingly. If the
 667 * highest or lowest host is deleted from a range, NULL is returned
 668 * and the hostrange hr is adjusted properly.
 669 */
 670static hostrange_t hostrange_delete_host(hostrange_t hr, unsigned long n)
 671{
 672    hostrange_t new = NULL;
 673
 674    assert(hr != NULL);
 675    assert(n >= hr->lo && n <= hr->hi);
 676
 677    if (n == hr->lo)
 678        hr->lo++;
 679    else if (n == hr->hi)
 680        hr->hi--;
 681    else {
 682        if (!(new = hostrange_copy(hr)))
 683            out_of_memory("hostrange copy");
 684        hr->hi = n - 1;
 685        new->lo = n + 1;
 686    }
 687
 688    return new;
 689}
 690
 691/* hostrange_cmp() is used to sort hostrange objects. It will
 692 * sort based on the following (in order):
 693 *  o result of strcmp on prefixes
 694 *  o if widths are compatible, then: 
 695 *       sort based on lowest suffix in range
 696 *    else
 697 *       sort based on width                     */
 698static int hostrange_cmp(hostrange_t h1, hostrange_t h2)
 699{
 700    int retval;
 701
 702    assert(h1 != NULL);
 703    assert(h2 != NULL);
 704
 705    if ((retval = hostrange_prefix_cmp(h1, h2)) == 0)
 706        retval = hostrange_width_combine(h1, h2) ?
 707            h1->lo - h2->lo : h1->width - h2->width;
 708
 709    return retval;
 710}
 711
 712
 713/* compare the prefixes of two hostrange objects. 
 714 * returns:
 715 *    < 0   if h1 prefix is less than h2 OR h1 == NULL.
 716 *
 717 *      0   if h1's prefix and h2's prefix match, 
 718 *          UNLESS, either h1 or h2 (NOT both) do not have a valid suffix.
 719 *
 720 *    > 0   if h1's prefix is greater than h2's OR h2 == NULL. */
 721static int hostrange_prefix_cmp(hostrange_t h1, hostrange_t h2)
 722{
 723    int retval;
 724    if (h1 == NULL)
 725        return 1;
 726    if (h2 == NULL)
 727        return -1;
 728
 729    retval = strcmp(h1->prefix, h2->prefix);
 730    return retval == 0 ? h2->singlehost - h1->singlehost : retval;
 731}
 732
 733/* returns true if h1 and h2 would be included in the same bracketed hostlist.
 734 * h1 and h2 will be in the same bracketed list iff:
 735 *
 736 *  1. h1 and h2 have same prefix
 737 *  2. neither h1 nor h2 are singlet hosts (i.e. invalid suffix)
 738 *
 739 *  (XXX: Should incompatible widths be placed in the same bracketed list?
 740 *        There's no good reason not to, except maybe aesthetics)
 741 */
 742static int hostrange_within_range(hostrange_t h1, hostrange_t h2)
 743{
 744    if (hostrange_prefix_cmp(h1, h2) == 0)
 745        return h1->singlehost || h2->singlehost ? 0 : 1;
 746    else
 747        return 0;
 748}
 749
 750
 751/* compare two hostrange objects to determine if they are width 
 752 * compatible,  returns:
 753 *  1 if widths can safely be combined
 754 *  0 if widths cannot be safely combined
 755 */
 756static int hostrange_width_combine(hostrange_t h0, hostrange_t h1)
 757{
 758    assert(h0 != NULL);
 759    assert(h1 != NULL);
 760
 761    return _width_equiv(h0->lo, &h0->width, h1->lo, &h1->width);
 762}
 763
 764
 765/* Return true if hostrange hr contains no hosts, i.e. hi < lo
 766 */
 767static int hostrange_empty(hostrange_t hr)
 768{
 769    assert(hr != NULL);
 770    return ((hr->hi < hr->lo) || (hr->hi == (unsigned long) -1));
 771}
 772
 773/* return the string representation of the last host in hostrange hr
 774 * and remove that host from the range (i.e. decrement hi if possible)
 775 *
 776 * Returns NULL if malloc fails OR there are no more hosts left
 777 */
 778static char *hostrange_pop(hostrange_t hr)
 779{
 780    size_t size = 0;
 781    char *host = NULL;
 782
 783    assert(hr != NULL);
 784
 785    if (hr->singlehost) {
 786        hr->lo++;    /* effectively set count == 0 */
 787        host = strdup(hr->prefix);
 788    } else if (hostrange_count(hr) > 0) {
 789        size = strlen(hr->prefix) + hr->width + 16;    
 790        if (!(host = (char *) malloc(size * sizeof(char))))
 791            out_of_memory("hostrange pop");
 792        snprintf(host, size, "%s%0*lu", hr->prefix, 
 793             hr->width, hr->hi--);
 794    }
 795
 796    return host;
 797}
 798
 799/* Same as hostrange_pop(), but remove host from start of range */
 800static char *hostrange_shift(hostrange_t hr)
 801{
 802    size_t size = 0;
 803    char *host = NULL;
 804
 805    assert(hr != NULL);
 806
 807    if (hr->singlehost) {
 808        hr->lo++;
 809        if (!(host = strdup(hr->prefix)))
 810            out_of_memory("hostrange shift");
 811    } else if (hostrange_count(hr) > 0) {
 812        size = strlen(hr->prefix) + hr->width + 16;
 813        if (!(host = (char *) malloc(size * sizeof(char))))
 814            out_of_memory("hostrange shift");
 815        snprintf(host, size, "%s%0*lu", hr->prefix,
 816             hr->width, hr->lo++);
 817    }
 818
 819    return host;
 820}
 821
 822
 823/* join two hostrange objects.
 824 *
 825 * returns:
 826 *
 827 * -1 if ranges do not overlap (including incompatible zero padding)
 828 *  0 if ranges join perfectly
 829 * >0 number of hosts that were duplicated in  h1 and h2 
 830 *
 831 * h2 will be coalesced into h1 if rc >= 0
 832 *
 833 * it is assumed that h1->lo <= h2->lo, i.e. hr1 <= hr2
 834 *
 835 */
 836static int hostrange_join(hostrange_t h1, hostrange_t h2)
 837{
 838    int duplicated = -1;
 839
 840    assert(h1 != NULL);
 841    assert(h2 != NULL);
 842    assert(hostrange_cmp(h1, h2) <= 0);
 843
 844    if (hostrange_prefix_cmp(h1, h2) == 0 &&
 845        hostrange_width_combine(h1, h2)) {
 846
 847        if (h1->singlehost && h2->singlehost) {    /* matching singlets  */
 848            duplicated = 1;
 849        } else if (h1->hi == h2->lo - 1) {    /* perfect join       */
 850            h1->hi = h2->hi;
 851            duplicated = 0;
 852        } else if (h1->hi >= h2->lo) {    /* some duplication   */
 853            if (h1->hi < h2->hi) {
 854                duplicated = h1->hi - h2->lo + 1;
 855                h1->hi = h2->hi;
 856            } else
 857                duplicated = hostrange_count(h2);
 858        }
 859    }
 860
 861    return duplicated;
 862}
 863
 864/* hostrange intersect returns the intersection (common hosts)
 865 * of hostrange objects h1 and h2. If there is no intersection,
 866 * NULL is returned.
 867 *
 868 * It is assumed that h1 <= h2 (i.e. h1->lo <= h2->lo)
 869 */
 870static hostrange_t hostrange_intersect(hostrange_t h1, hostrange_t h2)
 871{
 872    hostrange_t new = NULL;
 873
 874    assert(h1 != NULL);
 875    assert(h2 != NULL);
 876
 877    if (h1->singlehost || h2->singlehost)
 878        return NULL;
 879
 880    assert(hostrange_cmp(h1, h2) <= 0);
 881
 882    if ((hostrange_prefix_cmp(h1, h2) == 0)
 883        && (h1->hi > h2->lo) 
 884        && (hostrange_width_combine(h1, h2))) {
 885
 886        if (!(new = hostrange_copy(h1)))
 887            return NULL;
 888        new->lo = h2->lo;
 889        new->hi = h2->hi < h1->hi ? h2->hi : h1->hi;
 890    }
 891
 892    return new;
 893}
 894
 895/* return offset of hn if it is in the hostlist or
 896 *        -1 if not.
 897 */
 898static int hostrange_hn_within(hostrange_t hr, hostname_t hn)
 899{
 900    int len_hr;
 901    int len_hn;
 902    int width;
 903
 904    if (hr->singlehost) {
 905        /*
 906         *  If the current hostrange [hr] is a `singlehost' (no valid
 907         *   numeric suffix (lo and hi)), then the hostrange [hr]
 908         *   stores just one host with name == hr->prefix.
 909         *
 910         *  Thus the full hostname in [hn] must match hr->prefix, in
 911         *   which case we return true. Otherwise, there is no
 912         *   possibility that [hn] matches [hr].
 913         */
 914        if (strcmp (hn->hostname, hr->prefix) == 0)
 915            return 0;
 916        else
 917            return -1;
 918    }
 919
 920    /*
 921     *  Now we know [hr] is not a "singlehost", so hostname
 922     *   better have a valid numeric suffix, or there is no
 923     *   way we can match
 924     */
 925    if (!hostname_suffix_is_valid (hn))
 926        return -1;
 927
 928    len_hn = strlen (hn->prefix);
 929
 930    /*
 931     *  If hostrange and hostname prefixes don't match to at least
 932     *   the length of the hostname object (which will have the min
 933     *   possible prefix length), then there is no way the hostname
 934     *   falls within the range [hr].
 935     */
 936    if (strncmp (hr->prefix, hn->prefix, len_hn) != 0)
 937        return -1;
 938
 939    /*
 940     *  Now we know hostrange and hostname prefixes match up to the
 941     *   length of the hostname prefix.  If the hostrange and hostname
 942     *   prefix lengths do not match (specifically if the hostname prefix
 943     *   length is less than the hostrange prefix length) and the
 944     *   hostrange prefix contains trailing digits, then it might be
 945     *   the case that the hostrange was created by forcing the prefix
 946     *   to contain digits a la f00[1-2]. So we try adjusting the
 947     *   hostname with the longer prefix and calling this function
 948     *   again with the new hostname. (Yes, this is ugly, sorry)
 949     */
 950    len_hr = strlen (hr->prefix);
 951    width = hostname_suffix_width (hn);
 952
 953    if ((len_hn < len_hr)
 954         && (width > 1)
 955         && (isdigit (hr->prefix [len_hr - 1]))
 956         && (hr->prefix [len_hn] == hn->suffix[0]) ) {
 957        int rc;
 958        /*
 959         *  Create new hostname object with its prefix offset by one
 960         */
 961        hostname_t h = hostname_create_with_suffix (hn->hostname, len_hn);
 962        /*
 963         *  Recursive call :-o
 964         */
 965        rc = hostrange_hn_within (hr, h);
 966        hostname_destroy (h);
 967        return rc;
 968    }
 969
 970
 971    /*
 972     *  Finally, check whether [hn], with a valid numeric suffix,
 973     *   falls within the range of [hr].
 974     */
 975    if (hn->num <= hr->hi && hn->num >= hr->lo) {
 976        int width = hostname_suffix_width (hn);
 977        if (!_width_equiv(hr->lo, &hr->width, hn->num, &width))
 978            return -1;
 979        return (hn->num - hr->lo);
 980    }
 981
 982    return -1;
 983}
 984
 985
 986/* copy a string representation of the hostrange hr into buffer buf,
 987 * writing at most n chars including NUL termination
 988 */
 989static size_t
 990hostrange_to_string(hostrange_t hr, size_t n, char *buf, char *separator)
 991{
 992    unsigned long i;
 993    int truncated = 0;
 994    int len = 0;
 995    char sep = separator == NULL ? ',' : separator[0];
 996
 997    if (n == 0)
 998        return 0;
 999
1000    if (hr->singlehost)
1001        return snprintf(buf, n, "%s", hr->prefix);
1002
1003    for (i = hr->lo; i <= hr->hi; i++) {
1004        size_t m = (n - len) <= n ? n - len : 0; /* check for < 0 */
1005        int ret = snprintf(buf + len, m, "%s%0*lu",
1006                   hr->prefix, hr->width, i);
1007        if (ret < 0 || ret >= m) {
1008            len = n;
1009            truncated = 1;
1010            break;
1011        }
1012        len+=ret;
1013        buf[len++] = sep;
1014    }
1015
1016    if (truncated) {
1017        buf[n-1] = '\0';
1018        return -1;
1019    } else {
1020        /* back up over final separator */
1021        buf[--len] = '\0';
1022        return len;
1023    }
1024}
1025
1026/* Place the string representation of the numeric part of hostrange into buf
1027 * writing at most n chars including NUL termination.
1028 */
1029static size_t hostrange_numstr(hostrange_t hr, size_t n, char *buf)
1030{
1031    int len = 0;
1032
1033    assert(buf != NULL);
1034
1035    if (hr->singlehost || n == 0)
1036        return 0;
1037
1038    len = snprintf(buf, n, "%0*lu", hr->width, hr->lo);
1039
1040    if ((len >= 0) && (len < n) && (hr->lo < hr->hi)) {
1041        int len2 = snprintf(buf+len, n-len, "-%0*lu", hr->width, hr->hi);
1042        if (len2 < 0) 
1043            len = -1;
1044        else
1045            len += len2;
1046    }
1047
1048    return len;
1049}
1050
1051
1052/* ----[ hostlist functions ]---- */
1053
1054/* Create a new hostlist object. 
1055 * Returns an empty hostlist, or NULL if memory allocation fails.
1056 */
1057static hostlist_t hostlist_new(void)
1058{
1059    int i;
1060    hostlist_t new = (hostlist_t) malloc(sizeof(*new));
1061    if (!new)
1062        goto fail1;
1063
1064    assert(new->magic = HOSTLIST_MAGIC);
1065    mutex_init(&new->mutex);
1066
1067    new->hr = (hostrange_t *) malloc(HOSTLIST_CHUNK * sizeof(hostrange_t));
1068    if (!new->hr)
1069        goto fail2;
1070
1071    /* set entries in hostrange array to NULL */
1072    for (i = 0; i < HOSTLIST_CHUNK; i++)
1073        new->hr[i] = NULL;
1074
1075    new->size = HOSTLIST_CHUNK;
1076    new->nranges = 0;
1077    new->nhosts = 0;
1078    new->ilist = NULL;
1079    return new;
1080
1081  fail2:
1082    free(new);
1083  fail1:
1084    out_of_memory("hostlist_create");
1085}
1086
1087
1088/* Resize the internal array used to store the list of hostrange objects.
1089 *
1090 * returns 1 for a successful resize,
1091 *         0 if call to _realloc fails    
1092 *
1093 * It is assumed that the caller has the hostlist hl locked 
1094 */
1095static int hostlist_resize(hostlist_t hl, size_t newsize)
1096{
1097    int i;
1098    size_t oldsize;
1099    assert(hl != NULL);
1100    assert(hl->magic == HOSTLIST_MAGIC);
1101    oldsize = hl->size;
1102    hl->size = newsize;
1103    hl->hr = realloc((void *) hl->hr, hl->size*sizeof(hostrange_t));
1104    if (!(hl->hr)) 
1105        return 0;
1106
1107    for (i = oldsize; i < newsize; i++)
1108        hl->hr[i] = NULL;
1109
1110    return 1;
1111}
1112
1113/* Resize hostlist by one HOSTLIST_CHUNK
1114 * Assumes that hostlist hl is locked by caller
1115 */
1116static int hostlist_expand(hostlist_t hl)
1117{
1118    if (!hostlist_resize(hl, hl->size + HOSTLIST_CHUNK))
1119        return 0;
1120    else
1121        return 1;
1122}
1123
1124/* Push a hostrange object onto hostlist hl
1125 * Returns the number of hosts successfully pushed onto hl
1126 * or -1 if there was an error allocating memory
1127 */
1128static int hostlist_push_range(hostlist_t hl, hostrange_t hr)
1129{
1130    hostrange_t tail;
1131    int retval;
1132
1133    assert(hr != NULL);
1134    LOCK_HOSTLIST(hl);
1135
1136    tail = (hl->nranges > 0) ? hl->hr[hl->nranges-1] : hl->hr[0];
1137
1138    if (hl->size == hl->nranges && !hostlist_expand(hl))
1139        goto error;
1140
1141    if (hl->nranges > 0
1142        && hostrange_prefix_cmp(tail, hr) == 0
1143        && tail->hi == hr->lo - 1
1144        && hostrange_width_combine(tail, hr)) {
1145        tail->hi = hr->hi;
1146    } else {
1147        if ((hl->hr[hl->nranges++] = hostrange_copy(hr)) == NULL)
1148            goto error;
1149    }
1150
1151    retval = hl->nhosts += hostrange_count(hr);
1152
1153    UNLOCK_HOSTLIST(hl);
1154
1155    return retval;
1156
1157  error:
1158    UNLOCK_HOSTLIST(hl);
1159    return -1;
1160}
1161
1162
1163
1164/* Same as hostlist_push_range() above, but prefix, lo, hi, and width
1165 * are passed as args 
1166 */
1167static int
1168hostlist_push_hr(hostlist_t hl, char *prefix, unsigned long lo,
1169         unsigned long hi, int width)
1170{
1171    hostrange_t hr = hostrange_create(prefix, lo, hi, width);
1172    int retval = hostlist_push_range(hl, hr);
1173    hostrange_destroy(hr);
1174    return retval;
1175}
1176
1177/* Insert a range object hr into position n of the hostlist hl
1178 * Assumes that hl->mutex is already held by calling process
1179 */
1180static int hostlist_insert_range(hostlist_t hl, hostrange_t hr, int n)
1181{
1182    int i;
1183    hostrange_t tmp;
1184    hostlist_iterator_t hli;
1185
1186    assert(hl != NULL);
1187    assert(hl->magic == HOSTLIST_MAGIC);
1188    assert(hr != NULL);
1189
1190    if (n > hl->nranges)
1191        return 0;
1192
1193    if (hl->size == hl->nranges && !hostlist_expand(hl))
1194        return 0;
1195
1196    /* copy new hostrange into slot "n" in array */
1197    tmp = hl->hr[n];
1198    hl->hr[n] = hostrange_copy(hr);
1199
1200    /* push remaining hostrange entries up */
1201    for (i = n + 1; i < hl->nranges + 1; i++) {
1202        hostrange_t last = hl->hr[i];
1203        hl->hr[i] = tmp;
1204        tmp = last;
1205    }
1206    hl->nranges++;
1207
1208    /* adjust hostlist iterators if needed */
1209    for (hli = hl->ilist; hli; hli = hli->next) {
1210        if (hli->idx >= n)
1211            hli->hr = hli->hl->hr[++hli->idx];
1212    }
1213
1214    return 1;
1215}
1216
1217/* Delete the range at position n in the range array
1218 * Assumes the hostlist lock is already held.
1219 */
1220static void hostlist_delete_range(hostlist_t hl, int n)
1221{
1222    int i;
1223    hostrange_t old;
1224
1225    assert(hl != NULL);
1226    assert(hl->magic == HOSTLIST_MAGIC);
1227    assert(n < hl->nranges && n >= 0);
1228
1229    old = hl->hr[n];
1230    for (i = n; i < hl->nranges - 1; i++)
1231        hl->hr[i] = hl->hr[i + 1];
1232    hl->nranges--;
1233    hl->hr[hl->nranges] = NULL;
1234    hostlist_shift_iterators(hl, n, 0, 1);
1235
1236    /* XXX caller responsible for adjusting nhosts */
1237    /* hl->nhosts -= hostrange_count(old) */
1238
1239    hostrange_destroy(old);
1240}
1241
1242#if WANT_RECKLESS_HOSTRANGE_EXPANSION
1243
1244/* The reckless hostrange expansion function.
1245 * See comment in hostlist.h:hostlist_create() for more info on
1246 * the different choices for hostlist notation.
1247 */
1248hostlist_t _hostlist_create(const char *hostlist, char *sep, char *r_op)
1249{
1250    char *str, *orig;
1251    char *tok, *cur;
1252    int high, low, fmt = 0;
1253    char prefix[256] = "";
1254    int pos = 0;
1255    int error = 0;
1256    char range_op = r_op[0];/* XXX support > 1 char range ops in future? */
1257
1258    hostlist_t new = hostlist_new();
1259
1260    orig = str = strdup(hostlist);
1261
1262    /* return an empty list if an empty string was passed in */
1263    if (str == NULL || strlen(str) == 0)
1264        goto done;
1265
1266    /* Use hostlist_create_bracketed if we see "[" */
1267    if (strchr(str, '[') != NULL)
1268        return _hostlist_create_bracketed(hostlist, sep, r_op);
1269
1270    while ((tok = _next_tok(sep, &str)) != NULL) {
1271
1272        /* save the current string for error messages */
1273        cur = tok;
1274
1275        high = low = 0;
1276
1277        /* find end of alpha part 
1278         *   do this by finding last occurence of range_op in str */
1279        pos = strlen(tok) - 1;
1280        if (strstr(tok, r_op) != '\0') {
1281            while (pos >= 0 && (char) tok[pos] != range_op) 
1282                pos--;
1283        }
1284
1285        /* now back up past any digits */
1286        while (pos >= 0 && isdigit((char) tok[--pos])) {;}
1287
1288        /* Check for valid x-y range (x must be a digit) 
1289         *   Reset pos if the range is not valid         */
1290        if (!isdigit((char) tok[++pos]))
1291            pos = strlen(tok) - 1;
1292
1293        /* create prefix string 
1294         * if prefix will be zero length, but prefix already exists
1295         * use the previous prefix and fmt
1296         */
1297        if ((pos > 0) || (prefix[0] == '\0')) {
1298            memcpy(prefix, tok, (size_t) pos * sizeof(char));
1299            prefix[pos] = '\0';
1300
1301            /* push pointer past prefix */
1302            tok += pos;
1303
1304            /* count number of digits for ouput fmt */
1305            for (fmt = 0; isdigit(tok[fmt]); ++fmt) {;}
1306
1307            if (fmt == 0)
1308                error = 1;
1309
1310        } else
1311            tok += pos;
1312
1313        /* get lower bound */
1314        low = strtoul(tok, (char **) &tok, 10);
1315
1316        if (*tok == range_op) {    /* now get range upper bound */
1317            /* push pointer past range op */
1318            ++tok;
1319
1320            /* find length of alpha part */
1321            for (pos = 0; tok[pos] && !isdigit(tok[pos]); ++pos) {;}
1322
1323            /* alpha part must match prefix or error
1324             * this could mean we've got something like "rtr1-a2"
1325             * so just record an error
1326             */
1327            if (pos > 0) {
1328                if (pos != strlen(prefix) ||
1329                    strncmp(prefix, tok, pos) != 0)
1330                    error = 1;
1331            }
1332
1333            if (*tok != '\0')
1334                tok += pos;
1335
1336            /* make sure we have digits to the end */
1337            for (pos = 0; tok[pos] && isdigit((char) tok[pos]); ++pos) {;}
1338
1339            if (pos > 0) {    /* we have digits to process */
1340                high = strtoul(tok, (char **) &tok, 10);
1341            } else {    /* bad boy, no digits */
1342                error = 1;
1343            }
1344
1345            if ((low > high) || (high - low > MAX_RANGE))
1346                error = 1;
1347
1348        } else {    /* single value */
1349            high = 0;    /* special case, ugh. */
1350        }
1351
1352        /* error if: 
1353         * 1. we are not at end of string
1354         * 2. upper bound equals lower bound
1355         */
1356        if (*tok != '\0' || high == low)
1357            error = 1;
1358
1359        if (error) {    /* assume this is not a range on any error */
1360            hostlist_push_host(new, cur);
1361        } else {
1362            if (high < low)
1363                high = low;
1364            hostlist_push_hr(new, prefix, low, high, fmt);
1365        }
1366
1367        error = 0;
1368    }
1369
1370  done:
1371    free(orig);
1372
1373    return new;
1374}
1375
1376#else                /* !WANT_RECKLESS_HOSTRANGE_EXPANSION */
1377
1378hostlist_t _hostlist_create(const char *hostlist, char *sep, char *r_op) 
1379{
1380    return _hostlist_create_bracketed(hostlist, sep, r_op);
1381}
1382
1383#endif                /* WANT_RECKLESS_HOSTRANGE_EXPANSION */
1384
1385struct _range {
1386    unsigned long lo, hi;
1387    int width;
1388};
1389
1390/* Grab a single range from str 
1391 * returns 1 if str contained a valid number or range,
1392 *         0 if conversion of str to a range failed.
1393 */
1394static int _parse_single_range(const char *str, struct _range *range)
1395{
1396    char *p, *q;
1397    char *orig = strdup(str);
1398    if (!orig) 
1399        seterrno_ret(ENOMEM, 0);
1400
1401    if ((p = strchr(str, '-'))) {
1402        *p++ = '\0';
1403        if (*p == '-')     /* do NOT allow negative numbers */
1404            goto error;
1405    }
1406    range->lo = strtoul(str, &q, 10);
1407    if (q == str) 
1408        goto error;
1409
1410    range->hi = (p && *p) ? strtoul(p, &q, 10) : range->lo;
1411
1412    if (q == p || *q != '\0') 
1413        goto error;
1414
1415    if (range->lo > range->hi) 
1416        goto error;
1417
1418    if (range->hi - range->lo + 1 > MAX_RANGE ) {
1419        _error(__FILE__, __LINE__, "Too many hosts in range `%s'", orig);
1420        free(orig);
1421        seterrno_ret(ERANGE, 0);
1422    }
1423
1424    free(orig);
1425    range->width = strlen(str);
1426    return 1;
1427
1428  error:
1429    _error(__FILE__, __LINE__, "Invalid range: `%s'", orig);
1430    free(orig);
1431    seterrno_ret(EINVAL, 0);
1432}
1433
1434
1435/*
1436 * Convert 'str' containing comma separated digits and ranges into an array
1437 *  of struct _range types (max 'len' elements).  
1438 *
1439 * Return number of ranges created, or -1 on error.
1440 */
1441static int _parse_range_list(char *str, struct _range *ranges, int len)
1442{
1443    char *p;
1444    int count = 0;
1445
1446    while (str) {
1447        if (count == len)
1448            return -1;
1449        if ((p = strchr(str, ',')))
1450            *p++ = '\0';
1451        if (!_parse_single_range(str, &ranges[count++])) 
1452            return -1;  
1453        str = p;
1454    }
1455    return count;
1456}
1457
1458static void
1459_push_range_list(hostlist_t hl, char *pfx, struct _range *rng,
1460             int n)
1461{
1462    int i;
1463    for (i = 0; i < n; i++) {
1464        hostlist_push_hr(hl, pfx, rng->lo, rng->hi, rng->width);
1465        rng++;
1466    }
1467}
1468
1469static void
1470_push_range_list_with_suffix(hostlist_t hl, char *pfx, char *sfx, 
1471                             struct _range *rng, int n)
1472{
1473    int i;
1474    unsigned long j;
1475    for (i = 0; i < n; i++) {
1476        for (j = rng->lo; j <= rng->hi; j++) {
1477            char host[4096];
1478            hostrange_t hr;
1479            snprintf (host, 4096, "%s%0*lu%s", pfx, rng->width, j, sfx);
1480            hr = hostrange_create_single (host);
1481            hostlist_push_range (hl, hr);
1482            /*
1483             * hr is copied in hostlist_push_range. Need to free here.
1484             */
1485            hostrange_destroy (hr);
1486        }
1487        rng++;
1488    }
1489}
1490
1491/*
1492 * Create a hostlist from a string with brackets '[' ']' to aid 
1493 * detection of ranges and compressed lists
1494 */
1495static hostlist_t 
1496_hostlist_create_bracketed(const char *hostlist, char *sep, char *r_op)
1497{
1498    hostlist_t new = hostlist_new();
1499    struct _range ranges[MAX_RANGES];
1500    int nr, err;
1501    char *p, *tok, *str, *orig;
1502    char cur_tok[1024];
1503
1504    if (hostlist == NULL)
1505        return new;
1506
1507    if (!(orig = str = strdup(hostlist))) {
1508        hostlist_destroy(new);
1509        return NULL;
1510    }
1511
1512    while ((tok = _next_tok(sep, &str)) != NULL) {
1513        strncpy(cur_tok, tok, 1024);
1514
1515        if ((p = strchr(tok, '[')) != NULL) {
1516            char *q, *prefix = tok;
1517            *p++ = '\0';
1518
1519            if ((q = strchr(p, ']'))) {
1520                *q = '\0';
1521                nr = _parse_range_list(p, ranges, MAX_RANGES);
1522                if (nr < 0) 
1523                    goto error;
1524
1525                if (*(++q) != '\0')
1526                    _push_range_list_with_suffix (new, prefix, q, ranges, nr);
1527                else
1528                    _push_range_list(new, prefix, ranges, nr);
1529
1530                
1531            } else
1532                hostlist_push_host(new, cur_tok);
1533
1534        } else
1535            hostlist_push_host(new, cur_tok);
1536    }
1537
1538    free(orig);
1539    return new;
1540
1541  error:
1542    err = errno;
1543    hostlist_destroy(new);
1544    free(orig);
1545    seterrno_ret(err, NULL);
1546}
1547
1548
1549
1550hostlist_t hostlist_create(const char *str)
1551{
1552    return _hostlist_create(str, "\t, ", "-");
1553}
1554
1555
1556hostlist_t hostlist_copy(const hostlist_t hl)
1557{
1558    int i;
1559    hostlist_t new;
1560
1561    if (hl == NULL)
1562        return NULL;
1563
1564    LOCK_HOSTLIST(hl);
1565    if (!(new = hostlist_new()))
1566        goto done;
1567
1568    new->nranges = hl->nranges;
1569    new->nhosts = hl->nhosts;
1570    if (new->nranges > new->size)
1571        hostlist_resize(new, new->nranges);
1572
1573    for (i = 0; i < hl->nranges; i++)
1574        new->hr[i] = hostrange_copy(hl->hr[i]);
1575
1576  done:
1577    UNLOCK_HOSTLIST(hl);
1578    return new;
1579}
1580
1581
1582void hostlist_destroy(hostlist_t hl)
1583{
1584    int i;
1585    if (hl == NULL)
1586        return;
1587    LOCK_HOSTLIST(hl);
1588    while (hl->ilist) {
1589        mutex_unlock(&hl->mutex);
1590        hostlist_iterator_destroy(hl->ilist);
1591        mutex_lock(&hl->mutex);
1592    }
1593    for (i = 0; i < hl->nranges; i++)
1594        hostrange_destroy(hl->hr[i]);
1595    free(hl->hr);
1596    assert(hl->magic = 0x1);
1597    UNLOCK_HOSTLIST(hl);
1598    mutex_destroy(&hl->mutex);
1599    free(hl);
1600}
1601
1602
1603int hostlist_push(hostlist_t hl, const char *hosts)
1604{
1605    hostlist_t new;
1606    int retval;
1607    if (hosts == NULL)
1608        return 0;
1609    new = hostlist_create(hosts);
1610    if (!new)
1611        return 0;
1612    mutex_lock(&new->mutex);
1613    retval = new->nhosts;
1614    mutex_unlock(&new->mutex);
1615    hostlist_push_list(hl, new);
1616    hostlist_destroy(new);
1617    return retval;
1618}
1619
1620int hostlist_push_host(hostlist_t hl, const char *str)
1621{
1622    hostrange_t hr;
1623    hostname_t hn;
1624
1625    if (str == NULL)
1626        return 0;
1627
1628    hn = hostname_create(str);
1629
1630    if (hostname_suffix_is_valid(hn)) {
1631        hr = hostrange_create(hn->prefix, hn->num, hn->num,
1632                      hostname_suffix_width(hn));
1633    } else
1634        hr = hostrange_create_single(str);
1635
1636    hostlist_push_range(hl, hr);
1637
1638    hostrange_destroy(hr);
1639    hostname_destroy(hn);
1640
1641    return 1;
1642}
1643
1644int hostlist_push_list(hostlist_t h1, hostlist_t h2)
1645{
1646    int i, n = 0;
1647
1648    if (h2 == NULL)
1649        return 0;
1650
1651    LOCK_HOSTLIST(h2);
1652
1653    for (i = 0; i < h2->nranges; i++)
1654        n += hostlist_push_range(h1, h2->hr[i]);
1655
1656    UNLOCK_HOSTLIST(h2);
1657
1658    return n;
1659}
1660
1661
1662char *hostlist_pop(hostlist_t hl)
1663{
1664    char *host = NULL;
1665
1666    LOCK_HOSTLIST(hl);
1667    if (hl->nhosts > 0) {
1668        hostrange_t hr = hl->hr[hl->nranges - 1];
1669        host = hostrange_pop(hr);
1670        hl->nhosts--;
1671        if (hostrange_empty(hr)) {
1672            hostrange_destroy(hl->hr[--hl->nranges]);
1673            hl->hr[hl->nranges] = NULL;
1674        }
1675    }
1676    UNLOCK_HOSTLIST(hl);
1677    return host;
1678}
1679
1680/* find all iterators affected by a shift (or deletion) at 
1681 * hl->hr[idx], depth, with the deletion of n ranges */
1682static void
1683hostlist_shift_iterators(hostlist_t hl, int idx, int depth, int n)
1684{
1685    hostlist_iterator_t i;
1686    for (i = hl->ilist; i; i = i->next) {
1687        if (n == 0) {
1688            if (i->idx == idx && i->depth >= depth)
1689                i->depth = i->depth > -1 ? i->depth - 1 : -1;
1690        } else {
1691            if (i->idx >= idx) {
1692                if ((i->idx -= n) >= 0)
1693                    i->hr = i->hl->hr[i->idx];
1694                else
1695                    hostlist_iterator_reset(i);
1696            }
1697        }
1698    }
1699}
1700
1701char *hostlist_shift(hostlist_t hl)
1702{
1703    char *host = NULL;
1704
1705    LOCK_HOSTLIST(hl);
1706
1707    if (hl->nhosts > 0) {
1708        hostrange_t hr = hl->hr[0];
1709
1710        host = hostrange_shift(hr);
1711        hl->nhosts--;
1712
1713        if (hostrange_empty(hr)) {
1714            hostlist_delete_range(hl, 0);
1715            /* hl->nranges--; */
1716        } else
1717            hostlist_shift_iterators(hl, 0, 0, 0);
1718    }
1719
1720    UNLOCK_HOSTLIST(hl);
1721
1722    return host;
1723}
1724
1725
1726char *hostlist_pop_range(hostlist_t hl)
1727{
1728    int i;
1729    char buf[MAXHOSTRANGELEN + 1];
1730    hostlist_t hltmp;
1731    hostrange_t tail;
1732
1733    LOCK_HOSTLIST(hl);
1734    if (hl->nranges < 1 || !(hltmp = hostlist_new())) {
1735        UNLOCK_HOSTLIST(hl);
1736        return NULL;
1737    }
1738
1739    i = hl->nranges - 2;
1740    tail = hl->hr[hl->nranges - 1];
1741    while (i >= 0 && hostrange_within_range(tail, hl->hr[i]))
1742        i--;
1743
1744    for (i++; i < hl->nranges; i++) {
1745        hostlist_push_range(hltmp, hl->hr[i]);
1746        hostrange_destroy(hl->hr[i]);
1747        hl->hr[i] = NULL;
1748    }
1749    hl->nhosts -= hltmp->nhosts;
1750    hl->nranges -= hltmp->nranges;
1751
1752    UNLOCK_HOSTLIST(hl);
1753    hostlist_ranged_string(hltmp, MAXHOSTRANGELEN, buf);
1754    hostlist_destroy(hltmp);
1755    return strdup(buf);
1756}
1757
1758
1759char *hostlist_shift_range(hostlist_t hl)
1760{
1761    int i;
1762    char buf[1024];
1763    hostlist_t hltmp = hostlist_new();
1764    if (!hltmp)
1765        return NULL;
1766
1767    LOCK_HOSTLIST(hl);
1768
1769    if (hl->nranges == 0) {
1770        hostlist_destroy(hltmp);
1771        UNLOCK_HOSTLIST(hl);
1772        return NULL;
1773    }
1774
1775    i = 0;
1776    do {
1777        hostlist_push_range(hltmp, hl->hr[i]);
1778        hostrange_destroy(hl->hr[i]);
1779    } while ( (++i < hl->nranges) 
1780            && hostrange_within_range(hltmp->hr[0], hl->hr[i]) );
1781
1782    hostlist_shift_iterators(hl, i, 0, hltmp->nranges);
1783
1784    /* shift rest of ranges back in hl */
1785    for (; i < hl->nranges; i++) {
1786        hl->hr[i - hltmp->nranges] = hl->hr[i];
1787        hl->hr[i] = NULL;
1788    }
1789    hl->nhosts -= hltmp->nhosts;
1790    hl->nranges -= hltmp->nranges;
1791
1792    UNLOCK_HOSTLIST(hl);
1793
1794    hostlist_ranged_string(hltmp, 1024, buf);
1795    hostlist_destroy(hltmp);
1796
1797    return strdup(buf);
1798}
1799
1800/* XXX: Note: efficiency improvements needed */
1801int hostlist_delete(hostlist_t hl, const char *hosts)
1802{
1803    int n = 0;
1804    char *hostname = NULL;
1805    hostlist_t hltmp;
1806
1807    if (!(hltmp = hostlist_create(hosts)))
1808        seterrno_ret(EINVAL, 0);
1809
1810    while ((hostname = hostlist_pop(hltmp)) != NULL) {
1811        n += hostlist_delete_host(hl, hostname);
1812        free(hostname);
1813    }
1814    hostlist_destroy(hltmp);
1815
1816    return n;
1817}
1818
1819
1820/* XXX watch out! poor implementation follows! (fix it at some point) */
1821int hostlist_delete_host(hostlist_t hl, const char *hostname)
1822{
1823    int n = hostlist_find(hl, hostname);
1824    if (n >= 0)
1825        hostlist_delete_nth(hl, n);
1826    return n >= 0 ? 1 : 0;
1827}
1828
1829
1830static char *
1831_hostrange_string(hostrange_t hr, int depth)
1832{
1833    char buf[MAXHOSTNAMELEN + 16];
1834    int  len = snprintf(buf, MAXHOSTNAMELEN + 15, "%s", hr->prefix);
1835
1836    if (!hr->singlehost)
1837        snprintf(buf+len, MAXHOSTNAMELEN+15 - len, "%0*lu", 
1838                

Large files files are truncated, but you can click here to view the full file