view examples/dsl/query.cpp @ 7:57b2cc9b87f7

Use memcpy instead of strncpy when we know the length anyway. Modern gcc seems to think it knows how to detect misuse of strncpy, but it's wrong (in fact: very, very wrong) and the path of least resistance is to not try to fight with it.
author David A. Holland
date Mon, 30 May 2022 23:47:52 -0400
parents 13d2b8934445
children ec2b657edf13
line wrap: on
line source

/*****

 AnaGram Programming Examples

 A Dos Script Language
 Query Table Module

 Copyright 1993 Parsifal Software. All Rights Reserved.

 This software is provided 'as-is', without any express or implied
 warranty.  In no event will the authors be held liable for any damages
 arising from the use of this software.

 Permission is granted to anyone to use this software for any purpose,
 including commercial applications, and to alter it and redistribute it
 freely, subject to the following restrictions:

 1. The origin of this software must not be misrepresented; you must not
    claim that you wrote the original software. If you use this software
    in a product, an acknowledgment in the product documentation would be
    appreciated but is not required.
 2. Altered source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.
 3. This notice may not be removed or altered from any source distribution.

*****/

#include "query.h"
#include "screen.h"
#include "symbol.h"
#include "util.h"
#include "edit.h"
#include <ctype.h>
#include <stdlib.h>

#if defined(__MSDOS__) || defined(__WINDOWS__)
#include <conio.h>
#else
/* This is only meant to compile, not run. (Unix) */
static int getch(void) { return '?'; }
#endif

// Editing keystrokes

#define BACKSPACE '\b'
#define BACKTAB   271
#define LEFT      331
#define DELETE    339
#define ESCAPE     27
#define DOWN      336
#define RIGHT     333
#define TAB       '\t'
#define UP        328
#define HOME      327
#define END       335


void perform_action(action_pointer);

int get_key(void) {
  int key = getch();
  if (key == 0) key = 256 + getch();
  if (key == 3) {
    printf("\n^C");
    exit(1);
  }
  return key;
}

void expand_formula(const int *s) {
  ++sa;
  while (*s) {
    switch (st[*s].type) {
      case value_type:
      case string_type: {
        sa << st[*s].data.text;
        break;
      }
      case integer_type: {
        sa.printf("%d", st[*s].data.integer);
        break;
      }
      default: {
        sa << sd[*s];
      }
    }
    s++;
  }
}

screen_rect &operator << (screen_rect &r, const int *s) {
  expand_formula(s);
  r << (sa--).top();
  return r;
}

// Query Table Class

// Constructor

query_table::query_table(stack<query_item> &qs, screen_descriptor *scd) {
  int i = 0;
  int j;

  title = scd->title;
  color = scd->color;
  entry_color = scd->entry_color;
  highlight_color = scd->highlight_color;
  height = scd->height;
  width = scd-> width;
  if (scd->pos.x >= 0)
    window_box = at(scd->pos.x,scd->pos.y).rect(width,height,0);
  else window_box = at(0,0).rect(width,height,11);
  height -= 2;
  width -= 4;
  window = window_box.rect(width,height);
  previous = contents(window_box);

  nq = size(qs);                            // Number of fields
  q =           new query_item[nq];         // Query items
  sr =          new screen_rect[nq];        // screen position
  field_color = new unsigned char[nq];      // color


  j = nq;
  for (j = nq, i = 0; j--; i++) {
    unsigned index;
    q[i] = qs[j];
    if (q[i].value == NULL) continue;
    index = q[i].id;
    release(st[index]);
    st[index].type = value_type;
    expand_formula(q[i].value);
    st[index].data.text = copy(sa--);
  }
  current_field = 0;
  resize();
}


// Destructor

query_table::~query_table() {
  window_box << previous;
  delete [] previous;
  delete [] q;
  delete [] sr;
  delete [] field_color;
}

