Subversion Repositories SvarDOS

Rev

Rev 989 | Blame | Compare with Previous | Last modification | View Log | RSS feed

/* This file is part of the SvarCOM project and is published under the terms
 * of the MIT license.
 *
 * Copyright (C) 2021-2022 Mateusz Viste
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

/*
 * time [time]
 */


/* read a one or two digit number and write it to buff */
static int cmd_time_get_item(char *buff, const char *s) {
  unsigned short i;

  for (i = 0; i < 3; i++) {
    if ((s[i] < '0') || (s[i] > '9')) {
      buff[i] = 0;
      return(0);
    }
    buff[i] = s[i];
  }

  /* err */
  *buff = 0;
  return(-1);
}


/* parse a NULL-terminated string int hour, minutes and seconds, returns 0 on success
 * valid inputs: 0, 7, 5:5, 23:23, 17:54:45, 9p, 9:05, ...
 */
static int cmd_time_parse(const char *s, signed char *ho, signed char *mi, signed char *se, struct nls_patterns *nls) {
  unsigned short i;
  const char *ptrs[2] = {NULL, NULL}; /* minutes, seconds */
  char buff[3];
  char ampm = 0;

  *ho = -1;
  *mi = 0;
  *se = 0;

  /* validate input - must contain only chars 0-9, time separator and 'a' or 'p' */
  for (i = 0; s[i] != 0; i++) {
    switch (s[i]) {
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        break;
      case 'a':
      case 'A':
      case 'p':
      case 'P':
        /* these can be only at last position and never at the first */
        if ((s[i + 1] != 0) || (i == 0)) return(-1);
        ampm = s[i];
        if (ampm >= 'a') ampm -= ('a' - 'A');
        break;
      default:
        if ((s[i] != nls->timesep[0]) || (i == 0)) return(-1);
        if (ptrs[0] == NULL) {
          ptrs[0] = s + i + 1;
        } else if (ptrs[1] == NULL) {
          ptrs[1] = s + i + 1;
        } else { /* too many separators */
          return(-1);
        }
        break;
    }
  }

  /* read hour */
  if (cmd_time_get_item(buff, s) != 0) goto FAIL;
  if (atous(&i, buff) != 0) goto FAIL;
  *ho = i;

  /* if minutes provided, read them */
  if (ptrs[0] != NULL) {
    if (cmd_time_get_item(buff, ptrs[0]) != 0) goto FAIL;
    if (atous(&i, buff) != 0) goto FAIL;
    *mi = i;
  }

  /* if seconds provided, read them */
  if (ptrs[1] != NULL) {
    if (cmd_time_get_item(buff, ptrs[1]) != 0) goto FAIL;
    if (atous(&i, buff) != 0) goto FAIL;
    *se = i;
  }

  /* validate ranges */
  if ((*ho > 23) || (*mi > 59) || (*se > 59)) goto FAIL;

  /* am? */
  if ((ampm == 'A') && (*ho > 12)) goto FAIL;
  if ((ampm == 'A') && (*ho == 12)) *ho = 0; /* 12:00am is 00:00 (midnight) */

  /* pm? */
  if (ampm == 'P') {
    if (*ho > 12) goto FAIL;
    if (*ho < 12) *ho += 12;
  }

  return(0);

  FAIL:
  *ho = -1;
  return(-1);
}


static enum cmd_result cmd_time(struct cmd_funcparam *p) {
  struct nls_patterns *nls = (void *)(p->BUFFER);
  char *buff = p->BUFFER + sizeof(*nls);
  unsigned short i;
  signed char ho = -1, mi = -1, se = -1;

  if (cmd_ishlp(p)) {
    nls_outputnl(22,0); /* "Displays or sets the system time." */
    outputnl("");
    nls_outputnl(22,1); /* "TIME [time]" */
    outputnl("");
    nls_outputnl(22,2); /* "Type TIME with no parameters to display the current time and (...)" */
    return(CMD_OK);
  }

  i = nls_getpatterns(nls);
  if (i != 0) {
    nls_outputnl_doserr(i);
    return(CMD_FAIL);
  }

  /* display current time if no args */
  if (p->argc == 0) {
    /* get cur time */
    _asm {
      push ax
      push bx
      push cx
      push dx

      mov ah, 0x2c  /* DOS 1+ -- Query DOS Time */
      int 0x21      /* CH=hour CL=minutes DH=seconds DL=1/100sec */
      mov [ho], ch
      mov [mi], cl
      mov [se], dh

      pop dx
      pop cx
      pop bx
      pop ax
    }
    buff[0] = ' ';
    nls_format_time(buff + 1, ho, mi, se, nls);
    nls_output(22,3); /* "Current time is" */
    outputnl(buff);
    ho = -1;
  } else { /* parse time if provided */
    if (cmd_time_parse(p->argv[0], &ho, &mi, &se, nls) != 0) {
      nls_outputnl(22,4); /* "Invalid time" */
      ho = -1;
    }
  }

  /* ask for time if not provided or if input was malformed */
  while (ho < 0) {
    nls_output(22,5); /* "Enter new time:" */
    output(" ");
    /* collect user input into buff */
    _asm {
      push ax
      push bx
      push dx

      mov ah, 0x0a   /* DOS 1+ -- Buffered String Input */
      mov bx, buff
      mov dx, bx
      mov al, 16
      mov [bx], al   /* max input length */
      mov al, 1
      mov [bx+1], al /* zero out the "previous entry" length */
      int 0x21
      /* terminate the string with a NULL terminator */
      xor ax, ax
      inc bx
      mov al, [bx] /* read length of input string */
      mov bx, ax
      add bx, dx
      mov [bx+2], ah
      /* output a \n */
      mov ah, 2
      mov dl, 0x0A
      int 0x21

      pop dx
      pop bx
      pop ax
    }
    if (buff[1] == 0) break; /* empty string = do not change time */
    if (cmd_time_parse(buff + 2, &ho, &mi, &se, nls) == 0) break;
    nls_outputnl(22,4); /* "Invalid time" */
    return(CMD_FAIL);
  }

  if (ho >= 0) {
    /* set time */
    _asm {
      push ax
      push bx
      push cx
      push dx

      mov ah, 0x2d   /* DOS 1+ -- Set DOS Time */
      mov ch, [ho]   /* hour (0-23) */
      mov cl, [mi]   /* minutes (0-59) */
      mov dh, [se]   /* seconds (0-59) */
      mov dl, 0      /* 1/100th seconds (0-99) */
      int 0x21

      pop dx
      pop cx
      pop bx
      pop ax
    }
  }

  return(CMD_OK);
}