/*      $NetBSD: utilities.c,v 1.7 2024/08/18 20:47:20 christos Exp $   */

#include <config.h>
#include "utilities.h"
#include <assert.h>

/* Display a NTP packet in hex with leading address offset
* e.g. offset: value, 0: ff 1: fe ... 255: 00
*/
void
pkt_output (
               struct pkt *dpkg,
               int pkt_length,
               FILE *output
          )
{
       register int a;
       u_char *pkt;

       pkt = (u_char *)dpkg;

       fprintf(output, HLINE);

       for (a = 0; a < pkt_length; a++) {
               if (a > 0 && a % 8 == 0)
                       fprintf(output, "\n");

               fprintf(output, "%3d: %02x  ", a, pkt[a]);
       }

       fprintf(output, "\n");
       fprintf(output, HLINE);
}

/* Output a long floating point value in hex in the style described above
*/
void
l_fp_output(
       l_fp *  ts,
       FILE *  output
       )
{
       fprintf(output, "%s\n", prettydate(ts));
}

/* Output a long floating point value in binary in the style described above
*/
void
l_fp_output_bin (
               l_fp *ts,
               FILE *output
               )
{
       register int a, b;

       fprintf(output, HLINE);

       for(a=0; a<8; a++) {
               short tmp = ((unsigned char *) ts)[a];
               tmp++;

               fprintf(output, "%i: ", a);

               for(b=7; b>=0; b--) {
                       int texp = (int) pow(2, b);

                       if(tmp - texp > 0) {
                               fprintf(output, "1");
                               tmp -= texp;
                       }
                       else {
                               fprintf(output, "0");
                       }
               }

               fprintf(output, " ");
       }

       fprintf(output, "\n");
       fprintf(output, HLINE);
}

/* Output a long floating point value in decimal in the style described above
*/
void
l_fp_output_dec (
               l_fp *ts,
               FILE *output
           )
{
       register int a;

       fprintf(output, HLINE);

       for(a=0; a<8; a++)
               fprintf(output, "%i: %i \t", a, ((unsigned char *) ts)[a]);

       fprintf(output, "\n");
       fprintf(output, HLINE);

}

/* Convert a struct addrinfo to a string containing the address in style
* of inet_ntoa
*/
char *
addrinfo_to_str (
       const struct addrinfo *addr
       )
{
       sockaddr_u      s;

       ZERO(s);
       memcpy(&s, addr->ai_addr, min(sizeof(s), addr->ai_addrlen));

       return ss_to_str(&s);
}


/* Convert a sockaddr_u to a string containing the address in
* style of inet_ntoa
* Why not switch callers to use stoa from libntp?  No free() needed
* in that case.
*/
char *
ss_to_str(
       sockaddr_u *saddr
       )
{
       return estrdup(stoa(saddr));
}


/*
* Converts a struct tv to a date string
*/
char *
tv_to_str(
       const struct timeval *tv
       )
{
       const size_t bufsize = 48;
       char *buf;
       time_t gmt_time, local_time;
       struct tm *ptm, tm_local;
       int hh, mm, lto, isdst;

       /*
        * convert to struct tm in UTC, then intentionally feed
        * that tm to mktime() which expects local time input, to
        * derive the offset from UTC to local time.
        * Need to retrieve dst flag from localtime first for mktime.
        */
       gmt_time = tv->tv_sec;
       ptm = localtime(&gmt_time);
       memcpy (&tm_local, ptm, sizeof(tm_local));
       isdst = ptm->tm_isdst;
       ptm = gmtime(&gmt_time);
       ptm->tm_isdst = isdst;
       local_time = mktime(ptm);

       /* Local timezone offsets should never cause an overflow.  Yeah. */
       lto = difftime(local_time, gmt_time);
       lto /= 60;
       hh = lto / 60;
       mm = abs(lto % 60);

       buf = emalloc(bufsize);
       snprintf(buf, bufsize,
                "%d-%.2d-%.2d %.2d:%.2d:%.2d.%.6d (%+03d%02d)",
                tm_local.tm_year + 1900,
                tm_local.tm_mon + 1,
                tm_local.tm_mday,
                tm_local.tm_hour,
                tm_local.tm_min,
                tm_local.tm_sec,
                (int)tv->tv_usec,
                hh,
                mm);

       return buf;
}


/*
*
* hostnameaddr()
*
* Formats the hostname and resulting numeric IP address into a string,
* avoiding duplication if the "hostname" was in fact a numeric address.
*
*/
const char *
hostnameaddr(
       const char *            hostname,
       const sockaddr_u *      addr
       )
{
       const char *    addrtxt;
       char *          result;
       int             cnt;

       addrtxt = stoa(addr);
       LIB_GETBUF(result);
       if (strcmp(hostname, addrtxt))
               cnt = snprintf(result, LIB_BUFLENGTH, "%s %s",
                              hostname, addrtxt);
       else
               cnt = snprintf(result, LIB_BUFLENGTH, "%s", addrtxt);
       if (cnt >= LIB_BUFLENGTH)
               snprintf(result, LIB_BUFLENGTH,
                        "hostnameaddr ERROR have %d (%d needed)",
                        LIB_BUFLENGTH, cnt + 1);

       return result;
}