Esempio n. 1
0
/*
 * Remove the job indexed by ikey from the jobs queue.
 * Removes job structure and flags removal to underlying runq class.
 * No further work should be carried out for the job identified by key
 * but runq may take a while to catch up if there are any proccesses 
 * involved.
 * Returns 1=success, 0=error.
 */
int job_rm(int ikey /* job key */) {
     struct job_work *w;
     
     /* Find job details */
     if ((w = itree_find(job_tab, ikey)) == ITREE_NOVAL) {
          elog_printf(ERROR, "job not found (id %d)", ikey);
	  return 0;
     }

     /* Debug: Notify the removal from the job list */
     elog_printf(DEBUG, "remove job %s", w->runarg->key);

     /* Remove runq work instruction */
     if (!runq_rm(w->runq)) {
          elog_printf(DEBUG, "job %s can't remove runq (id %d)", 
		      w->runarg->key, w->runq);
	  return 0;
     }

     itree_rm(job_tab);			/* Remove job record */

     /* Free job storage excluding w->runarg which was reparented by runq */
     nfree(w->origin);
     nfree(w);

     return(1);
}
Esempio n. 2
0
/* get the name of a user by uid, caching results in the ITREE */
char *plinps_getuser(int uid, ITREE *uidtoname)
{
     char *name;
     struct passwd *pwent;

     /* return name if in table */
     name = itree_find(uidtoname, uid);
     if (name != ITREE_NOVAL)
	  return name;

     /* fetch pw entry and load name into table */
     pwent = getpwuid(uid);
     if (pwent)
	  itree_add(uidtoname, uid, xnstrdup(pwent->pw_name));
     else
	  itree_add(uidtoname, uid, xnstrdup("unknown"));

     return itree_find(uidtoname, uid);
}
Esempio n. 3
0
uint8_t topology_distance(uint32_t ip1,uint32_t ip2) {
	uint32_t rid1,rid2;
	if (ip1==ip2) {
		return 0;
	}
//	rid1 = hash_find(ip1);
//	rid2 = hash_find(ip2);
	rid1 = itree_find(racktree,ip1);
	rid2 = itree_find(racktree,ip2);
//	rid1^=rid2;
//	rid2=0;
//	while(rid1) {
//		if (rid1&1) {
//			rid2++;
//		}
//		rid1>>=1;
//	}
//	return rid2;
	return (rid1==rid2)?1:2;
}
Esempio n. 4
0
/*
 * Linux specific routines
 */
