109
81
#define ANSI_CLEAR_EOL "\x1B[K"
110
82
#define ANSI_CURSOR "\x1B[%d;%dH"
112
int NPDup = 0; /* 1 when new panel is up */
113
int NPDinit = 0; /* 1 when new panel is initialized */
114
int NPhelpup = 0; /* 1 when displaying help panel */
115
int NPhelpdown = 0; /* 1 when the help panel is brought down */
116
int NPregdisp = 0; /* which regs are displayed 0=gpr, 1=cr, 2=ar, 3=fpr */
117
int NPaddress = 0; /* Address switches */
118
int NPdata = 0; /* Data switches */
119
int NPipl = 0; /* IPL address switches */
121
int NPcmd = 0; /* 1 when command mode for NP is in effect */
122
int NPdataentry = 0; /* 1 when data entry for NP is in progress */
123
int NPdevsel = 0; /* 1 when device selection is in progress */
84
/*-------------------------------------------------------------------*/
85
/* Definitions for ANSI control sequences */
86
/*-------------------------------------------------------------------*/
87
#define ANSI_SAVE_CURSOR "\x1B[s"
88
#define ANSI_CURSOR_UP "\x1B[1A"
89
#define ANSI_CURSOR_DOWN "\x1B[1B"
90
#define ANSI_CURSOR_FORWARD "\x1B[1C"
91
#define ANSI_CURSOR_BACKWARD "\x1B[1D"
92
#define ANSI_POSITION_CURSOR "\x1B[%d;%dH"
93
#define ANSI_ROW1_COL1 "\x1B[1;1H"
94
#define ANSI_ROW1_COL80 "\x1B[1;80H"
95
#define ANSI_ROW22_COL80 "\x1B[22;80H"
96
#define ANSI_ROW23_COL1 "\x1B[23;1H"
97
#define ANSI_ROW24_COL1 "\x1B[24;1H"
98
#define ANSI_ROW24_COL79 "\x1B[24;79H"
99
#define ANSI_BLACK_GREEN "\x1B[30;42m"
100
#define ANSI_YELLOW_RED "\x1B[33;1;41m"
101
#define ANSI_WHITE_BLACK "\x1B[0m"
102
#define ANSI_HIGH_INTENSITY "\x1B[1m"
103
#define ANSI_ERASE_EOL "\x1B[K"
104
#define ANSI_ERASE_SCREEN "\x1B[2J"
105
#define ANSI_RESTORE_CURSOR "\x1B[u"
107
/*-------------------------------------------------------------------*/
108
/* Definitions for keyboard input sequences */
109
/*-------------------------------------------------------------------*/
110
#define KBD_HOME "\x1B[1~"
111
#define KBD_INSERT "\x1B[2~"
112
#define KBD_DELETE "\x1B[3~"
113
#define KBD_END "\x1B[4~"
114
#define KBD_PAGE_UP "\x1B[5~"
115
#define KBD_PAGE_DOWN "\x1B[6~"
116
#define KBD_UP_ARROW "\x1B[A"
117
#define KBD_DOWN_ARROW "\x1B[B"
118
#define KBD_RIGHT_ARROW "\x1B[C"
119
#define KBD_LEFT_ARROW "\x1B[D"
120
#define xKBD_UP_ARROW "\x1BOA"
121
#define xKBD_DOWN_ARROW "\x1BOB"
122
#define xKBD_RIGHT_ARROW "\x1BOC"
123
#define xKBD_LEFT_ARROW "\x1BOD"
125
#define ANSI_RESET_WHT_BLK "\x1B[0;37;40m"
126
#define ANSI_CLEAR_SCREEN "\x1B[2J"
128
int NPDup = 0; /* 1 when new panel is up */
129
int NPDinit = 0; /* 1 when new panel is initialized */
130
int NPhelpup = 0; /* 1 when displaying help panel */
131
int NPhelpdown = 0; /* 1 when the help panel is brought down */
132
int NPregdisp = 0; /* which regs are displayed 0=gpr, 1=cr, 2=ar, 3=fpr */
133
int NPaddress = 0; /* Address switches */
134
int NPdata = 0; /* Data switches */
135
int NPipl = 0; /* IPL address switches */
137
int NPcmd = 0; /* 1 when command mode for NP is in effect */
138
int NPdataentry = 0; /* 1 when data entry for NP is in progress */
139
int NPdevsel = 0; /* 1 when device selection is in progress */
124
140
char NPpending; /* Command which is pending data entry */
125
141
char NPentered[128]; /* Data which was entered */
126
142
char NPprompt1[40]; /* Prompts for left and right bottom of screen */
127
143
char NPprompt2[40];
128
144
char NPsel2; /* Command letter to trigger 2nd phase of dev sel */
129
145
char NPdevice; /* Which device selected */
130
int NPasgn; /* Index to device being reassigned */
131
int NPlastdev; /* Number of devices */
132
int NPdevaddr[24]; /* current device addresses */
146
int NPasgn; /* Index to device being reassigned */
147
int NPlastdev; /* Number of devices */
148
int NPdevaddr[24]; /* current device addresses */
133
149
char NPdevstr[16]; /* device - stringed */
135
151
/* the following fields are current states, to detect changes and redisplay */
137
153
char NPstate[24]; /* Current displayed CPU state */
138
U32 NPregs[16]; /* Current displayed reg values */
139
int NPbusy[24]; /* Current busy state of displayed devices */
140
int NPpend[24]; /* Current int pending state */
141
int NPopen[24]; /* Current open state */
142
int NPonline[24]; /* Current online state of devices */
143
char NPdevname[24][128]; /* Current name assignments */
144
int NPcuraddr; /* current addr switches */
145
int NPcurdata; /* current data switches */
146
int NPcurrg; /* current register set displayed */
147
int NPcuripl; /* current IPL switches */
148
int NPcurpos[2]; /* Cursor position (row, col) */
154
U32 NPregs[16]; /* Current displayed reg values */
155
int NPbusy[24]; /* Current busy state of displayed devices */
156
int NPpend[24]; /* Current int pending state */
157
int NPopen[24]; /* Current open state */
158
int NPonline[24]; /* Current online state of devices */
159
char NPdevname[24][128];/* Current name assignments */
160
int NPcuraddr; /* current addr switches */
161
int NPcurdata; /* current data switches */
162
int NPcurrg; /* current register set displayed */
163
int NPcuripl; /* current IPL switches */
164
int NPcurpos[2]; /* Cursor position (row, col) */
149
165
char NPcolor[24]; /* color string */
150
int NPdatalen; /* Length of data */
166
int NPdatalen; /* Length of data */
151
167
char NPcurprompt1[40];
152
168
char NPcurprompt2[40];
156
/*-------------------------------------------------------------------*/
157
/* External GUI control */
158
/*-------------------------------------------------------------------*/
159
#ifdef OPTION_MIPS_COUNTING
162
U32 prevmipsrate = 0;
163
U32 prevsiosrate = 0;
164
int gui_cpupct = 0; /* 1=cpu percentage active */
165
#endif /*OPTION_MIPS_COUNTING*/
166
int gui_gregs = 1; /* 1=gregs status active */
167
int gui_cregs = 1; /* 1=cregs status active */
168
int gui_aregs = 1; /* 1=aregs status active */
169
int gui_fregs = 1; /* 1=fregs status active */
170
int gui_devlist = 1; /* 1=devlist status active */
174
int stat_online, stat_busy, stat_pend, stat_open;
175
#endif /*EXTERNALGUI*/
171
#define MAX_MSGS 800 /* Number of slots in buffer */
172
#define MSG_SIZE 80 /* Size of one message */
173
#define BUF_SIZE (MAX_MSGS*MSG_SIZE) /* Total size of buffer */
174
#define NUM_LINES 22 /* Number of scrolling lines */
175
#define CMD_SIZE 32767 /* Length of command line */
177
static int firstmsgn = 0; /* Number of first message to
178
be displayed relative to
179
oldest message in buffer */
180
static BYTE *msgbuf; /* Circular message buffer */
181
static int msgslot = 0; /* Next available buffer slot*/
182
static int nummsgs = 0; /* Number of msgs in buffer */
187
// THE FOLLOWING CODE IS HERE TO PROVIDE COMPATIBILIY WITH THE CURRENT
188
// PANEL IMPLEMENTATION
190
// THE PANEL DISPLAY SHOULD AT SOME POINT BE REWRITTEN TO USE LOG_LINE
191
// AND LOG_READ RATHER THEN THE MESSAGE PIPE PROVIDED HERE
194
FILE *compat_msgpipew; /* Message pipe write handle */
195
int compat_msgpiper; /* Message pipe read handle */
196
int compat_shutdown; /* Shutdown flag */
199
#if defined(OPTION_DYNAMIC_LOAD)
200
void *(*panel_command) (void *);
201
void (*panel_display) (void);
202
void (*daemon_task) (void);
203
int (*config_command) (int argc, char *argv[], char *cmdline);
204
int (*system_command) (int argc, char *argv[], char *cmdline);
205
void *(*debug_cpu_state) (REGS *);
206
void *(*debug_device_state) (DEVBLK *);
207
void *(*debug_program_interrupt) (REGS *, int);
208
void *(*debug_diagnose) (U32, int, int, REGS *);
209
void *(*debug_sclp_unknown_command) (U32, void *, REGS *);
210
void *(*debug_sclp_unknown_event) (void *, void *, REGS *);
211
void *(*debug_sclp_event_data) (void *, void *, REGS *);
212
void *(*debug_chsc_unknown_request) (void *, void *, REGS *);
213
void *(*debug_watchdog_signal) (REGS *);
219
static void *panel_compat_thread(void *arg)
224
while(!compat_shutdown)
225
if((lmscnt = log_read(&lmsbuf, &lmsnum, LOG_BLOCK)))
226
fwrite(lmsbuf,lmscnt,1,compat_msgpipew);
228
fclose(compat_msgpipew);
234
static void panel_compat_init()
242
logmsg(_("HHCLG013S Message pipe creation failed: %s\n"),
248
compat_msgpiper = pfd[0];
249
compat_msgpipew = fdopen (pfd[1], "w");
250
if (compat_msgpipew == NULL)
252
compat_msgpipew = stderr;
253
logmsg(_("HHCLG014S Message pipe open failed: %s\n"),
257
setvbuf (compat_msgpipew, NULL, _IOLBF, 0);
259
initialize_detach_attr (&compat_attr);
261
create_thread(&compat_tid, &compat_attr, panel_compat_thread, NULL);
177
265
/*=NP================================================================*/
178
266
/* Initialize the NP data */
658
743
/* ============== End of the main NP block of code =============*/
661
/*-------------------------------------------------------------------*/
662
/* Definitions for ANSI control sequences */
663
/*-------------------------------------------------------------------*/
664
#define ANSI_SAVE_CURSOR "\x1B[s"
665
#define ANSI_CURSOR_UP "\x1B[1A"
666
#define ANSI_CURSOR_DOWN "\x1B[1B"
667
#define ANSI_CURSOR_FORWARD "\x1B[1C"
668
#define ANSI_CURSOR_BACKWARD "\x1B[1D"
669
#define ANSI_POSITION_CURSOR "\x1B[%d;%dH"
670
#define ANSI_ROW1_COL1 "\x1B[1;1H"
671
#define ANSI_ROW1_COL80 "\x1B[1;80H"
672
#define ANSI_ROW22_COL80 "\x1B[22;80H"
673
#define ANSI_ROW23_COL1 "\x1B[23;1H"
674
#define ANSI_ROW24_COL1 "\x1B[24;1H"
675
#define ANSI_ROW24_COL79 "\x1B[24;79H"
676
#define ANSI_BLACK_GREEN "\x1B[30;42m"
677
#define ANSI_YELLOW_RED "\x1B[33;1;41m"
678
#define ANSI_WHITE_BLACK "\x1B[0m"
679
#define ANSI_HIGH_INTENSITY "\x1B[1m"
680
#define ANSI_ERASE_EOL "\x1B[K"
681
#define ANSI_ERASE_SCREEN "\x1B[2J"
682
#define ANSI_RESTORE_CURSOR "\x1B[u"
684
/*-------------------------------------------------------------------*/
685
/* Definitions for keyboard input sequences */
686
/*-------------------------------------------------------------------*/
687
#define KBD_HOME "\x1B[1~"
688
#define KBD_INSERT "\x1B[2~"
689
#define KBD_DELETE "\x1B[3~"
690
#define KBD_END "\x1B[4~"
691
#define KBD_PAGE_UP "\x1B[5~"
692
#define KBD_PAGE_DOWN "\x1B[6~"
693
#define KBD_UP_ARROW "\x1B[A"
694
#define KBD_DOWN_ARROW "\x1B[B"
695
#define KBD_RIGHT_ARROW "\x1B[C"
696
#define KBD_LEFT_ARROW "\x1B[D"
698
/*-------------------------------------------------------------------*/
699
/* Cleanup routine */
700
/*-------------------------------------------------------------------*/
701
static void system_cleanup (void)
746
static void panel_cleanup(void *unused __attribute__ ((unused)) )
703
748
struct termios kbattr; /* Terminal I/O structure */
705
755
/* Restore the terminal mode */
706
756
tcgetattr (STDIN_FILENO, &kbattr);
707
757
kbattr.c_lflag |= (ECHO | ICANON);
708
758
tcsetattr (STDIN_FILENO, TCSANOW, &kbattr);
712
#endif /*EXTERNALGUI*/
713
/* Reset the cursor position */
719
"HHCIN007I Hercules terminated\n");
721
} /* end function system_cleanup */
723
/*-------------------------------------------------------------------*/
724
/* Process .RC file thread */
725
/*-------------------------------------------------------------------*/
727
int rc_thread_done = 0; /* 1 = RC file processed */
729
void* process_rc_file (void* dummy)
731
BYTE *rcname; /* hercules.rc name pointer */
732
FILE *rcfp; /* RC file pointer */
733
size_t rcbufsize = 1024; /* Size of RC file buffer */
734
BYTE *rcbuf = NULL; /* RC file input buffer */
735
int rclen; /* length of RC file record */
736
int rc_pause_amt = 0; /* seconds to pause RC file */
737
BYTE *p; /* (work) */
741
/* Obtain the name of the hercules.rc file or default */
743
if(!(rcname = getenv("HERCULES_RC")))
744
rcname = "hercules.rc";
746
/* Open RC file. If it doesn't exist,
747
then there's nothing for us to do */
749
if (!(rcfp = fopen(rcname, "r")))
752
logmsg(_("HHCPN007E RC file %s open failed: %s\n"),
753
rcname, strerror(errno));
758
logmsg(_("HHCPN008I RC file processing thread started using file %s\n"),
761
/* Obtain storage for the RC file buffer */
763
if (!(rcbuf = malloc (rcbufsize)))
765
logmsg(_("HHCPN009E RC file buffer malloc failed: %s\n"),
774
/* Read a complete line from the RC file */
776
if (!fgets(rcbuf, rcbufsize, rcfp)) break;
778
/* Remove trailing whitespace */
780
for (rclen = strlen(rcbuf); rclen && isspace(rcbuf[rclen-1]); rclen--);
783
/* '#' == silent comment, '*' == loud comment */
785
if ('#' == rcbuf[0] || '*' == rcbuf[0])
788
logmsg ("> %s",rcbuf);
792
/* Remove any # comments on the line before processing */
794
if ((p = strchr(rcbuf,'#')) && p > rcbuf)
795
do *p = 0; while (isspace(*--p) && p >= rcbuf);
797
if (strncasecmp(rcbuf,"pause",5) == 0)
799
sscanf(rcbuf+5, "%d", &rc_pause_amt);
801
if (rc_pause_amt < 0 || rc_pause_amt > 999)
803
logmsg(_("HHCPN010W Ignoring invalid RC file pause "
809
logmsg (_("HHCPN011I Pausing RC file processing for %d "
813
logmsg (_("HHCPN012I Resuming RC file processing...\n"));
818
/* Process the command */
820
for (p = rcbuf; isspace(*p); p++);
822
SYNCHRONOUS_PANEL_CMD(p);
826
logmsg (_("HHCPN013I EOF reached on RC file. Processing complete.\n"));
828
logmsg (_("HHCPN014E I/O error reading RC file: %s\n"),
760
fprintf(stderr, ANSI_RESET_WHT_BLK ANSI_CLEAR_SCREEN );
762
/* Reset the first line to be displayed (i.e.
763
"scroll down to the most current message") */
764
firstmsgn = nummsgs - NUM_LINES;
765
if (firstmsgn < 0) firstmsgn = 0;
767
/* Display messages in scrolling area */
768
for (i=0; i < NUM_LINES && firstmsgn + i < nummsgs; i++)
770
n = (nummsgs < MAX_MSGS) ? 0 : msgslot;
772
if (n >= MAX_MSGS) n -= MAX_MSGS;
777
fwrite (msgbuf + (n * MSG_SIZE), MSG_SIZE, 1, stderr);
780
/* Read any remaining msgs from the msg pipe */
781
while(read (compat_msgpiper, &c, 1) > 0)
784
/* Read any remaining msgs from the system log */
785
while((lmscnt = log_read(&lmsbuf, &lmsnum, LOG_NOBLOCK)))
786
fwrite(lmsbuf,lmscnt,1,stderr);
838
789
/*-------------------------------------------------------------------*/
839
790
/* Panel display thread */
841
792
/* This function runs on the main thread. It receives messages */
842
/* from other threads and displays them on the screen. It accepts */
793
/* from the log task and displays them on the screen. It accepts */
843
794
/* panel commands from the keyboard and executes them. It samples */
844
795
/* the PSW periodically and displays it on the screen status line. */
846
/* Note that this routine must not attempt to write messages into */
847
/* the message pipe by calling the logmsg function, because this */
848
/* will cause a deadlock when the pipe becomes full during periods */
849
/* of high message activity. For this reason a separate thread is */
850
/* created to process all commands entered. */
851
796
/*-------------------------------------------------------------------*/
853
int volatile initdone = 0; /* Initialization complete flag */
855
#define MAX_MSGS 800 /* Number of slots in buffer */
856
#define MSG_SIZE 80 /* Size of one message */
857
#define BUF_SIZE (MAX_MSGS*MSG_SIZE) /* Total size of buffer */
858
#define NUM_LINES 22 /* Number of scrolling lines */
859
#define CMD_SIZE 32767 /* Length of command line */
860
BYTE *msgbuf; /* Circular message buffer */
861
int msgslot = 0; /* Next available buffer slot*/
862
int nummsgs = 0; /* Number of msgs in buffer */
863
int msg_size = MSG_SIZE;
864
int max_msgs = MAX_MSGS;
866
void get_msgbuf(BYTE **_msgbuf, int *_msgslot, int *_nummsgs, int *_msg_size, int *_max_msgs)
871
*_msg_size = msg_size;
872
*_max_msgs = max_msgs;
875
/* (forward references) */
877
void gui_devlist_status (FILE *confp);
798
#if defined(OPTION_DYNAMIC_LOAD)
799
void panel_display_r (void)
880
801
void panel_display (void)
882
804
int rc; /* Return code */
883
805
int i, n; /* Array subscripts */
1218
/* Process backspace character */
1219
if (kbbuf[i] == '\b' || kbbuf[i] == '\x7F')
1223
for (j = cmdoff-1; j<cmdlen; j++)
1224
cmdline[j] = cmdline[j+1];
1232
/* Process DEL character */
1233
if (strcmp(kbbuf+i, KBD_DELETE) == 0) {
1234
if (cmdoff < cmdlen) {
1236
for (j = cmdoff; j<cmdlen; j++)
1237
cmdline[j] = cmdline[j+1];
1245
/* Process LEFT_ARROW character */
1246
if (strcmp(kbbuf+i, KBD_LEFT_ARROW) == 0
1247
|| strcmp(kbbuf+i, xKBD_LEFT_ARROW) == 0)
1249
if (cmdoff > 0) cmdoff--;
1255
/* Test for other KBD_* strings */
1256
/* Just ignore them, no function assigned */
1257
if (strcmp(kbbuf+i, KBD_RIGHT_ARROW) == 0
1258
|| strcmp(kbbuf+i, xKBD_RIGHT_ARROW) == 0
1259
|| strcmp(kbbuf+i, KBD_INSERT) == 0)
1262
if (cmdoff < cmdlen)
1323
1267
/* Process escape key */
1324
1268
if (kbbuf[i] == '\x1B')
1326
1270
/* =NP= : Switch to new panel display */
1333
/* Process backspace character */
1334
if (kbbuf[i] == '\b' || kbbuf[i] == '\x7F'
1335
|| strcmp(kbbuf+i, KBD_LEFT_ARROW) == 0)
1280
/* Process TAB character */
1281
if (kbbuf[i] == '\t' || kbbuf[i] == '\x7F')
1337
if (cmdoff > 0) cmdoff--;
1283
tab_pressed(cmdline, &cmdoff);
1284
cmdlen = strlen(cmdline);
1285
/* cmdoff = cmdlen; */
1343
1291
/* Process the command if newline was read */
1344
1292
if (kbbuf[i] == '\n')
1346
cmdline[cmdoff] = '\0';
1347
/* =NP= create_thread replaced with: */
1350
if (extgui && (cmdline[0] == ']'))
1354
if (strncmp(cmdline,"]GREGS=",7) == 0)
1356
gui_gregs = atoi(cmdline+7);
1359
if (strncmp(cmdline,"]CREGS=",7) == 0)
1361
gui_cregs = atoi(cmdline+7);
1364
if (strncmp(cmdline,"]AREGS=",7) == 0)
1366
gui_aregs = atoi(cmdline+7);
1369
if (strncmp(cmdline,"]FREGS=",7) == 0)
1371
gui_fregs = atoi(cmdline+7);
1374
if (strncmp(cmdline,"]DEVLIST=",9) == 0)
1376
gui_devlist = atoi(cmdline+9);
1379
if (strncmp(cmdline,"]MAINSTOR=",10) == 0)
1381
fprintf(stderr,"MAINSTOR=%d\n",(U32)regs->mainstor);
1382
fprintf(stderr,"MAINSIZE=%d\n",(U32)sysblk.mainsize);
1384
#if defined(OPTION_MIPS_COUNTING)
1386
if (strncmp(cmdline,"]CPUPCT=",8) == 0)
1388
gui_cpupct = atoi(cmdline+8);
1390
#endif /*defined(OPTION_MIPS_COUNTING)*/
1393
#endif /*EXTERNALGUI*/
1395
if ('#' == cmdline[0] || '*' == cmdline[0])
1294
if (cmdlen == 0 && NPDup == 0) {
1297
cmdline[cmdlen] = '\0';
1298
/* =NP= create_thread replaced with: */
1300
if ('#' == cmdline[0] || '*' == cmdline[0]) {
1397
1301
if ('*' == cmdline[0])
1398
1302
logmsg("%s\n", cmdline);
1404
ASYNCHRONOUS_PANEL_CMD(cmdline);
1412
strcpy(NPcolor, "");
1413
switch (NPpending) {
1415
sscanf(cmdline, "%x", &NPaddress);
1417
strcpy(NPprompt1, "");
1420
sscanf(cmdline, "%x", &NPdata);
1422
strcpy(NPprompt1, "");
1425
if (strlen(cmdline) < 1) {
1426
strcpy(cmdline, NPdevname[NPasgn]);
1428
strcpy(NPdevname[NPasgn], "");
1429
strcpy(NPentered, "devinit ");
1430
sprintf(NPdevstr, "%x", NPdevaddr[NPasgn]);
1431
strcat(NPentered, NPdevstr);
1432
strcat(NPentered, " ");
1433
strcat(NPentered, cmdline);
1434
ASYNCHRONOUS_PANEL_CMD(NPentered);
1435
strcpy(NPprompt2, "");
1304
history_requested = 0;
1305
panel_command(cmdline);
1307
for (;cmdlen >=0; cmdlen--)
1308
cmdline[cmdlen] = '\0';
1310
if (history_requested == 1) {
1311
strcpy(cmdline, historyCmdLine);
1312
cmdoff = strlen(cmdline);
1323
strcpy(NPcolor, "");
1324
switch (NPpending) {
1326
sscanf(cmdline, "%x", &NPaddress);
1328
strcpy(NPprompt1, "");
1331
sscanf(cmdline, "%x", &NPdata);
1333
strcpy(NPprompt1, "");
1336
if (strlen(cmdline) < 1) {
1337
strcpy(cmdline, NPdevname[NPasgn]);
1339
strcpy(NPdevname[NPasgn], "");
1340
strcpy(NPentered, "devinit ");
1341
sprintf(NPdevstr, "%x", NPdevaddr[NPasgn]);
1342
strcat(NPentered, NPdevstr);
1343
strcat(NPentered, " ");
1344
strcat(NPentered, cmdline);
1345
panel_command(NPentered);
1346
strcpy(NPprompt2, "");
1445
/* Process *ALL* of the 'keyboard' (stdin) buffer data! */
1446
if (extgui) {i++; continue;}
1447
#endif /*EXTERNALGUI*/
1460
1370
/* Append the character to the command buffer */
1461
if (cmdoff < CMD_SIZE-1) cmdline[cmdoff++] = kbbuf[i];
1371
if (cmdoff < CMD_SIZE-1) {
1372
if (cmdoff < cmdlen) {
1374
for (j=cmdlen-1; j>=cmdoff; j--)
1375
cmdline[j+1] = cmdline[j];
1376
cmdline[cmdoff++] = kbbuf[i];
1379
cmdline[cmdoff++] = kbbuf[i];
1463
1383
redraw_cmd = 1;
1465
1384
} /* end for(i) */
1468
1387
/* If a message has arrived then receive it */
1469
1388
if (FD_ISSET(pipefd, &readset))
1471
/* Clear the message buffer */
1472
memset (readbuf, SPACE, MSG_SIZE);
1474
/* Read message bytes until newline */
1477
/* Read a byte from the message pipe */
1478
rc = read (pipefd, &c, 1);
1482
"HHCPN006E message pipe read: %s\n",
1488
/* Exit if newline was read */
1489
if (c == '\n') break;
1491
/* Handle tab character */
1495
readoff &= 0xFFFFFFF8;
1499
/* Eliminate non-printable characters */
1500
if (!isprint(c)) c = SPACE;
1502
/* Append the byte to the read buffer */
1503
if (readoff < MSG_SIZE) readbuf[readoff++] = c;
1507
/* Exit if read was unsuccessful */
1510
/* Copy the message to the log file if present */
1513
fprintf (logfp, "%.*s\n", readoff, readbuf);
1521
/* Copy message to circular buffer and empty read buffer */
1522
#if defined(EXTERNALGUI) && !defined(OPTION_HTTP_SERVER)
1524
#endif /*EXTERNALGUI*/
1525
memcpy (msgbuf + (msgslot * MSG_SIZE), readbuf, MSG_SIZE);
1528
#if defined(EXTERNALGUI) && !defined(OPTION_HTTP_SERVER)
1531
#endif /*EXTERNALGUI*/
1532
/* Update message count and next available slot number */
1533
if (nummsgs < MAX_MSGS)
1534
msgslot = ++nummsgs;
1537
if (msgslot == MAX_MSGS) msgslot = 0;
1539
/* Calculate the first line to display */
1540
firstmsgn = nummsgs - NUM_LINES;
1541
if (firstmsgn < 0) firstmsgn = 0;
1543
/* Set the display update indicator */
1545
#if defined(EXTERNALGUI) && !defined(OPTION_HTTP_SERVER)
1547
#endif /*EXTERNALGUI*/
1550
/* Check if any sockets have received new connections */
1551
check_socket_devices_for_connections (&readset);
1555
#endif /*EXTERNALGUI*/
1390
/* Read message bytes until newline... */
1392
while (c != '\n' && c != '\r')
1394
/* Initialize the read buffer */
1395
if (!readoff || readoff >= MSG_SIZE) {
1396
memset (readbuf, SPACE, MSG_SIZE);
1400
/* Read message bytes and copy into read buffer
1401
until we either encounter a newline character
1402
or our buffer is completely filled with data. */
1403
while (c != '\n' && c != '\r')
1405
/* Read a byte from the message pipe */
1406
rc = read (pipefd, &c, 1);
1409
_("HHCPN006E message pipe read: %s\n"),
1414
/* Break to process received message
1415
whenever a newline is encountered */
1416
if (c == '\n' || c == '\r') {
1417
readoff = 0; /* (for next time) */
1421
/* Handle tab character */
1424
readoff &= 0xFFFFFFF8;
1425
/* Messages longer than one screen line will
1426
be continued on the very next screen line */
1427
if (readoff >= MSG_SIZE)
1432
/* Eliminate non-displayable characters */
1433
if (!isgraph(c)) c = SPACE;
1435
/* Stuff byte into message processing buffer */
1436
readbuf[readoff++] = c;
1438
/* Messages longer than one screen line will
1439
be continued on the very next screen line */
1440
if (readoff >= MSG_SIZE)
1444
/* If we have a message to be displayed (or a complete
1445
part of one), then copy it to the circular buffer. */
1446
if (!readoff || readoff >= MSG_SIZE) {
1447
/* Set the display update indicator */
1450
memcpy(msgbuf+(msgslot*MSG_SIZE),readbuf,MSG_SIZE);
1452
/* Update message count and next available slot */
1453
if (nummsgs < MAX_MSGS)
1454
msgslot = ++nummsgs;
1457
if (msgslot >= MAX_MSGS) msgslot = 0;
1459
/* Calculate the first line to display */
1460
firstmsgn = nummsgs - NUM_LINES;
1461
if (firstmsgn < 0) firstmsgn = 0;
1463
if (rc < 1) break; /* Exit if read was unsuccessful */
1464
} /* end while read(pipefd) */
1465
if (rc < 1) break; /* Exit if read was unsuccessful */
1466
} /* end if (FD_ISSET(pipefd)) */
1556
1468
/* =NP= : Reinit traditional panel if NP is down */
1557
1469
if (NPDup == 0 && NPDinit == 1) {
1666
1575
#if defined(_FEATURE_SIE)
1667
regs->sie_state ? (long long) regs->hostregs->instcount :
1576
SIE_MODE(regs) ? (long long) regs->hostregs->instcount :
1668
1577
#endif /*defined(_FEATURE_SIE)*/
1669
1578
(long long)regs->instcount,
1672
#endif /*EXTERNALGUI*/
1673
1579
ANSI_ERASE_EOL);
1678
/* SYS / WAIT lights */
1679
if (!(regs->cpustate == CPUSTATE_STOPPING ||
1680
regs->cpustate == CPUSTATE_STOPPED))
1681
fprintf(confp,"SYS=%c\n",pswwait?'0':'1');
1682
#ifdef OPTION_MIPS_COUNTING
1683
/* Calculate MIPS rate */
1684
#ifdef FEATURE_CPU_RECONFIG
1685
for (mipsrate = siosrate = i = 0; i < MAX_CPU_ENGINES; i++)
1686
if(sysblk.regs[i].cpuonline)
1687
#else /*!FEATURE_CPU_RECONFIG*/
1688
for(mipsrate = siosrate = i = 0; i < sysblk.numcpu; i++)
1689
#endif /*!FEATURE_CPU_RECONFIG*/
1691
mipsrate += sysblk.regs[i].mipsrate;
1692
siosrate += sysblk.regs[i].siosrate;
1695
if (mipsrate > 100000) mipsrate = 0; /* ignore wildly high rate */
1698
if (prevmipsrate != mipsrate)
1700
prevmipsrate = mipsrate;
1701
fprintf(confp, "MIPS=%2.1d.%2.2d\n",
1702
prevmipsrate / 1000, (prevmipsrate % 1000) / 10);
1706
if (prevsiosrate != siosrate)
1708
prevsiosrate = siosrate;
1709
fprintf(confp, "SIOS=%5d\n",prevsiosrate);
1712
#endif /*OPTION_MIPS_COUNTING*/
1713
if (gui_gregs) /* GP regs */
1715
fprintf(confp,"GR0-3=%8.8X %8.8X %8.8X %8.8X\n",
1716
regs->GR_L(0),regs->GR_L(1),regs->GR_L(2),regs->GR_L(3));
1717
fprintf(confp,"GR4-7=%8.8X %8.8X %8.8X %8.8X\n",
1718
regs->GR_L(4),regs->GR_L(5),regs->GR_L(6),regs->GR_L(7));
1719
fprintf(confp,"GR8-B=%8.8X %8.8X %8.8X %8.8X\n",
1720
regs->GR_L(8),regs->GR_L(9),regs->GR_L(10),regs->GR_L(11));
1721
fprintf(confp,"GRC-F=%8.8X %8.8X %8.8X %8.8X\n",
1722
regs->GR_L(12),regs->GR_L(13),regs->GR_L(14),regs->GR_L(15));
1725
if (gui_cregs) /* CR regs */
1727
fprintf(confp,"CR0-3=%8.8X %8.8X %8.8X %8.8X\n",
1728
regs->CR_L(0),regs->CR_L(1),regs->CR_L(2),regs->CR_L(3));
1729
fprintf(confp,"CR4-7=%8.8X %8.8X %8.8X %8.8X\n",
1730
regs->CR_L(4),regs->CR_L(5),regs->CR_L(6),regs->CR_L(7));
1731
fprintf(confp,"CR8-B=%8.8X %8.8X %8.8X %8.8X\n",
1732
regs->CR_L(8),regs->CR_L(9),regs->CR_L(10),regs->CR_L(11));
1733
fprintf(confp,"CRC-F=%8.8X %8.8X %8.8X %8.8X\n",
1734
regs->CR_L(12),regs->CR_L(13),regs->CR_L(14),regs->CR_L(15));
1737
if (gui_aregs) /* AR regs */
1739
fprintf(confp,"AR0-3=%8.8X %8.8X %8.8X %8.8X\n",
1740
regs->AR(0),regs->AR(1),regs->AR(2),regs->AR(3));
1741
fprintf(confp,"AR4-7=%8.8X %8.8X %8.8X %8.8X\n",
1742
regs->AR(4),regs->AR(5),regs->AR(6),regs->AR(7));
1743
fprintf(confp,"AR8-B=%8.8X %8.8X %8.8X %8.8X\n",
1744
regs->AR(8),regs->AR(9),regs->AR(10),regs->AR(11));
1745
fprintf(confp,"ARC-F=%8.8X %8.8X %8.8X %8.8X\n",
1746
regs->AR(12),regs->AR(13),regs->AR(14),regs->AR(15));
1749
if (gui_fregs) /* FP regs */
1751
fprintf(confp,"FR0-2=%8.8X %8.8X %8.8X %8.8X\n",
1752
regs->fpr[0],regs->fpr[1],regs->fpr[2],regs->fpr[3]);
1753
fprintf(confp,"FR4-6=%8.8X %8.8X %8.8X %8.8X\n",
1754
regs->fpr[4],regs->fpr[5],regs->fpr[6],regs->fpr[7]);
1757
#if defined(OPTION_MIPS_COUNTING)
1758
if (gui_cpupct) /* CPU Utilization */
1759
fprintf(confp,"CPUPCT=%d\n",(int)(100.0 * regs->cpupct));
1760
#endif /*defined(OPTION_MIPS_COUNTING)*/
1762
if (gui_devlist) /* device status */
1763
gui_devlist_status (confp);
1765
#endif /*EXTERNALGUI*/
1582
ANSI_ROW24_COL1 ANSI_YELLOW_RED
1766
1586
} /* end if(redraw_status) */
1768
else /* !redraw_status */
1770
/* If we're under the control of an external GUI,
1771
some status info we need to send ALL the time. */
1774
/* SYS / WAIT lights */
1775
if (!(regs->cpustate == CPUSTATE_STOPPING ||
1776
regs->cpustate == CPUSTATE_STOPPED))
1777
fprintf(confp,"SYS=%c\n",pswwait?'0':'1');
1779
#if defined(OPTION_MIPS_COUNTING)
1780
if (gui_cpupct) /* CPU Utilization */
1781
fprintf(confp,"CPUPCT=%d\n",(int)(100.0 * regs->cpupct));
1782
#endif /*defined(OPTION_MIPS_COUNTING)*/
1784
if (gui_devlist) /* device status */
1785
gui_devlist_status (confp);
1790
#endif /*EXTERNALGUI*/
1791
1588
if (redraw_cmd)
1793
1590
/* Display the command line */
1852
1640
} /* end function panel_display */
1854
#if defined(EXTERNALGUI)
1856
void gui_devlist_status (FILE *confp)
1860
for (dev = sysblk.firstdev; dev != NULL; dev = dev->nextdev)
1862
if (!(dev->pmcw.flag5 & PMCW5_V)) continue;
1864
stat_online = stat_busy = stat_pend = stat_open = 0;
1866
(dev->hnd->query)(dev, &devclass, sizeof(devnam), devnam);
1868
devnam[255] = 0; /* (ensure null termination) */
1870
if ((dev->console && dev->connected) ||
1871
(strlen(dev->filename) > 0))
1873
if (dev->busy) stat_busy = 1;
1874
if (dev->pending) stat_pend = 1;
1875
if (dev->fd > 2) stat_open = 1;
1877
fprintf(confp, "DEV=%4.4X %4.4X %-4.4s %d%d%d%d %s\n",
1888
fprintf(confp, "DEV=X\n"); /* (indicate end of list) */
1891
#endif /*defined(EXTERNALGUI)*/
1894
/*===================================================================*/
1895
/* S o c k e t D e v i c e s ... */
1896
/*===================================================================*/
1898
// #define DEBUG_SOCKDEV
1900
#ifdef DEBUG_SOCKDEV
1901
#define logdebug(args...) logmsg(## args)
1903
#define logdebug(args...) do {} while (0)
1904
#endif /* DEBUG_SOCKDEV */
1906
/* Linked list of bind structures for bound socket devices */
1908
LIST_ENTRY bind_head; /* (bind_struct list anchor) */
1909
LOCK bind_lock; /* (lock for accessing list) */
1911
/*-------------------------------------------------------------------*/
1912
/* bind_device bind a device to a socket (adds entry to our list */
1913
/* of bound devices) (1=success, 0=failure) */
1914
/*-------------------------------------------------------------------*/
1915
int bind_device (DEVBLK* dev, char* spec)
1919
logdebug("bind_device (%4.4X, %s)\n", dev->devnum, spec);
1921
/* Error if device already bound */
1925
logmsg (_("HHCSD001E Device %4.4X already bound to socket %s\n"),
1926
dev->devnum, dev->bs->spec);
1927
return 0; /* (failure) */
1930
/* Create a new bind_struct entry */
1932
bs = malloc(sizeof(bind_struct));
1936
logmsg (_("HHCSD002E bind_device malloc() failed for device %4.4X\n"),
1938
return 0; /* (failure) */
1941
memset(bs,0,sizeof(bind_struct));
1943
if (!(bs->spec = safe_strdup(spec)))
1945
logmsg (_("HHCSD003E bind_device safe_strdup() failed for device %4.4X\n"),
1948
return 0; /* (failure) */
1951
/* Create a listening socket */
1953
if (bs->spec[0] == '/') bs->sd = unix_socket (bs->spec);
1954
else bs->sd = inet_socket (bs->spec);
1958
/* (error message already issued) */
1960
return 0; /* (failure) */
1963
/* Chain device and socket to each other */
1968
/* Add the new entry to our list of bound devices */
1970
obtain_lock(&bind_lock);
1971
InsertListTail(&bind_head,&bs->bind_link);
1972
release_lock(&bind_lock);
1974
logmsg (_("HHCSD004I Device %4.4X bound to socket %s\n"),
1975
dev->devnum, dev->bs->spec);
1977
return 1; /* (success) */
1980
/*-------------------------------------------------------------------*/
1981
/* unbind_device unbind a device from a socket (removes entry from */
1982
/* our list and discards it) (1=success, 0=failure) */
1983
/*-------------------------------------------------------------------*/
1984
int unbind_device (DEVBLK* dev)
1988
logdebug("unbind_device(%4.4X)\n", dev->devnum);
1990
/* Error if device not bound */
1992
if (!(bs = dev->bs))
1994
logmsg (_("HHCSD005E Device %4.4X not bound to any socket\n"),
1996
return 0; /* (failure) */
1999
/* Error if someone still connected */
2003
logmsg (_("HHCSD006E Client %s (%s) still connected to device %4.4X (%s)\n"),
2004
dev->bs->clientip, dev->bs->clientname, dev->devnum, dev->bs->spec);
2005
return 0; /* (failure) */
2008
/* IMPORTANT! it's bad form to close a listening socket (and it
2009
* happens to crash the Cygwin build) while another thread is still
2010
* listening for connections on that socket (i.e. is in its FD_SET
2011
* 'select' list). Thus we always issue a message (any message)
2012
* immediately AFTER removing the entry from the sockdev (bind_struct)
2013
* list and BEFORE closing our listening socket, thereby forcing
2014
* the panel thread to rebuild its FD_SET 'select' list. (It wakes up
2015
* from its 'select' as a result of our sending it our message and
2016
* then before issuing another 'select' before going back to sleep,
2017
* it then rebuilds its FD_SET 'select' list based on the current
2018
* state of the sockdev (bind_struct) list, and since we just removed
2019
* our entry from that list, the panel thread will thus not add our
2020
* listening socket to its FD_SET 'select' list and thus we can then
2021
* SAFELY close the listening socket).
2024
/* Remove the entry from our list */
2026
obtain_lock(&bind_lock);
2027
RemoveListEntry(&bs->bind_link);
2028
release_lock(&bind_lock);
2030
/* Issue message to wake up panel thread from its 'select' */
2032
logmsg (_("HHCSD007I Device %4.4X unbound from socket %s\n"),
2033
dev->devnum, bs->spec);
2035
/* Give panel thread time to process our message
2036
* and rebuild its select list. */
2040
/* Now safe to close the listening socket */
2045
/* Unchain device and socket from each another */
2050
/* Discard the entry */
2053
free(bs->clientname);
2054
bs->clientname = NULL;
2058
bs->clientip = NULL;
2063
return 1; /* (success) */
2066
/*-------------------------------------------------------------------*/
2067
/* unix_socket create and bind a Unix domain socket */
2068
/*-------------------------------------------------------------------*/
2070
#include <sys/un.h> /* (need "sockaddr_un") */
2072
int unix_socket (char* path)
2074
struct sockaddr_un addr;
2077
logdebug ("unix_socket(%s)\n", path);
2079
if (strlen (path) > sizeof(addr.sun_path) - 1)
2081
logmsg (_("HHCSD008E Socket pathname \"%s\" exceeds limit of %d\n"),
2082
path, (int) sizeof(addr.sun_path) - 1);
2086
addr.sun_family = AF_UNIX;
2087
strcpy (addr.sun_path, path); /* guaranteed room by above check */
2088
sd = socket (PF_UNIX, SOCK_STREAM, 0);
2092
logmsg (_("HHCSD009E Error creating socket for %s: %s\n"),
2093
path, strerror(errno));
2101
|| bind (sd, (struct sockaddr*) &addr, sizeof(addr)) == -1
2102
|| listen (sd, 5) == -1
2105
logmsg (_("HHCSD010E Failed to bind or listen on socket %s: %s\n"),
2106
path, strerror(errno));
2113
/*-------------------------------------------------------------------*/
2114
/* inet_socket create and bind a regular TCP/IP socket */
2115
/*-------------------------------------------------------------------*/
2116
int inet_socket (char* spec)
2118
/* We need a copy of the path to overwrite a ':' with '\0' */
2120
char buf[sizeof(((DEVBLK*)0)->filename)];
2126
struct sockaddr_in sin;
2128
logdebug("inet_socket(%s)\n", spec);
2130
memset(&sin, 0, sizeof(sin));
2131
sin.sin_family = AF_INET;
2133
colon = strchr(buf, ':');
2139
service = colon + 1;
2148
sin.sin_addr.s_addr = INADDR_ANY;
2151
struct hostent* he = gethostbyname(node);
2155
logmsg (_("HHCSD011E Failed to determine IP address from %s\n"),
2160
memcpy(&sin.sin_addr, he->h_addr_list[0], sizeof(sin.sin_addr));
2163
if (isdigit(service[0]))
2165
sin.sin_port = htons(atoi(service));
2169
struct servent* se = getservbyname(service, "tcp");
2173
logmsg (_("HHCSD012E Failed to determine port number from %s\n"),
2178
sin.sin_port = se->s_port;
2181
sd = socket (PF_INET, SOCK_STREAM, 0);
2185
logmsg (_("HHCSD013E Error creating socket for %s: %s\n"),
2186
spec, strerror(errno));
2190
setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
2193
|| bind (sd, (struct sockaddr*) &sin, sizeof(sin)) == -1
2194
|| listen (sd, 5) == -1
2197
logmsg (_("HHCSD014E Failed to bind or listen on socket %s: %s\n"),
2198
spec, strerror(errno));
2205
/*-------------------------------------------------------------------*/
2206
/* add_socket_devices_to_fd_set add all bound socket devices' */
2207
/* listening sockets to the FD_SET */
2208
/*-------------------------------------------------------------------*/
2209
int add_socket_devices_to_fd_set (fd_set* readset, int maxfd)
2213
LIST_ENTRY* pListEntry;
2215
obtain_lock(&bind_lock);
2217
pListEntry = bind_head.Flink;
2219
while (pListEntry != &bind_head)
2221
bs = CONTAINING_RECORD(pListEntry,bind_struct,bind_link);
2223
if (bs->sd != -1) /* if listening for connections, */
2227
if (dev->fd == -1) /* and not already connected, */
2229
FD_SET(bs->sd, readset); /* then add file to set */
2236
pListEntry = pListEntry->Flink;
2239
release_lock(&bind_lock);
2244
/*-------------------------------------------------------------------*/
2245
/* check_socket_devices_for_connections */
2246
/*-------------------------------------------------------------------*/
2247
void check_socket_devices_for_connections (fd_set* readset)
2250
LIST_ENTRY* pListEntry;
2252
obtain_lock(&bind_lock);
2254
pListEntry = bind_head.Flink;
2256
while (pListEntry != &bind_head)
2258
bs = CONTAINING_RECORD(pListEntry,bind_struct,bind_link);
2260
if (bs->sd != -1 && FD_ISSET(bs->sd, readset))
2262
/* Note: there may be other connection requests
2263
* waiting to be serviced, but we'll catch them
2264
* the next time the panel thread calls us. */
2266
release_lock(&bind_lock);
2267
socket_device_connection_handler(bs);
2271
pListEntry = pListEntry->Flink;
2274
release_lock(&bind_lock);
2277
/*-------------------------------------------------------------------*/
2278
/* socket_device_connection_handler */
2279
/*-------------------------------------------------------------------*/
2280
void socket_device_connection_handler (bind_struct* bs)
2282
struct sockaddr_in client; /* Client address structure */
2283
struct hostent* pHE; /* Addr of hostent structure */
2284
socklen_t namelen; /* Length of client structure*/
2285
char* clientip; /* Addr of client ip address */
2286
char* clientname; /* Addr of client hostname */
2287
DEVBLK* dev; /* Device Block pointer */
2288
int csock; /* Client socket */
2292
logdebug("socket_device_connection_handler(dev=%4.4X)\n",
2295
/* Obtain the device lock */
2297
obtain_lock (&dev->lock);
2299
/* Reject if device is busy or interrupt pending */
2301
if (dev->busy || dev->pending || (dev->scsw.flag3 & SCSW3_SC_PEND))
2303
release_lock (&dev->lock);
2304
logmsg (_("HHCSD015E Connect to device %4.4X (%s) rejected; "
2305
"device busy or interrupt pending\n"),
2306
dev->devnum, bs->spec);
2310
/* Reject if previous connection not closed (should not occur) */
2314
release_lock (&dev->lock);
2315
logmsg (_("HHCSD016E Connect to device %4.4X (%s) rejected; "
2316
"client %s (%s) still connected\n"),
2317
dev->devnum, bs->spec, bs->clientip, bs->clientname);
2321
/* Accept the connection... */
2323
csock = accept(bs->sd, 0, 0);
2327
release_lock (&dev->lock);
2328
logmsg (_("HHCSD017E Connect to device %4.4X (%s) failed: %s\n"),
2329
dev->devnum, bs->spec, strerror(errno));
2333
/* Determine the connected client's IP address and hostname */
2335
namelen = sizeof(client);
2337
clientname = "host name unknown";
2340
&& getpeername(csock, (struct sockaddr*) &client, &namelen) == 0
2341
&& (clientip = inet_ntoa(client.sin_addr)) != NULL
2342
&& (pHE = gethostbyaddr((unsigned char*)(&client.sin_addr),
2343
sizeof(client.sin_addr), AF_INET)) != NULL
2344
&& pHE->h_name && *pHE->h_name
2347
clientname = (char*) pHE->h_name;
2350
/* Log the connection */
2354
logmsg (_("HHCSD018I %s (%s) connected to device %4.4X (%s)\n"),
2355
clientip, clientname, dev->devnum, bs->spec);
2359
logmsg (_("HHCSD019I <unknown> connected to device %4.4X (%s)\n"),
2360
dev->devnum, bs->spec);
2363
/* Save the connected client information in the bind_struct */
2365
if (bs->clientip) free(bs->clientip);
2366
if (bs->clientname) free(bs->clientname);
2368
bs->clientip = safe_strdup(clientip);
2369
bs->clientname = safe_strdup(clientname);
2371
/* Indicate that a client is now connected to device (prevents
2372
* listening for new connections until THIS client disconnects).
2375
dev->fd = csock; /* (indicate client connected to device) */
2377
/* Release the device lock */
2379
release_lock (&dev->lock);
2381
/* Raise unsolicited device end interrupt for the device */
2383
device_attention (dev, CSW_DE);
2386
/*-------------------------------------------------------------------*/
2387
/* safe_strdup make copy of string and return a pointer to it */
2388
/*-------------------------------------------------------------------*/
2389
char* safe_strdup (char* str)
2392
if (!str) return NULL;
2393
newstr = malloc (strlen (str) + 1);
2394
if (!newstr) return NULL;
2395
strcpy (newstr, str); /* (guaranteed room) */