query_table &query_table::resize(void) {
  int bw;                                   // bar width
  int i;                                    // index query lines
  int ln;                                   // line number

  int blank_lines;
  int justification_points;
  int undistributed_blanks;


 // Vertical justification calculations

  blank_lines = height - 2 - nq;
  justification_points = nq + 1;

  nbl = blank_lines/justification_points;   //number of blank lines

  undistributed_blanks =
    blank_lines - justification_points*nbl;

  title_line = undistributed_blanks/4;
  exp_line = height -1 - title_line;
  undistributed_blanks -= 2*title_line;

  first_line = undistributed_blanks/2 + nbl + 1;     // first line location


// Calculate horizontal spacing

  pw = 0;                                   // prompt width
  dw = width/2;                             // data width
  for (i = 0; i < nq; i++) {
    int qw;                                 // width of prompt-this query
    expand_formula(q[i].prompt);
    qw = size(sa--);                        // width
    if (qw > pw) pw = qw;                   // max prompt width
  }
  pw += 2;                                  // extra space
  bw = pw + dw;                             // bar width
  if (bw > width) bw = width;
  dw = bw - pw;                             // maybe reduce data width
  px = (width - bw)/2;                      // left position of prompt
  dx = px + pw;                             // left position of data


// Set up data windows

  ln = first_line;                          // ln is line number
  for (i = 0; i < nq; i++) {
    int id = q[i].id;                       // Variable id
    if (id) sr[i] = window.line(dx, ln, dw);       // If data field
    else {                                  // Otherwise button
      expand_formula(q[i].prompt);          // Expand prompt to get size
      sr[i] = window.line(px,ln, size(sa--));
    }
    ln += nbl + 1;
  }
  return *this;
}


// Refresh query table display

query_table &query_table::refresh(void) {
  int ln = first_line;
  int k;                                    // Index queries
  screen_rect msg_line;


// Reset field colors

  for (k = 0; k < nq; k++) field_color[k] = entry_color;
  field_color[current_field] = highlight_color;


// Display prompts and initial values

  for (k = 0; k < nq; k++) {
    int id = q[k].id;

    sr[k].tint(field_color[k]);
    msg_line = window.line(px,ln,width);
    msg_line << q[k].prompt;
    if (id) {                               // Data field
      if (q[k].value) {
        expand_formula(q[k].value);
        release(st[id]);
        st[id].data.text = copy(sa--);
      }
      sr[k] << st[q[k].id].data.text;
    }
    ln += nbl + 1;
  }
  sr[current_field].set_cursor();


// Display explanation line

  msg_line = window.line(0,exp_line, width, 10);
  msg_line << q[current_field].explanation;

  return *this;
}


int query_table::edit_line(void) {
  int key;

  if (current_field >= nq || q[current_field].id == 0) {
    return get_key();
  }

  line_edit_buffer buf(st[q[current_field].id].data.text,100);

  while (1) {
    sr[current_field] << buf;               // Update display
    key = get_key();
    switch (key) {
      case HOME:
        home(buf);
        break;
      case END:
        end(buf);
        break;
      case LEFT:
        --buf;
        break;
      case RIGHT:
        ++buf;
        break;
      case DELETE:
        ~buf;
        break;
      case BACKSPACE:
        if (buf.index() == 0) break;
        ~(--buf);
        break;
      case TAB:
      case BACKTAB:
      case UP:
      case DOWN:
      case '\r': {
        unsigned index = q[current_field].id;
        if (buf.changes) {
          st[index].data.text = memdup((char *) buf, 1+strlen(buf));
          delete [] q[current_field].value;
          q[current_field].value = NULL;
        }
      }
      case ESCAPE:
        return key;
      default:
        if (current_field < nq && key >= ' ' && key < 255) buf << key;
        break;
    }
  }
}

void display_queries(screen_descriptor *scd) {
  protect_display save_screen;

  query_table qt(qs,scd);
  qt.window_box << char_cell(' ',qt.color);
  qt.window = box(qt.window_box);
  screen_rect title_line = qt.window.line(0,qt.title_line,qt.width,10);
  title_line << qt.title;

  while (1) {
    int key;
    qt.refresh();
    key = qt.edit_line();
    switch (key) {
      case BACKTAB:
      case UP:
        qt.current_field += qt.nq - 1;
        qt.current_field %= qt.nq;
        break;
      case TAB:
      case DOWN:
        qt.current_field++;
        qt.current_field %= qt.nq;
        break;
      case '\r': {
        int id = qt.q[qt.current_field].id;
        action_pointer ap = qt.q[qt.current_field].action;
        if (id) {
          qt.current_field++;
          qt.current_field %= qt.nq;
          break;
        }
        if (ap.pointer) perform_action(ap);
        return;
      }
    }
  }
}