void pmacnet_collect(TABLE tab) {
     char *data;
     ITREE *lines;

     /* open and process the network stats file */
     data = probe_readfile("/proc/net/dev");
     if (data) {
	  /*elog_printf(DEBUG, "Read from /proc/net/dev: %s", data);*/
       util_scantext(data, ": |", UTIL_MULTISEP, &lines);	/* scan */
	  itree_find(lines, 2);			/* data starts at 3rd line */
	  while(!itree_isbeyondend(lines)) {
	       table_addemptyrow(tab);
	       pmacnet_col_netdev(tab, itree_get(lines) );
	       itree_next(lines);
	  }

	  /* clear up */
	  util_scanfree(lines);
	  table_freeondestroy(tab, data);
     }
}
Esempio n. 5
0
/* takes data from /procs's status structure into the table */
void plinps_col_status(TABLE tab, char *ps, ITREE *uidtoname)
{
     ITREE *lines, *cols;
     char *key, *val;
     int seen_vmdata=0, seen_vmlib=0, seen_vmexe=0;

     /* Format is similar to:-
      *    Name:   bash
      *    State:  R (running)
      *    SleepAVG:       100%
      *    Tgid:   4916
      *    Pid:    4916
      *    PPid:   4903
      *    TracerPid:      0
      *    Uid:    501     501     501     501
      *    Gid:    501     501     501     501
      *    FDSize: 256
      *    Groups: 501
      *    VmSize:     3120 kB
      *    VmLck:         0 kB
      *    VmRSS:      1852 kB
      *    VmData:      952 kB
      *    VmStk:        24 kB
      *    VmExe:       580 kB
      *    VmLib:      1264 kB
      *    Threads:        1
      *    SigPnd: 0000000000000000
      *    ShdPnd: 0000000000000000
      *    SigBlk: 0000000000010002
      *    SigIgn: 0000000000384004
      *    SigCgt: 000000004b813efb
      *    CapInh: 0000000000000000
      *    CapPrm: 0000000000000000
      *    CapEff: 0000000000000000
      */

     util_scantext(ps, " \t:", UTIL_MULTISEP, &lines);
     itree_traverse(lines) {
	  cols = itree_get(lines);
	  key = itree_find(cols, 0);
	  if (key == ITREE_NOVAL)
	       continue;

	  switch(key[0]) {
	  case 'N':	/* Name */
	       break;
	  case 'S':	/* State, SleepSVG, Sig* */
	       break;
	  case 'T':	/* Tgid, TracerPid, Threads  */
	       break;
	  case 'P':	/* Pid, PPid */
	       break;
	  case 'U':	/* Uid */
	       if (strcmp(key, "Uid") == 0) {
		    /* real uid */
		    itree_next(cols);
		    val = itree_get(cols);
		    table_replacecurrentcell(tab, "uid",    val);
		    table_replacecurrentcell(tab, "pwname", 
			plinps_getuser(strtol(val, NULL, 10), uidtoname));

		    /* effective uid */
		    itree_next(cols);
		    val = itree_get(cols);
		    table_replacecurrentcell(tab, "euid",    val);
		    table_replacecurrentcell(tab, "epwname", 
			plinps_getuser(strtol(val, NULL, 10), uidtoname));
#if 0
		    itree_next(cols);
		    val = itree_get(cols);	/* suid - saved user id */
		    itree_next(cols);
		    val = itree_get(cols);	/* fuid - fs user id */
#endif
	       }
	       break;
	  case 'G':	/* Gid, Groups */
	       if (strcmp(key, "Gid") == 0) {
		    /* real gid */
		    itree_next(cols);
		    val = itree_get(cols);
		    table_replacecurrentcell(tab, "gid",    val);

		    /* effective gid */
		    itree_next(cols);
		    val = itree_get(cols);
		    table_replacecurrentcell(tab, "egid",    val);
#if 0
		    itree_next(cols);
		    val = itree_get(cols);	/* sgid - saved group id */
		    itree_next(cols);
		    val = itree_get(cols);	/* fgid - fs group id */
#endif
	       }
	       break;
	  case 'F':	/* FDSize */
	       break;
	  case 'V':	/* Vm* */
	       if (strcmp(key, "VmData") == 0) {
		    itree_next(cols);
		    val = itree_get(cols);
		    table_replacecurrentcell(tab, "data_size", val);
		    seen_vmdata++;
	       }
	       if (strcmp(key, "VmLib") == 0) {
		    itree_next(cols);
		    val = itree_get(cols);
		    table_replacecurrentcell(tab, "library", val);
		    seen_vmlib++;
	       }
	       if (strcmp(key, "VmExe") == 0) {
		    itree_next(cols);
		    val = itree_get(cols);
		    table_replacecurrentcell(tab, "text_size", val);
		    seen_vmexe++;
	       }
	       break;
	  case 'C':	/* Cap* */
	       break;
	  default:
	       break;
	  }
     }
     util_scanfree(lines);

     if (!seen_vmdata)
	  table_replacecurrentcell_alloc(tab, "data_size", "0");
     if (!seen_vmlib)
	  table_replacecurrentcell_alloc(tab, "library", "0");
     if (!seen_vmexe)
	  table_replacecurrentcell_alloc(tab, "text_size", "0");
}
Esempio n. 6
0
/*
 * Read in job definitions from the pseudo-url and add them to the
 * job class. If there is a problem with a job definition, it will not
 * be added to the job class, but other jobs will continue to be processed.
 *
 * File format: one line per job. magic string of 'job 1' at top
 * Fields/columns match the job_add() spec and are:
 * 1. start time (seconds)
 * 2. interval (currently seconds, but ought to have weekly or monthly based
 *    intervals as well)
 * 3. phase (int; job order at each scheduled point)
 * 4. count (int; how many times to run; 0=indefinately)
 * 5. key (unique job id)
 * 6. origin (char *; no spaces)
 * 7. result (pseudo-url; where to send results)
 * 8. errors (pseudo-url; where to send errors)
 * 9. keep (number of results to keep in timestore or tablestore)
 * 10.method (char *; string representation of method)
 * 11.command (char *; command for method to run)
 *
 * The result url, error url and command are parsed for tokens of the
 * form %x, where x is a single letter. These are expanded into context
 * specific strings. Currently, %h is hostname and %j is jobname. See
 * route_expand() for more details.
 *
 * Returns the number of jobs added, or -1 for a major problem
 */
