First Commit of my working state
[simh.git] / Ibm1130 / utils / diskview.c
CommitLineData
196ba1fc
PH
1/*\r
2 * (C) Copyright 2002, Brian Knittel.\r
3 * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN\r
4 * RISK basis, there is no warranty of fitness for any purpose, and the rest of the\r
5 * usual yada-yada. Please keep this notice and the copyright in any distributions\r
6 * or modifications.\r
7 *\r
8 * This is not a supported product, but I welcome bug reports and fixes.\r
9 * Mail to sim@ibm1130.org\r
10 */\r
11\r
12// DISKVIEW - lists contents of an 1130 system disk image file. Not finished yet.\r
13// needs LET/SLET listing routine.\r
14//\r
15// usage:\r
16// diskview -v diskfile\r
17\r
18#include <stdio.h>\r
19#include <stdlib.h>\r
20#include <string.h>\r
21#include <stdarg.h>\r
22#include "util_io.h"\r
23\r
24#define BETWEEN(v,a,b) (((v) >= (a)) && ((v) <= (b)))\r
25#define MIN(a,b) (((a) <= (b)) ? (a) : (b))\r
26#define MAX(a,b) (((a) >= (b)) ? (a) : (b))\r
27\r
28#ifndef TRUE\r
29# define TRUE 1\r
30# define FALSE 0\r
31# define BOOL int\r
32#endif\r
33\r
34#define NOT_DEF 0x0658 // defective cylinder table entry means no defect\r
35\r
36#define DSK_NUMWD 321 /* words/sector */\r
37#define DSK_NUMCY 203 /* cylinders/drive */\r
38#define DSK_SECCYL 8 /* sectors per cylinder */\r
39#define SECLEN 320 /* data words per sector */\r
40#define SLETLEN ((3*SECLEN)/4) /* length of slet in records */\r
41\r
42typedef unsigned short WORD;\r
43\r
44FILE *fp;\r
45WORD buf[DSK_NUMWD];\r
46WORD dcom[DSK_NUMWD];\r
47\r
48#pragma pack(2)\r
49struct tag_slet {\r
50 WORD phid;\r
51 WORD addr;\r
52 WORD nwords;\r
53 WORD sector;\r
54} slet[SLETLEN];\r
55\r
56#pragma pack()\r
57\r
58WORD dcyl[3];\r
59BOOL verbose = FALSE;\r
60\r
61void checksectors (void);\r
62void dump_id (void);\r
63void dump_dcom (void);\r
64void dump_resmon (void);\r
65void dump_slet (void);\r
66void dump_hdng (void);\r
67void dump_scra (void);\r
68void dump_let (void);\r
69void dump_flet (void);\r
70void dump_cib (void);\r
71void getsector (int sec, WORD *sbuf);\r
72void getdcyl (void);\r
73char *lowcase (char *str);\r
74\r
75void bail(char *fmt, ...);\r
76char *trim (char *s);\r
77\r
78int main (int argc, char **argv)\r
79{\r
80 char *fname = NULL, *arg;\r
81 static char usestr[] = "Usage: diskview [-v] filename";\r
82 int i;\r
83\r
84 for (i = 1; i < argc;) {\r
85 arg = argv[i++];\r
86 if (*arg == '-') {\r
87 arg++;\r
88 lowcase(arg);\r
89 while (*arg) {\r
90 switch (*arg++) {\r
91 case 'v':\r
92 verbose = TRUE;\r
93 break;\r
94\r
95 default:\r
96 bail(usestr);\r
97 }\r
98 }\r
99 }\r
100 else if (fname == NULL)\r
101 fname = arg;\r
102 else\r
103 bail(usestr);\r
104 }\r
105\r
106 if (fname == NULL)\r
107 bail(usestr);\r
108\r
109 if ((fp = fopen(fname, "rb")) == NULL) {\r
110 perror(fname);\r
111 return 2;\r
112 }\r
113\r
114 printf("%s:\n", fname);\r
115\r
116 checksectors();\r
117 getdcyl();\r
118\r
119 dump_id(); // ID & coldstart\r
120 dump_dcom(); // DCOM\r
121 dump_resmon(); // resident image\r
122 dump_slet(); // SLET\r
123 dump_hdng(); // heading sector\r
124 dump_scra();\r
125 dump_flet();\r
126 dump_cib();\r
127 dump_let();\r
128\r
129 fclose(fp);\r
130 return 0;\r
131}\r
132\r
133// checksectors - verify that all sectors are properly numbered\r
134\r
135void checksectors ()\r
136{\r
137 WORD sec = 0;\r
138\r
139 fseek(fp, 0, SEEK_SET);\r
140\r
141 for (sec = 0; sec < DSK_NUMCY*DSK_SECCYL; sec++) {\r
142 if (fxread(buf, sizeof(WORD), DSK_NUMWD, fp) != DSK_NUMWD)\r
143 bail("File read error or not a disk image file");\r
144\r
145 if (buf[0] != sec)\r
146 bail("Sector /%x is misnumbered, run checkdisk [-f]", sec);\r
147 }\r
148}\r
149\r
150// get defective cylinder list\r
151\r
152void getdcyl (void)\r
153{\r
154 fseek(fp, sizeof(WORD), SEEK_SET); // skip sector count\r
155 if (fxread(dcyl, sizeof(WORD), 3, fp) != 3)\r
156 bail("Unable to read defective cylinder table");\r
157}\r
158\r
159// getsector - read specified absolute sector\r
160\r
161void getsector (int sec, WORD *sbuf)\r
162{\r
163 int i, cyl, ssec;\r
164\r
165 sec &= 0x7FF; // mask of drive bits, if any\r
166\r
167 cyl = sec / DSK_SECCYL; // get cylinder\r
168 ssec = sec & ~(DSK_SECCYL-1); // mask to get starting sector of cylinder\r
169 for (i = 0; i < 3; i++) { // map through defective cylinder table\r
170 if (dcyl[i] == ssec) {\r
171 sec &= (DSK_SECCYL-1); // mask to get base sector\r
172 cyl = DSK_NUMCY-3+i; // replacements are last three on disk\r
173 sec += cyl*DSK_SECCYL; // add new cylinder offset\r
174 break;\r
175 }\r
176 }\r
177 // read the sector\r
178 if (fseek(fp, (sec*DSK_NUMWD+1)*sizeof(WORD), SEEK_SET) != 0)\r
179 bail("File seek failed");\r
180\r
181 if (fxread(sbuf, sizeof(WORD), DSK_NUMWD, fp) != DSK_NUMWD)\r
182 bail("File read error or not a disk image file");\r
183}\r
184\r
185void dump (int nwords)\r
186{\r
187 int i, nline = 0;\r
188\r
189 for (i = 0; i < nwords; i++) {\r
190 if (nline == 16) {\r
191 putchar('\n');\r
192 nline = 0;\r
193 }\r
194\r
195 printf("%04x", buf[i]);\r
196 nline++;\r
197 }\r
198 putchar('\n');\r
199}\r
200\r
201void showmajor (char *label)\r
202{\r
203 int i;\r
204\r
205 printf("\n--- %s ", label);\r
206\r
207 for (i = strlen(label); i < 40; i++)\r
208 putchar('-');\r
209\r
210 putchar('\n');\r
211 putchar('\n');\r
212}\r
213\r
214void name (char *label)\r
215{\r
216 printf("%-32.32s ", label);\r
217}\r
218\r
219void pbf (char *label, WORD *buf, int nwords)\r
220{\r
221 int i, nout;\r
222\r
223 name(label);\r
224\r
225 for (i = nout = 0; i < nwords; i++, nout++) {\r
226 if (nout == 8) {\r
227 putchar('\n');\r
228 name("");\r
229 nout = 0;\r
230 }\r
231 printf(" %04x", buf[i]);\r
232 }\r
233\r
234 putchar('\n');\r
235}\r
236\r
237void prt (char *label, char *fmt, ...)\r
238{\r
239 va_list args;\r
240\r
241 name(label);\r
242\r
243 putchar(' ');\r
244 va_start(args, fmt);\r
245 vprintf(fmt, args);\r
246 va_end(args);\r
247\r
248 putchar('\n');\r
249}\r
250\r
251void dump_id (void)\r
252{\r
253 showmajor("Sector 0 - ID & coldstart");\r
254 getsector(0, buf);\r
255\r
256 pbf("DCYL def cyl table", buf+ 0, 3);\r
257 pbf("CIDN cart id", buf+ 3, 1);\r
258 pbf(" copy code", buf+ 4, 1);\r
259 pbf("DTYP disk type", buf+ 7, 1);\r
260 pbf(" diskz copy", buf+ 30, 8);\r
261 pbf(" cold start pgm",buf+270, 8);\r
262}\r
263\r
264// EQUIVALENCES FOR DCOM PARAMETERS \r
265#define NAME 4 // NAME OF PROGRAM/CORE LOAD\r
266#define DBCT 6 // BLOCK CT OF PROGRAM/CORE LOAD\r
267#define FCNT 7 // FILES SWITCH\r
268#define SYSC 8 // SYSTEM/NON-SYSTEM CARTRIDGE INDR\r
269#define JBSW 9 // JOBT SWITCH\r
270#define CBSW 10 // CLB-RETURN SWITCH\r
271#define LCNT 11 // NO. OF LOCALS\r
272#define MPSW 12 // CORE MAP SWITCH\r
273#define MDF1 13 // NO. DUP CTRL RECORDS (MODIF)\r
274#define MDF2 14 // ADDR OF MODIF BUFFER\r
275#define NCNT 15 // NO. OF NOCALS\r
276#define ENTY 16 // RLTV ENTRY ADDR OF PROGRAM\r
277#define RP67 17 // 1442-5 SWITCH\r
278#define TODR 18 // OBJECT WORK STORAGE DRIVE CODE\r
279#define FHOL 20 // ADDR LARGEST HOLE IN FIXED AREA\r
280#define FSZE 21 // BLK CNT LARGEST HOLE IN FXA\r
281#define UHOL 22 // ADDR LAST HOLE IN USER AREA 2-10\r
282#define USZE 23 // BLK CNT LAST HOLE IN UA 2-10\r
283#define DCSW 24 // DUP CALL SWITCH\r
284#define PIOD 25 // PRINCIPAL I/O DEVICE INDICATOR\r
285#define PPTR 26 // PRINCIPAL PRINT DEVICE INDICATOR\r
286#define CIAD 27 // RLTV ADDR IN @STRT OF CIL ADDR\r
287#define ACIN 28 // AVAILABLE CARTRIDGE INDICATOR\r
288#define GRPH 29 // 2250 INDICATOR 2G2\r
289#define GCNT 30 // NO. G2250 RECORDS 2G2\r
290#define LOSW 31 // LOCAL-CALLS-LOCAL SWITCH 2-2\r
291#define X3SW 32 // SPECIAL ILS SWITCH 2-2\r
292#define ECNT 33 // NO. OF *EQUAT RCDS 2-4\r
293#define ANDU 35 // 1+BLK ADDR END OF UA (ADJUSTED)\r
294#define BNDU 40 // 1+BLK ADDR END OF UA (BASE)\r
295#define FPAD 45 // FILE PROTECT ADDR\r
296#define PCID 50 // CARTRIDGE ID, PHYSICAL DRIVE\r
297#define CIDN 55 // CARTRIDGE ID, LOGICAL DRIVE\r
298#define CIBA 60 // SCTR ADDR OF CIB\r
299#define SCRA 65 // SCTR ADDR OF SCRA\r
300#define FMAT 70 // FORMAT OF PROG IN WORKING STG\r
301#define FLET 75 // SCTR ADDR 1ST SCTR OF FLET\r
302#define ULET 80 // SCTR ADDR 1ST SCTR OF LET\r
303#define WSCT 85 // BLK CNT OF PROG IN WORKING STG\r
304#define CSHN 90 // NO. SCTRS IN CUSHION AREA\r
305\r
306struct tag_dcominfo {\r
307 char *nm;\r
308 int offset;\r
309 char *descr;\r
310} dcominfo[] = {\r
311 "NAME", 4, "NAME OF PROGRAM/CORE LOAD",\r
312 "DBCT", 6, "BLOCK CT OF PROGRAM/CORE LOAD",\r
313 "FCNT", 7, "FILES SWITCH",\r
314 "SYSC", 8, "SYSTEM/NON-SYSTEM CARTRIDGE INDR",\r
315 "JBSW", 9, "JOBT SWITCH",\r
316 "CBSW", 10, "CLB-RETURN SWITCH",\r
317 "LCNT", 11, "NO. OF LOCALS",\r
318 "MPSW", 12, "CORE MAP SWITCH",\r
319 "MDF1", 13, "NO. DUP CTRL RECORDS (MODIF)",\r
320 "MDF2", 14, "ADDR OF MODIF BUFFER",\r
321 "NCNT", 15, "NO. OF NOCALS",\r
322 "ENTY", 16, "RLTV ENTRY ADDR OF PROGRAM",\r
323 "RP67", 17, "1442-5 SWITCH",\r
324 "TODR", 18, "OBJECT WORK STORAGE DRIVE CODE",\r
325 "FHOL", 20, "ADDR LARGEST HOLE IN FIXED AREA",\r
326 "FSZE", 21, "BLK CNT LARGEST HOLE IN FXA",\r
327 "UHOL", 22, "ADDR LAST HOLE IN USER AREA",\r
328 "USZE", 23, "BLK CNT LAST HOLE IN UA",\r
329 "DCSW", 24, "DUP CALL SWITCH",\r
330 "PIOD", 25, "PRINCIPAL I/O DEVICE INDICATOR",\r
331 "PPTR", 26, "PRINCIPAL PRINT DEVICE INDICATOR",\r
332 "CIAD", 27, "RLTV ADDR IN @STRT OF CIL ADDR",\r
333 "ACIN", 28, "AVAILABLE CARTRIDGE INDICATOR",\r
334 "GRPH", 29, "2250 INDICATOR",\r
335 "GCNT", 30, "NO. G2250 RECORDS",\r
336 "LOSW", 31, "LOCAL-CALLS-LOCAL SWITCH",\r
337 "X3SW", 32, "SPECIAL ILS SWITCH",\r
338 "ECNT", 33, "NO. OF *EQUAT RCDS",\r
339 "ANDU", 35, "1+BLK ADDR END OF UA (ADJUSTED)",\r
340 "BNDU", 40, "1+BLK ADDR END OF UA (BASE)",\r
341 "FPAD", 45, "FILE PROTECT ADDR",\r
342 "PCID", 50, "CARTRIDGE ID, PHYSICAL DRIVE",\r
343 "CIDN", 55, "CARTRIDGE ID, LOGICAL DRIVE",\r
344 "CIBA", 60, "SCTR ADDR OF CIB",\r
345 "SCRA", 65, "SCTR ADDR OF SCRA",\r
346 "FMAT", 70, "FORMAT OF PROG IN WORKING STG",\r
347 "FLET", 75, "SCTR ADDR 1ST SCTR OF FLET",\r
348 "ULET", 80, "SCTR ADDR 1ST SCTR OF LET",\r
349 "WSCT", 85, "BLK CNT OF PROG IN WORKING STG",\r
350 "CSHN", 90, "NO. SCTRS IN CUSHION AREA",\r
351 NULL\r
352};\r
353\r
354void dump_dcom (void)\r
355{\r
356 struct tag_dcominfo *d;\r
357 char txt[50];\r
358\r
359 showmajor("Sector 1 - DCOM");\r
360 getsector(1, dcom);\r
361\r
362 for (d = dcominfo; d->nm != NULL; d++) {\r
363 sprintf(txt, "%-4.4s %s", d->nm, d->descr);\r
364 pbf(txt, dcom+d->offset, 1);\r
365 }\r
366}\r
367\r
368void dump_resmon (void)\r
369{\r
370 showmajor("Sector 2 - Resident Image");\r
371 getsector(2, buf);\r
372 dump(verbose ? SECLEN : 32);\r
373}\r
374\r
375struct {\r
376 int pfrom, pto;\r
377 int printed;\r
378 char *name;\r
379} sletinfo[] = {\r
380 0x01, 0x12, FALSE, "DUP",\r
381 0x1F, 0x39, FALSE, "Fortran",\r
382 0x51, 0x5C, FALSE, "Cobol",\r
383 0x6E, 0x74, FALSE, "Supervisor",\r
384 0x78, 0x84, FALSE, "Core Load Builder",\r
385 0x8C, 0x8C, FALSE, "Sys 1403 prt",\r
386 0x8D, 0x8D, FALSE, "Sys 1132 prt",\r
387 0x8E, 0x8E, FALSE, "Sys console prt",\r
388 0x8F, 0x8F, FALSE, "Sys 2501 rdr",\r
389 0x90, 0x90, FALSE, "Sys 1442 rdr/pun",\r
390 0x91, 0x91, FALSE, "Sys 1134 paper tape",\r
391 0x92, 0x92, FALSE, "Sys kbd",\r
392 0x93, 0x93, FALSE, "Sys 2501/1442 conv",\r
393 0x94, 0x94, FALSE, "Sys 1134 conv",\r
394 0x95, 0x95, FALSE, "Sys kbd conv",\r
395 0x96, 0x96, FALSE, "Sys diskz",\r
396 0x97, 0x97, FALSE, "Sys disk1",\r
397 0x98, 0x98, FALSE, "Sys diskn",\r
398 0x99, 0x99, FALSE, "(primary print)",\r
399 0x9A, 0x9A, FALSE, "(primary input)",\r
400 0x9B, 0x9B, FALSE, "(primary input excl kbd)",\r
401 0x9C, 0x9C, FALSE, "(primary sys conv)",\r
402 0x9D, 0x9D, FALSE, "(primary conv excl kbd)",\r
403 0xA0, 0xA1, FALSE, "Core Image Loader",\r
404 0xB0, 0xCC, FALSE, "RPG",\r
405 0xCD, 0xCE, FALSE, "Dup Part 2",\r
406 0xCF, 0xF6, FALSE, "Macro Assembler",\r
407 0\r
408};\r
409\r
410void dump_slet (void)\r
411{\r
412 int i, j, iphase, nsecs, sec, max_sec = 0;\r
413 char sstr[16], *smark;\r
414\r
415 showmajor("Sectors 3-5 - SLET");\r
416 for (i = 0; i < 3; i++) {\r
417 getsector(3+i, buf);\r
418 memmove(((WORD *) slet)+SECLEN*i, buf, SECLEN*sizeof(WORD));\r
419 }\r
420\r
421 printf("# PHID Addr Len Sector Secs\n");\r
422 printf("------------------------------------------\n");\r
423 for (i = 0; i < SLETLEN; i++) {\r
424 if (slet[i].phid == 0)\r
425 break;\r
426\r
427 sec = slet[i].sector;\r
428 iphase = (int) (signed short) slet[i].phid;\r
429 nsecs = (slet[i].nwords + SECLEN-1)/SECLEN;\r
430\r
431 if (sec & 0xF800) {\r
432 smark = "*";\r
433 sec &= 0x7FF;\r
434 }\r
435 else\r
436 smark = " ";\r
437\r
438 for (j = 0; sletinfo[j].pfrom != 0; j++)\r
439 if (sletinfo[j].pfrom <= iphase && sletinfo[j].pto >= iphase)\r
440 break;\r
441\r
442 sprintf(sstr, "(%d.%d)", sec / DSK_SECCYL, slet[i].sector % DSK_SECCYL);\r
443\r
444 printf("%3d %04x %4d %04x %04x %04x %s %-7s %3x",\r
445 i, slet[i].phid, iphase, slet[i].addr, slet[i].nwords, slet[i].sector, smark, sstr, nsecs);\r
446\r
447 if (iphase < 0)\r
448 iphase = -iphase;\r
449\r
450 if (sletinfo[j].pfrom == 0)\r
451 printf(" ???");\r
452 else if (! sletinfo[j].printed) {\r
453 printf(" %s", sletinfo[j].name);\r
454 sletinfo[j].printed = TRUE;\r
455 }\r
456\r
457 for (j = 0; j < i; j++) {\r
458 if (sec == (slet[j].sector & 0x7FF)) {\r
459 printf(" (same as %04x)", slet[j].phid);\r
460 break;\r
461 }\r
462 }\r
463\r
464 max_sec = MAX(max_sec, sec+nsecs-1); // find last sector used\r
465\r
466 putchar('\n');\r
467\r
468 if (i >= 15 && ! verbose) {\r
469 printf("...\n");\r
470 break;\r
471 }\r
472 }\r
473}\r
474\r
475int ascii_to_ebcdic_table[128] = \r
476{\r
477 0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f, 0x16,0x05,0x25,0x0b,0x0c,0x0d,0x0e,0x0f,\r
478 0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26, 0x18,0x19,0x3f,0x27,0x1c,0x1d,0x1e,0x1f,\r
479 0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d, 0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61,\r
480 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, 0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f,\r
481\r
482 0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, 0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,\r
483 0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6, 0xe7,0xe8,0xe9,0xba,0xe0,0xbb,0xb0,0x6d,\r
484 0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96,\r
485 0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6, 0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07,\r
486};\r
487\r
488int ebcdic_to_ascii (int ch)\r
489{\r
490 int j;\r
491\r
492 for (j = 32; j < 128; j++)\r
493 if (ascii_to_ebcdic_table[j] == ch)\r
494 return j;\r
495\r
496 return '?';\r
497}\r
498\r
499#define HDR_LEN 120\r
500\r
501void dump_hdng(void)\r
502{\r
503 int i;\r
504 char str[HDR_LEN+1], *p = str;\r
505\r
506 showmajor("Sector 7 - Heading");\r
507 getsector(7, buf);\r
508\r
509 for (i = 0; i < (HDR_LEN/2); i++) {\r
510 *p++ = ebcdic_to_ascii((buf[i] >> 8) & 0xFF);\r
511 *p++ = ebcdic_to_ascii( buf[i] & 0xFF);\r
512 }\r
513\r
514 *p = '\0';\r
515 trim(str);\r
516 printf("%s\n", str);\r
517}\r
518\r
519BOOL mget (int offset, char *name)\r
520{\r
521 char title[80];\r
522\r
523 if (dcom[offset] == 0)\r
524 return FALSE;\r
525\r
526 getsector(dcom[offset], buf);\r
527 sprintf(title, "Sector %x - %s", dcom[offset], name);\r
528 showmajor(title);\r
529 return TRUE;\r
530}\r
531\r
532void dump_scra (void)\r
533{\r
534 if (! mget(SCRA, "SCRA"))\r
535 return;\r
536\r
537 dump(verbose ? SECLEN : 32);\r
538}\r
539\r
540void dump_let (void)\r
541{\r
542 if (! mget(ULET, "LET"))\r
543 return;\r
544}\r
545\r
546void dump_flet (void)\r
547{\r
548 if (! mget(FLET, "FLET"))\r
549 return;\r
550}\r
551\r
552void dump_cib (void)\r
553{\r
554 if (! mget(CIBA, "CIB"))\r
555 return;\r
556\r
557 dump(verbose ? SECLEN : 32);\r
558}\r
559\r
560#define LFHD 5 // WORD COUNT OF LET/FLET HEADER PMN09970\r
561#define LFEN 3 // NO OF WDS PER LET/FLET ENTRY PMN09980\r
562#define SCTN 0 // RLTY ADDR OF LET/FLET SCTR NO. PMN09990\r
563#define UAFX 1 // RLTV ADDR OF SCTR ADDR OF UA/FXA PMN10000\r
564#define WDSA 3 // RLTV ADDR OF WDS AVAIL IN SCTR PMN10010\r
565#define NEXT 4 // RLTV ADDR OF ADDR NEXT SCTR PMN10020\r
566#define LFNM 0 // RLTV ADDR OF LET/FLET ENTRY NAME PMN10030\r
567#define BLCT 2 // RLTV ADDR OF LET/FLET ENTRY DBCT PMN10040\r
568\r
569void bail (char *fmt, ...)\r
570{\r
571 va_list args;\r
572\r
573 va_start(args, fmt);\r
574 fprintf(stderr, fmt, args);\r
575 va_end(args);\r
576 putchar('\n');\r
577\r
578 exit(1);\r
579}\r
580\r
581// ---------------------------------------------------------------------------------\r
582// trim - remove trailing whitespace from string s\r
583// ---------------------------------------------------------------------------------\r
584\r
585char *trim (char *s)\r
586{\r
587 char *os = s, *nb;\r
588\r
589 for (nb = s-1; *s; s++)\r
590 if (*s > ' ')\r
591 nb = s;\r
592\r
593 nb[1] = '\0';\r
594 return os;\r
595}\r
596\r
597/* ------------------------------------------------------------------------ \r
598 * lowcase - force a string to lowercase (ASCII)\r
599 * ------------------------------------------------------------------------ */\r
600\r
601char *lowcase (char *str)\r
602{\r
603 char *s;\r
604\r
605 for (s = str; *s; s++) {\r
606 if (*s >= 'A' && *s <= 'Z')\r
607 *s += 32;\r
608 } \r
609\r
610 return str;\r
611}\r
612\r