int job_loadroute(char *purl 	/* p-url location of file */ )
{
     long start, interval, phase, count, keep;
     char *key, *origin, *result, *error, *method, *command;
     char *endint;
     ITREE *jobdefs, *job;
     int r, njobdefs, jobsadded = 0;
     char key_t[60], result_t[256], error_t[256], command_t[1024];

     /* read jobs into parse structure */
     njobdefs = util_parseroute(purl, " \t", "job 1", &jobdefs);
     if (njobdefs < 1)
	  return -1;

     util_parsedump(jobdefs);
     itree_traverse(jobdefs) {
          job = itree_get(jobdefs);

	  /* check the size of each row */
	  if (itree_n(job) != 11) {
	       elog_startprintf(ERROR, "%s row %d has %d fields, want 11 (",
				purl, itree_getkey(jobdefs)+1, itree_n(job) );
	       itree_traverse(job)
		    elog_contprintf(ERROR, "%s ", (char *) itree_get(job));
	       elog_endprintf(ERROR, ")");
	       continue;
	  }

	  /* 
	   * validate each column 
	   * we dont need to strdup() any space because job_add() will
	   * do that for itself
	   */
	  /* column 1: start time */
	  itree_find(job, 0);
	  start = strtol( itree_get(job), &endint, 10 );
	  if (start == LONG_MIN || start == LONG_MAX || 
	      itree_get(job) == endint) {
	       elog_printf(ERROR, "%s row %d start time (column 1) is "
			   "incorrect: '%s'; skipping", 
			   purl, itree_getkey(jobdefs), itree_get(job));
	       continue;
	  }

	  /* column 2: interval */
	  itree_find(job, 1);
	  interval = strtol( itree_get(job), &endint, 10 );
	  if (interval == LONG_MIN || interval == LONG_MAX ||
	      itree_get(job) == endint) {
	       elog_printf(ERROR, "%s row %d interval (column 2) is "
			   "incorrect: '%s'; skipping", 
			   purl, itree_getkey(jobdefs), itree_get(job));
	       continue;
	  }

	  /* column 3: phase */
	  itree_find(job, 2);
	  phase = strtol( itree_get(job), &endint, 10 );
	  if (phase == LONG_MIN || phase == LONG_MAX ||
	      itree_get(job) == endint) {
	       elog_printf(ERROR, "%s row %d phase (column 3) is incorrect: "
			   "'%s'; skipping",
			   purl, itree_getkey(jobdefs), itree_get(job));
	       continue;
	  }

	  /* column 4: count */
	  itree_find(job, 3);
	  count = strtol( itree_get(job), &endint, 10 );
	  if (count == LONG_MIN || count == LONG_MAX ||
	      itree_get(job) == endint) {
	       elog_printf(ERROR, "%s row %d count (column 4) is incorrect: "
			   "'%s'; skipping",  purl, itree_getkey(jobdefs), 
			   itree_get(job));
	       continue;
	  }

	  /* column 5: key */
	  itree_find(job, 4);
	  key = itree_get(job);
	  if (route_expand(key_t, key, key, interval) != -1)
	       key = key_t;

	  /* column 6: origin  */
	  itree_find(job, 5);
	  origin = itree_get(job);

	  /* column 7: result  */
	  itree_find(job, 6);
	  result = itree_get(job);
	  if (route_expand(result_t, result, key, interval) != -1)
	       result = result_t;

	  /* column 8: error  */
	  itree_find(job, 7);
	  error = itree_get(job);
	  if (route_expand(error_t, error, key, interval) != -1)
	       error = error_t;

	  /* column 9: keep */
	  itree_find(job, 8);
	  keep = strtol( itree_get(job), &endint, 10 );
	  if (keep == LONG_MIN || keep == LONG_MAX ||
	      itree_get(job) == endint) {
	       elog_printf(ERROR, "%s row %d keep (column 9) is incorrect: "
			   "'%s'; skipping", purl, itree_getkey(jobdefs), 
			   itree_get(job));
	       continue;
	  }

	  /* column 10: method  */
	  itree_find(job, 9);
	  method = itree_get(job);

	  /* column 11: command  */
	  itree_find(job, 10);
	  command = itree_get(job);
	  if (route_expand(command_t, command, key, interval) != -1)
	       command = command_t;

	  elog_printf(DEBUG, "%s row %d read: start=%ld interval=%ld phase=%ld "
		      "count=%ld key=%s origin=%s result=%s error=%s keep=%ld "
		      "method=%s command=%s", purl, itree_getkey(jobdefs), 
		      start, interval, phase, count, key, origin, result, 
		      error, keep, method, command);

	  if (meth_check(method)) {
	       elog_printf(ERROR, "%s row %d method %s not loaded; skipping",
			    purl, itree_getkey(jobdefs), method);
	       continue;
	  }

	  /* insert into job class */
	  r = job_add(start, interval, phase, count, key, origin, result, 
		      error, keep, method, command);
	  if (r == -1)
	       elog_printf(ERROR, "%s row %d unable to add job; skipping",
			   purl, itree_getkey(jobdefs));
	  else
	       jobsadded++;
     }

     util_freeparse(jobdefs);

     return jobsadded;
}
Esempio n. 7
0
/*
 * Calculate the major and minor x-axis points of a time line, given the
 * line size and viewing scale.
 * Dispdiff is a representation of the displayed scale and should be 
 * ( <upper time> - <lower time> ), giving the number seconds being 
 * displayed at once.
 * Min and max times have an offset added as set by timeline_setoffset(). 
 * Results are returned in an ITREE list, with the key being time_t
 * and the value being a pointer to a struct timeline_tick.
 * The value node indicates the type of tick it is and the text label
 * associated with it.
 * Returns NULL for an error, such as being impossible to work out.
 */
ITREE *timeline_ocalc(time_t min,	/* start of the timeline */
		     time_t max,	/* end of timeline */
		     time_t dispdiff	/* viewing area for scale */ )
{
     int tickindex;			/* index to applicable tick point */
     ITREE *results;			/* returned results */
     time_t t;				/* working time */
     struct tm  t_tm;			/* working time breakdown */
     struct tm *t_tmp;			/* returned time breakdown pointer */
     struct timeline_tick *tick;	/* tick details */
     char label[TIMELINE_SHORTSTR];	/* working label text */

     /* we must have at least 3 seconds to work sensibly */
     if (min +3 >= max || dispdiff < 3)
	  return NULL;

     results = itree_create();

     /* find which tick is applicable */
     for (tickindex = 0; timeline_tickpoints[ tickindex ].threshold; 
			tickindex++)
	  if (dispdiff <= timeline_tickpoints[ tickindex ].threshold)
	       break;

     /* start point (non-tick) */
     t = min + timeline_offset;
     t_tmp = localtime(&t);
     strftime(label, TIMELINE_SHORTSTR, "%d%b%y", t_tmp);
     /* store non-tick! as a result */
     tick = nmalloc(sizeof(struct timeline_tick));
     tick->type  = TIMELINE_NONE;
     tick->label = xnstrdup(label);
     tick->point = min;
     itree_add(results, min, tick);

     /* run off major ticks */
     t = min + timeline_offset;
     while (1) {
	  /* add major tick increment */
	  t    += timeline_tickpoints[ tickindex ].major;
	  t_tmp = localtime(&t);
	  memcpy(&t_tm, t_tmp, sizeof(struct tm));

	  /* round down */
	  timeline_rounddown(&t_tm, timeline_tickpoints[ tickindex ].base);
	  t = mktime(&t_tm);

	  /* loop check */
	  if (t >= max + timeline_offset)
	       break;

	  /* create label text */
	  switch (timeline_tickpoints[ tickindex ].base) {
	  case NOUNIT:
	       strftime(label, TIMELINE_SHORTSTR, "??", &t_tm);
	       break;
	  case SECS:
	       strftime(label, TIMELINE_SHORTSTR, "%H:%M:%S", &t_tm);
	       break;
	  case MINS:
	       strftime(label, TIMELINE_SHORTSTR, "%H:%M", &t_tm);
	       break;
	  case HOURS:
	       strftime(label, TIMELINE_SHORTSTR, "%H:00", &t_tm);
	       break;
	  case WEEKDAYS:
	       strftime(label, TIMELINE_SHORTSTR, "%a", &t_tm);
	       break;
	  case DAYS:
	       strftime(label, TIMELINE_SHORTSTR, "%d%b", &t_tm);
	       break;
	  case MDAYS:
	       strftime(label, TIMELINE_SHORTSTR, "%d", &t_tm);
	       break;
	  case MONTHS:
	       strftime(label, TIMELINE_SHORTSTR, "%b", &t_tm);
	       break;
	  case YEARS:
	       strftime(label, TIMELINE_SHORTSTR, "%Y", &t_tm);
	       break;
	  }

	  /* store tick as a result */
	  tick = nmalloc(sizeof(struct timeline_tick));
	  tick->type  = TIMELINE_MAJOR;
	  tick->label = xnstrdup(label);
	  tick->point = t - timeline_offset;
	  itree_add(results, t - timeline_offset, tick);
     }

     /* run off minor ticks */
     t = min + timeline_offset;
     while (1) {
	  /* add maior tick increment */
	  t    += timeline_tickpoints[ tickindex ].minor;
#if 0
/* suggest that minor ticks do not need the rounding treatment */
	  t_tmp = localtime(&t);
	  memcpy(&t_tm, t_tmp, sizeof(struct tm));

	  /* round down */
	  timeline_rounddown(&t_tm, timeline_tickpoints[ tickindex ].base);
	  t = mktime(&t_tm);
printf("%d\n", t);
#endif
	  /* loop check */
	  if (t >= max + timeline_offset)
	       break;

	  /* store tick as a result if there is not a major tick already */
	  if (itree_find(results, t) == ITREE_NOVAL) {
	       tick = nmalloc(sizeof(struct timeline_tick));
	       tick->type  = TIMELINE_MINOR;
	       tick->label = NULL;
	       tick->point = t - timeline_offset;
	       itree_add(results, t - timeline_offset, tick);
	  }
     }

     /* max point (non-tick) */
     t = max + timeline_offset;
     t_tmp = localtime(&t);
     strftime(label, TIMELINE_SHORTSTR, "%d%b%y", t_tmp);
     /* store non-tick! as a result */
     tick = nmalloc(sizeof(struct timeline_tick));
     tick->type  = TIMELINE_NONE;
     tick->label = xnstrdup(label);
     tick->point = max;
     itree_add(results, max, tick);

     return results;
}
Esempio n. 8
0
/*
 * Calculate the major and minor x-axis points of a time line, given the
 * line size and viewing scale.
 * Dispdiff is a representation of the displayed scale and should be 
 * ( <upper time> - <lower time> ), giving the number seconds being 
 * displayed at once.
 * Min and max times have an offset added as set by timeline_setoffset(). 
 * Results are returned in an ITREE list, with the key being time_t
 * and the value being a pointer to a struct timeline_tick.
 * The value node indicates the type of tick it is and the text label
 * associated with it.
 * Returns NULL for an error, such as being impossible to work out.
 */
ITREE *timeline_calc(time_t min,	/* start of the timeline */
		     time_t max,	/* end of timeline */
		     time_t dispdiff,	/* viewing area for scale */
		     int maxchars	/* max number of chars possible */)
{
     int diff;				/* diff betwix min+max */
     int maxlabels;			/* max number of labels poss */
     int tickindex;			/* index to applicable tick point */
     ITREE *results;			/* returned results */
     time_t t;				/* working time */
     struct tm  t_tm;			/* working time breakdown */
     struct timeline_tick *tick;	/* tick details */
     char label[TIMELINE_SHORTSTR];	/* working label text */

#if 0
     fprintf(stderr, "min %ld (%s), max %ld (%s), diff %ld, maxchars %d\n", 
	     min, util_decdatetime(min+timeline_offset), 
	     max, util_sdecdatetime(max+timeline_offset),
	     dispdiff, maxchars);
#endif

     /* we must have at least 3 seconds to work sensibly */
     diff = max - min;
     if (min +3 >= max || diff < 3)
	  return NULL;

     /* work out the maximum number of labels possible, if the widest
      * is 8 chars + 1 for the major tick + 1 space */
     maxlabels = maxchars / (1+8+1);

     results = itree_create();

     /* find which tick is applicable from the table */
     for (tickindex = 0; timeline_tickpoints[ tickindex ].threshold; 
			tickindex++)
	  if (dispdiff <= timeline_tickpoints[ tickindex ].threshold)
	       break;
#if 0
     fprintf(stderr, "tkidx %d, thresh %d, base %d, maj %d, min %d, desc %s\n",
	     tickindex, timeline_tickpoints[ tickindex ].threshold,
	     timeline_tickpoints[ tickindex ].base,
	     timeline_tickpoints[ tickindex ].major,
	     timeline_tickpoints[ tickindex ].minor,
	     timeline_tickpoints[ tickindex ].description);
#endif

     /* Start point should be the DATE of the min as a non-tick. Store it */
     t = min + timeline_offset;
     localtime_r(&t, &t_tm);
     strftime(label, TIMELINE_SHORTSTR, "%d%b%y", &t_tm);
     tick = nmalloc(sizeof(struct timeline_tick));
     tick->type  = TIMELINE_NONE;
     tick->label = xnstrdup(label);
     tick->point = min;
     tick->ptime = t;
     itree_add(results, tick->point, tick);
#if 0
     fprintf(stderr, "  point %ld (%s), type %d, label %s\n", 
	     tick->point, util_decdatetime(t), 
	     tick->type, tick->label);
#endif

     /* Min and max are the visible limits of the chart. 
      * The counting, however, needs to start from the greatest major 
      * point lower than the min */
     t -= timeline_tickpoints[ tickindex ].major;

     /* establish the first rounded time before looping */
     timeline_rounddown(&t_tm, timeline_tickpoints[ tickindex ].base);
     t = mktime(&t_tm);

     /* run off major ticks */
     while (t < max + timeline_offset) {
	  /* add major tick increment */
	  t += timeline_tickpoints[ tickindex ].major;
	  localtime_r(&t, &t_tm);

	  /* create label text */
	  switch (timeline_tickpoints[ tickindex ].base) {
	  case NOUNIT:
	       strftime(label, TIMELINE_SHORTSTR, "??", &t_tm);
	       break;
	  case SECS:
	       strftime(label, TIMELINE_SHORTSTR, "%H:%M:%S", &t_tm);
	       break;
	  case MINS:
	       strftime(label, TIMELINE_SHORTSTR, "%H:%M", &t_tm);
	       break;
	  case HOURS:
	       strftime(label, TIMELINE_SHORTSTR, "%H:00", &t_tm);
	       break;
	  case WEEKDAYS:
	       strftime(label, TIMELINE_SHORTSTR, "%a", &t_tm);
	       break;
	  case DAYS:
	       strftime(label, TIMELINE_SHORTSTR, "%d%b", &t_tm);
	       break;
	  case MDAYS:
	       strftime(label, TIMELINE_SHORTSTR, "%d", &t_tm);
	       break;
	  case MONTHS:
	       strftime(label, TIMELINE_SHORTSTR, "%b", &t_tm);
	       break;
	  case YEARS:
	       strftime(label, TIMELINE_SHORTSTR, "%Y", &t_tm);
	       break;
	  }

	  /* store tick as a result */
	  tick = nmalloc(sizeof(struct timeline_tick));
	  tick->type  = TIMELINE_MAJOR;
	  tick->label = xnstrdup(label);
	  tick->point = t - timeline_offset;
	  tick->ptime = t;
	  itree_add(results, tick->point, tick); /* really need an itree? */
#if 0
	  fprintf(stderr, "  point %ld (%s), type %d, label %s\n", 
		  tick->point, util_decdatetime(t), 
		  tick->type, tick->label);
#endif
     }

#if 0
     /* DISABLE MINOR TICKS AS GTK_DATABOX DOES NOT SUPPORT IT DIRECTLY */

     /* run off minor ticks */
     t = min + timeline_offset;

     /* As major points, start counting from the greatest minor
      * point lower than the min */
     t -= timeline_tickpoints[ tickindex ].minor;
     localtime_r(&t, &t_tm);

     /* establish the first rounded time before looping */
     timeline_rounddown(&t_tm, timeline_tickpoints[ tickindex ].base);
     t = mktime(&t_tm);

     while (t < max + timeline_offset) {
	  /* add minor tick increment */
	  t += timeline_tickpoints[ tickindex ].minor;
	  localtime_r(&t, &t_tm);

	  /* store tick as a result if there is not a major tick already */
	  if (itree_find(results, t) == ITREE_NOVAL) {
	       tick = nmalloc(sizeof(struct timeline_tick));
	       tick->type  = TIMELINE_MINOR;
	       tick->label = NULL;
	       tick->point = t - timeline_offset;
	       tick->ptime = t;
	       itree_add(results, tick->point, tick);
#if 0
	       fprintf(stderr, "  point %ld (%s), type %d, label %s\n", 
		       tick->point, util_decdatetime(t), 
		       tick->type, tick->label);
#endif
	  }
     }
#endif

     /* max point (non-tick) */
     t = max + timeline_offset;
     localtime_r(&t, &t_tm);
     strftime(label, TIMELINE_SHORTSTR, "%d%b%y", &t_tm);
     /* store non-tick! as a result */
     tick = nmalloc(sizeof(struct timeline_tick));
     tick->type  = TIMELINE_NONE;
     tick->label = xnstrdup(label);
     tick->point = max;
     tick->ptime = t;
     itree_add(results, max, tick);
#if 0
     fprintf(stderr, "  point %ld (%s), type %d, label %s\n", 
	     tick->point, util_decdatetime(t), 
	     tick->type, tick->label);
#endif

     return results;
}