Link with C++ linker
[oweals/cde.git] / cde / programs / dtsr / tomita.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *   COMPONENT_NAME: austext
25  *
26  *   FUNCTIONS: browser
27  *              change_database
28  *              deleter
29  *              kill_delete
30  *              load_dbatab
31  *              main
32  *              parse_infbuf
33  *              print_exit_code
34  *              retncode_abort
35  *
36  *   ORIGINS: 27
37  *
38  *   (C) COPYRIGHT International Business Machines Corp. 1992,1995
39  *   All Rights Reserved
40  *   US Government Users Restricted Rights - Use, duplication or
41  *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
42  */
43 /******************* TOMITA.C *******************
44  * $TOG: tomita.c /main/9 1998/04/17 11:23:38 mgreess $
45  * May 1992.
46  * Replaces original tomita but removes curses dependencies
47  * and uses Opera Engine for deletes rather than hard
48  * coding vista calls.
49  * 
50  * In effect, tomita is two programs.
51  * Program #1 browses an input list of opera record ids and
52  * prompts user to confirm that they should be deleted from opera.
53  * The confirmed list is written to an output file that is identical in format.
54  * Program 1 can be run anytime because it only reads the database.
55  * 
56  * Program #2 deletes record from an input list, presumably
57  * the output of program #1.  Program #2 writes to vista and changes it.
58  * It MUST be run offline when no users are logged into opera,
59  * in order to prevent database corruption.
60  * Currently password is passed on command line.
61  * The password is maintained in an encrypted flat file.
62  * It can be changed from an undocumented Opera Engine function
63  * available in tuiopera.
64  * 
65  * RECORD ID FILE FORMAT (shdscrd.lst format):
66  * One record to be deleted per line.
67  * 3 words per line separated by whitespace, everything thereafter is comments.
68  * (These programs only use the first 2 words).
69  * All words may be optionally enclosed in double quotes
70  * to capture embedded blanks ("xxx xxx").
71  *      ...
72  *      databasename  recordid  userid  comments...\n
73  *      ...
74  *
75  * $Log$
76  * Revision 2.2  1995/10/25  15:21:36  miker
77  * Added prolog.
78  *
79  * Revision 2.1  1995/09/22  22:17:11  miker
80  * Freeze DtSearch 0.1, AusText 2.1.8
81  *
82  * Revision 1.11  1995/09/05  19:14:39  miker
83  * Removed password requirement.  DtSearch header file and function
84  * name changes.  Made usrblk universal global.
85  */
86 #include "SearchE.h"
87 #include <string.h>
88 #include <ctype.h>
89 #include <signal.h>
90 #include <sys/stat.h>
91 #include <locale.h>
92
93 #define PRINT_MESSAGES \
94     { puts (DtSearchGetMessages()); DtSearchFreeMessages(); }
95 #define TOKEN_DELIM     " \t\n"
96 #define PAUSE_ROWS      15
97 #define DBACOUNT        2000
98 #define PROGNAME        "TOMITA"
99 #define MS_tomita       29
100 #define MS_misc         1
101
102 /*------------------ GLOBALS ------------------*/
103 static int      debug_mode =    FALSE;
104 static int      prog =          'B';    /* 'D' = deleting, 'B' = browsing */
105 static int      shutdown_now =  FALSE;
106 static int      yesarg =        FALSE;
107 static int      retncode =      0;
108 static int      max_dbacount =  DBACOUNT;
109 static FILE     *inf, *outf;
110 static char     *infname;
111 static char     *outfname;
112 static long     maxtime;
113 static long     records_read;
114 static char     parsed_dbname [24];
115 static DBLK     *parsed_dblk;
116
117 char            parsed_recid [2048];
118
119
120 /************************************************/
121 /*                                              */
122 /*               print_exit_code                */
123 /*                                              */
124 /************************************************/
125 /* Called from inside DtSearchExit() at austext_exit_last */
126 static void     print_exit_code (int exit_code)
127 {
128     printf ( catgets(dtsearch_catd, MS_tomita, 3,
129         "%s: Exit Code = %d.\n") ,
130         aa_argv0, exit_code);
131     return;
132 } /* print_exit_code() */
133
134
135 /************************************************/
136 /*                                              */
137 /*                 kill_delete                  */
138 /*                                              */
139 /************************************************/
140 /* Interrupt handler for all termination signals
141  * in Delete mode.  Just sets global flag so we
142  * can come down gracefully between deletions.
143  */
144 static void     kill_delete (int sig)
145 {
146     shutdown_now = TRUE;
147     printf ( catgets(dtsearch_catd, MS_tomita, 1,
148         "\n%s Received interrupt %d.\n"
149         "  Program will stop after current batch of deletions.\n") ,
150         PROGNAME"069", sig);
151     return;
152 }  /* kill_delete() */
153
154
155 /************************************************/
156 /*                                              */
157 /*              retncode_abort                  */
158 /*                                              */
159 /************************************************/
160 static void     retncode_abort (int location)
161 {
162     fputc ('\n', aa_stderr);
163     if (DtSearchHasMessages ())
164         fprintf (aa_stderr, "%s\n", DtSearchGetMessages ());
165     fprintf (aa_stderr,
166         PROGNAME "%d Program abort.  usrblk.retncode = %d.  Exit code = 3.\n",
167         location, usrblk.retncode);
168     DtSearchExit (3);
169 }  /* retncode_abort() */
170
171
172 /****************************************/
173 /*                                      */
174 /*           change_database            */
175 /*                                      */
176 /****************************************/
177 /* Changes usrblk.dblk to point to passed database name.
178  * Returns TRUE if successful.
179  */
180 static int      change_database (char *newname)
181 {
182     DBLK           *db;
183
184     for (db = usrblk.dblist; db != NULL; db = db->link)
185         if (strcmp (db->name, newname) == 0) {
186             usrblk.dblk = db;
187             return TRUE;
188         }
189
190     /* Invalid newname.  If deleting, just say which database is invalid. */
191     retncode = 1;
192     fprintf (aa_stderr,  catgets(dtsearch_catd, MS_tomita, 4,
193         "%s Database '%s' not found.\n") ,
194         PROGNAME"114", newname);
195     if (prog == 'D')
196         return FALSE;
197
198     /* If browsing, tell user his options */
199     fprintf (aa_stderr,  catgets(dtsearch_catd, MS_tomita, 5,
200         "Available choices are:") );
201     for (db = usrblk.dblist; db != NULL; db = db->link)
202         fprintf (aa_stderr, "   '%s'", db->name);
203     fputc ('\n', aa_stderr);
204     return FALSE;
205 }  /* change_database() */
206
207
208 /****************************************/
209 /*                                      */
210 /*           parse_infbuf               */
211 /*                                      */
212 /****************************************/
213 /* Parses a line from a standard formatted discard file.
214  * If first word indicates different database from usrblk.dblk,
215  * changes it.  First token loaded into parsed_dbname
216  * (and parsed_dblk will be made to track it), and
217  * second token is loaded into parsed_recid.
218  * Tokens are separated by blanks and/or tabs, 
219  * except 2nd token may have embedded spaces if it is
220  * surrounded by double quotes.  Returns TRUE unless
221  * database couldn't change or other error, then returns FALSE.
222  */
223 static int      parse_infbuf (char *infbuf)
224 {
225     char           *ptr;
226     char            mybuf[1024];
227
228     /* Do all parsing in my own buf so infbuf not peppered with \0's */
229     strncpy (mybuf, infbuf, sizeof (mybuf));
230     mybuf[sizeof (mybuf) - 1] = 0;
231
232     /* Parse first token (database name) */
233     if ((ptr = strtok (mybuf, " \t")) == NULL) {
234         /* Msg #8 is used in two places */
235         fprintf (aa_stderr, catgets(dtsearch_catd, MS_tomita, 8,
236             "%s Invalid input format: %.30s...\n") ,
237             PROGNAME"152", infbuf);
238         retncode = 1;
239         return FALSE;
240     }
241
242     /* Change database if necessary */
243     if (strcmp (ptr, usrblk.dblk->name) != 0)
244         if (!change_database (ptr)) {
245             retncode = 1;
246             return FALSE;
247         }
248
249     strcpy (parsed_dbname, ptr);
250     parsed_dblk = usrblk.dblk;
251
252     /* Hop over to beginning of 2nd token */
253     for (ptr += strlen (ptr) + 1; *ptr == ' ' || *ptr == '\t'; ptr++);
254
255     /* Get 2nd token (record id).  Token delimiters depend
256      * on whether token begins with a double quote.
257      */
258     ptr = strtok (ptr, (*ptr == '\"') ? "\"" : " \t");
259     if (ptr == NULL) {
260         /* Msg #8 is used in two places */
261         fprintf (aa_stderr, catgets(dtsearch_catd, MS_tomita, 8,
262             "%s Invalid input format: %.30s...\n") ,
263             PROGNAME"176", infbuf);
264         retncode = 1;
265         return FALSE;
266     }
267     strncpy (parsed_recid, ptr, sizeof (parsed_recid));
268     parsed_recid[sizeof (parsed_recid) - 1] = 0;
269
270     return TRUE;
271 }  /* parse_infbuf() */
272
273
274 /****************************************/
275 /*                                      */
276 /*               browser                */
277 /*                                      */
278 /****************************************/
279 /* Program 1: displays records in input file,
280  * or user selected records, and if confirmed,
281  * writes their record ids to output file.
282  */
283 static int      browser (void)
284 {
285     int         done = FALSE;
286     int         pausing = FALSE;
287     int         redisplay_rec = FALSE;
288     int         pause_counter;
289     time_t      stamp;
290     LLIST       *llptr;
291     char        *ptr;
292     char        datestr[32];    /* "1946/04/17 13:03" */
293     char        userbuf[1024];
294     char        infbuf[1024];
295
296     /* All writes to output file will have same date string in comment */
297     time (&stamp);
298     strftime (datestr, sizeof (datestr), "%Y/%m/%d %H:%M", localtime (&stamp));
299
300     /* Main menu loop */
301     while (!done) {
302         if (DtSearchHasMessages ()) {
303             putchar ('\n');
304             PRINT_MESSAGES
305         }
306
307         /* Write main menu prompt */
308         printf ( catgets(dtsearch_catd, MS_tomita, 10,
309             "\n---------- SHOW NEXT RECORD ----------- Database = '%s'\n"
310             "q      QUIT.                        Current Record Count = %ld\n"
311             "p      Toggle PAUSE from %s.\n"
312             "n      NEXT INPUT file record.\n"
313             "+      NEXT SEQUENTIAL database record.\n"
314             "-      PRIOR SEQUENTIAL database record.\n"
315             "r      REDISPLAY current record '%s'.\n"
316             "x      CONFIRM DELETION of current record.\n"
317             "dxxx   Change DATABASE to xxx.\n"
318             "\"xxx   GET record id xxx (embedded blanks are ok).\n"
319             "> ") ,
320             usrblk.dblk->name,
321             usrblk.dblk->dbrec.or_reccount,
322             (pausing) ? "on to OFF" : "off to ON",
323             usrblk.objrec.or_objkey
324             );
325
326         /* Read user's response.  Remove user's \n. */
327         *userbuf = '\0';
328         if ((fgets (userbuf, sizeof (userbuf), stdin)) == NULL) break;
329         if (strlen(userbuf) && userbuf[strlen(userbuf)-1] == '\n')
330           userbuf[strlen(userbuf)-1] = '\0';
331
332         putchar ('\n');
333
334         /* depending on response, get database address into usrblk */
335         redisplay_rec = FALSE;
336         switch (tolower (*userbuf)) {
337             case 'q':
338                 done = TRUE;
339                 break;
340
341             case 'd':
342                 change_database (userbuf + 1);
343                 continue;
344                 break;
345
346             case 'p':
347                 pausing = !pausing;
348                 continue;
349                 break;
350
351             case 'r':
352                 if (usrblk.objrec.or_objkey[0] == 0) {
353                     fprintf (aa_stderr,
354                         catgets(dtsearch_catd, MS_tomita, 11,
355                         "%s Record buffer empty.\n"),
356                         PROGNAME"267");
357                     continue;
358                 }
359                 redisplay_rec = FALSE;
360                 break;
361
362             case '+':
363             case '-':
364                 usrblk.request = (*userbuf == '+') ? OE_NEXT_DBA : OE_PREV_DBA;
365                 Opera_Engine ();
366                 break;
367
368             case 'n':
369                 if (inf == NULL) {
370                     fprintf (aa_stderr,
371                         catgets(dtsearch_catd, MS_tomita, 12,
372                         "%s Input file unavailable.\n"),
373                         PROGNAME"282");
374                     continue;
375                 }
376                 *infbuf = '\0';
377                 if ((fgets (infbuf, sizeof (infbuf), inf)) == NULL)
378                 {
379                     fprintf (aa_stderr,
380                         catgets(dtsearch_catd, MS_tomita, 13,
381                         "%s No more records in input file.\n"),
382                         PROGNAME"288");
383                     fclose (inf);
384                     inf = NULL;
385                     continue;
386                 }
387
388                 if (strlen(infbuf) && infbuf[strlen(infbuf)-1] == '\n')
389                   infbuf[strlen(infbuf)-1] = '\0';
390
391                 if (!parse_infbuf (infbuf))
392                     continue;
393                 usrblk.request = OE_RECKEY2DBA;
394                 usrblk.query = parsed_recid;
395                 Opera_Engine ();
396                 break;
397
398             case '\"':
399                 ptr = strtok (userbuf, "\"");
400                 if (ptr == NULL || *ptr == 0) {
401                     fprintf (aa_stderr,
402                         catgets(dtsearch_catd, MS_tomita, 14,
403                         "%s Invalid Record ID.\n"),
404                         PROGNAME"303");
405                     continue;
406                 }
407                 usrblk.request = OE_RECKEY2DBA;
408                 usrblk.query = ptr;
409                 Opera_Engine ();
410                 break;
411
412             case 'x':
413                 /*
414                  * Write record id to output file. Format:
415                  * dbasename "recid" userid comments(date)... 
416                  */
417                 fprintf (outf, DISCARD_FORMAT, usrblk.dblk->name,
418                     usrblk.objrec.or_objkey, usrblk.userid, datestr);
419                 printf ( catgets(dtsearch_catd, MS_tomita, 15,
420                     "%s '%s' appended to file of confirmed deletions.\n") ,
421                     PROGNAME"317", usrblk.objrec.or_objkey);
422                 continue;
423
424             default:
425                 printf (catgets(dtsearch_catd, MS_tomita, 16, "...what?\n"));
426                 continue;
427         }       /* end switch */
428
429         if (done)
430             break;
431
432         /* if user requested redisplay, skip the following OE code */
433         if (redisplay_rec)
434             goto DISPLAY_RECORD;
435
436         /*
437          * check return code from attempt to get opera database
438          * address 
439          */
440         if (usrblk.retncode == OE_WRAPPED)
441             fprintf (aa_stderr, catgets(dtsearch_catd, MS_tomita, 17,
442                 "%s %s Engine wrapped to next record.\n") ,
443                 PROGNAME"333", OE_prodname);
444         else if (usrblk.retncode != OE_OK)
445             retncode_abort (334);
446
447         /* retrieve the record and uncompress it */
448         usrblk.request = OE_GETREC;
449         Opera_Engine ();
450         if (usrblk.retncode != OE_OK)
451             retncode_abort (339);
452
453 DISPLAY_RECORD:
454         /* display the record's cleartext, character by character */
455         printf ( catgets(dtsearch_catd, MS_tomita, 18,
456             "\n\n"
457             "Record:    '%s'\n"
458             "Abstract:  '%s'\n"
459             "--------------------------------------\n") ,
460             usrblk.objrec.or_objkey,
461             (usrblk.abstrbufsz > 0) ? usrblk.abstrbuf :
462                                       catgets (dtsearch_catd, MS_misc, 1, "<null>"));
463
464         pause_counter = 0;
465         for (ptr = usrblk.cleartext; *ptr != 0; ptr++) {
466             putchar (*ptr);
467             /*
468              * pause every so many lines so user can browse the
469              * output 
470              */
471             if (pausing && *ptr == '\n') {
472                 if (++pause_counter >= PAUSE_ROWS) {
473                     /* Msg 21 is used in two places */
474                     printf ( catgets(dtsearch_catd, MS_tomita, 21,
475                         "\n...push ENTER to continue... ") );
476
477                     *userbuf = '\0';
478                     fgets (userbuf, sizeof (userbuf), stdin);
479                     if (strlen(userbuf) && userbuf[strlen(userbuf)-1] == '\n')
480                       userbuf[strlen(userbuf)-1] = '\0';
481
482                     putchar ('\n');
483                     pause_counter = 0;
484                 }
485             }
486         }       /* end of cleartext printing */
487
488         /* display the user notes if any, character by character */
489         if (usrblk.notes != NULL) {
490             printf ( catgets(dtsearch_catd, MS_tomita, 20,
491                 "--------------------------------------\n"
492                 "End of Text Blob for '%s':\n\n"
493                 "User Notes:\n"
494                 "--------------------------------------\n") ,
495                 usrblk.objrec.or_objkey);
496             pause_counter += 5;
497             for (llptr = usrblk.notes; llptr != NULL; llptr = llptr->link) {
498                 for (ptr = llptr->data; *ptr != '\0'; ptr++) {
499                     putchar (*ptr);
500                     if (pausing && *ptr == '\n')
501                         if (++pause_counter >= PAUSE_ROWS) {
502                             /* Msg 21 is used in two places */
503                             printf ( catgets(dtsearch_catd, MS_tomita, 21,
504                                 "\n...push ENTER to continue... ") );
505
506                             *userbuf = '\0';
507                             fgets (userbuf, sizeof (userbuf), stdin);
508                             if (strlen(userbuf) &&
509                                 userbuf[strlen(userbuf)-1] == '\n')
510                               userbuf[strlen(userbuf)-1] = '\0';
511
512                             putchar ('\n');
513                             pause_counter = 0;
514                         }
515                 }
516             }
517         }       /* end of user notes printing */
518
519         printf ("--------------------------------------\n"
520             "End of Record '%s'.\n", usrblk.objrec.or_objkey);
521
522     }   /* end of main menu loop */
523     return 0;
524 }  /* browser() */
525
526
527 /****************************************/
528 /*                                      */
529 /*              load_dbatab             */
530 /*                                      */
531 /****************************************/
532 /* Subroutine of deleter().  Reads discard file containing
533  * record ids to be deleted, converts to database addresses,
534  * loads usrblk.dbatab up to max batch size.
535  * Returns number of dba's added to table.
536  * Returns 0 when file is empty after last batch.
537  */
538 static int      load_dbatab (void)
539 {
540     static int      read_next_rec = TRUE;
541     static char     last_dbname[24] = "";
542     static DBLK    *last_dblk;
543     DB_ADDR        *next_dba;
544     char            buf[1024];
545     int             first_err = TRUE;
546
547     if (inf == NULL)
548         return 0;
549
550     usrblk.dbacount = 0;
551     next_dba = usrblk.dbatab;
552
553 KEEP_READING:
554     /* MAIN LOOP - break it at EOF, max count, or dbname change */
555     while (usrblk.dbacount < max_dbacount) {
556         /*
557          * Skip the read of the first record if the reason we left
558          * main loop the last time was because of a database name
559          * change, and the data from the last read is still in
560          * parsed_dbname, _dblk, and _recid. Update usrblk.dblk
561          * because it's based on the last table's database. 
562          */
563         if (!read_next_rec) {
564             read_next_rec = TRUE;
565             usrblk.dblk = parsed_dblk;
566         }
567         else {
568             *buf = '\0';
569             if (fgets (buf, sizeof (buf), inf) == NULL)
570             {
571                 fclose (inf);
572                 inf = NULL;
573                 break;
574             }
575             records_read++;
576             buf[sizeof (buf) - 1] = 0;  /* guarantee termination */
577             if (strlen(buf) && buf[strlen(buf)-1] == '\n')
578               buf[strlen(buf)-1] = '\0';
579
580
581             /*
582              * Parse line into dbname and recid.  Skip line if
583              * error. 
584              */
585             if (!parse_infbuf (buf))
586                 continue;
587
588             /* on very first read, save the database name */
589             if (last_dbname[0] == 0) {
590                 strcpy (last_dbname, parsed_dbname);
591                 last_dblk = parsed_dblk;
592             }
593         }       /* finished reading next rec in input file */
594
595         /*
596          * Test for change of database name.  Restore usrblk.dblk
597          * to reflect all the records on the dba table so far. Then
598          * save the new dblk for when we are again called. 
599          */
600         if (strcmp (last_dbname, parsed_dbname) != 0) {
601             read_next_rec = FALSE;
602             strcpy (last_dbname, parsed_dbname);
603             usrblk.dblk = last_dblk;
604             last_dblk = parsed_dblk;
605             break;
606         }
607
608         /*
609          * Call OE to get record's db address. Turn off debug
610          * temporarily so won't flood output with messages. 
611          */
612         usrblk.query = parsed_recid;
613         usrblk.debug &= ~USRDBG_DELETE;
614         usrblk.request = OE_RECKEY2DBA;
615         Opera_Engine ();
616         if (debug_mode) /* restore */
617             usrblk.debug |= USRDBG_DELETE;
618         if (DtSearchHasMessages ()) {
619             putchar ('\n');
620             PRINT_MESSAGES
621         }
622
623         if (usrblk.retncode == OE_WRAPPED) {
624             if (first_err) {
625                 first_err = FALSE;
626                 fputc ('\n', aa_stderr);
627             }
628             fprintf (aa_stderr, catgets(dtsearch_catd, MS_tomita, 24,
629                 "%s Database %s, '%s' not found.\n") ,
630                 PROGNAME"482", parsed_dbname, parsed_recid);
631             continue;
632         }
633         else if (usrblk.retncode != OE_OK)
634             retncode_abort (486);
635
636         /* add db address to growing table */
637         *next_dba = usrblk.dba;
638         next_dba++;
639         usrblk.dbacount++;
640
641     }   /* end of main record read loop */
642
643     /* It is possible to exit the main loop, because database changed
644      * or whatever, but no records were added to usrblk.dbatab.
645      * If there are still records to be read from the input file,
646      * go back and try another pass.
647      */
648     if (inf != NULL && usrblk.dbacount == 0)
649         goto KEEP_READING;
650
651     return usrblk.dbacount;
652 }  /* load_dbatab() */
653
654
655 /****************************************/
656 /*                                      */
657 /*               deleter                */
658 /*                                      */
659 /****************************************/
660 /* Program 2: deletes records specified in input file.
661  * Must be run offline when all online users have logged off.
662  */
663 static void     deleter (char *infname)
664 {
665     int             i;
666     long            records_deleted;
667     time_t          start_time, minutes, hours, seconds, elapsed;
668     char            buf[128];
669
670     if (!yesarg) {
671         printf ( catgets(dtsearch_catd, MS_tomita, 25,
672     "\nDO NOT CONTINUE under any of the following circumstances:\n"
673     "-> If the input file which lists record ids to be deleted is not\n"
674     "      named '%s'.\n"
675     "-> If any users are still accessing the affected database(s).\n"
676     "-> If any database files have not been backed up.\n\n"
677     "If you are sure you are ready to start deleting, enter 'y' now... ") ,
678             infname, OE_prodname);
679
680         fgets (buf, sizeof(buf)-1, stdin);
681         if (tolower (*buf) != 'y')
682             return;
683     }
684
685     /* Make sure engine doesn't abort because of
686      * recurring changes to d99 files.
687      */
688     OE_sitecnfg_mtime = 0L;
689
690     /* Init table of db addrs */
691     usrblk.dbatab = austext_malloc
692         (sizeof (DB_ADDR) * (max_dbacount + 2), PROGNAME "531", NULL);
693     usrblk.dbacount = 0;        /* number of recs currently in table */
694
695     /* Init status msg stuff */
696     records_read = 0L;
697     records_deleted = 0L;
698     time (&start_time);
699
700     signal (SIGINT, kill_delete);
701     signal (SIGQUIT, kill_delete);
702     signal (SIGTRAP, kill_delete);
703     signal (SIGTERM, kill_delete);
704     signal (SIGPWR, kill_delete);
705 #ifdef _AIX
706     signal (SIGXCPU, kill_delete);      /* cpu time limit exceeded */
707     signal (SIGDANGER, kill_delete);    /* imminent paging space
708                                          * crash */
709 #endif
710
711     /* MAIN LOOP */
712     while (load_dbatab ()) {
713         /*
714          * Stop now if we have exceeded user specified time limit
715          * or if user sent termination or interrupt signal. 
716          */
717         if (shutdown_now)
718             break;
719         elapsed = time (NULL) - start_time;
720         if (maxtime > 0L && elapsed >= maxtime)
721             break;
722
723         /* echo status for humans who might be watching */
724         hours = elapsed / 3600L;
725         seconds = elapsed - (3600L * hours);    /* remaining after hours */
726         minutes = seconds / 60L;
727         seconds = seconds - (60L * minutes);
728         printf ( catgets(dtsearch_catd, MS_tomita, 26,
729             "%s %ld read, %ld deleted, %ldh %2ldm %2lds elapsed.\n"
730             "  Database '%s': Current record count = %ld, Batch size = %d.\n") ,
731             aa_argv0, records_read, records_deleted,
732             hours, minutes, seconds,
733             usrblk.dblk->name, usrblk.dblk->dbrec.or_reccount, usrblk.dbacount);
734         /*****fflush (stdout);*****/
735
736         /* call OE to delete batch of records */
737         usrblk.request = OE_DELETE_BATCH;
738         Opera_Engine ();
739         if (DtSearchHasMessages ()) {
740             putchar ('\n');
741             PRINT_MESSAGES
742         }
743         if (usrblk.retncode != OE_OK)
744             retncode_abort (572);
745         records_deleted += usrblk.dbacount;
746
747     }   /* end main loop */
748
749
750     /* Print final status messages */
751     elapsed = time (NULL) - start_time; /* total elapsed time */
752
753     hours = elapsed / 3600L;
754     seconds = elapsed - (3600L * hours);        /* remaining after hours */
755     minutes = seconds / 60L;
756     seconds = seconds - (60L * minutes);        /* remaining after hours
757                                                  * & mins */
758     printf ( catgets(dtsearch_catd, MS_tomita, 27,
759         "%s %ld records read from input file.  %ld were deleted and\n"
760         "  %ld were not found in %ld hours, %ld minutes, %ld seconds,\n") ,
761         aa_argv0, records_read, records_deleted,
762         records_read - records_deleted,
763         hours, minutes, seconds);
764
765     /* Figure average time for a deletion */
766     elapsed = (records_deleted) ? elapsed / records_deleted : 0L;
767     minutes = elapsed / 60L;
768     seconds = elapsed - (60L * minutes);
769     printf ( catgets(dtsearch_catd, MS_tomita, 28,
770         "  or an average of %ld minutes, %ld seconds per record deleted.\n"),
771         minutes, seconds);
772     return;
773 }  /* deleter() */
774
775
776 /****************************************/
777 /*                                      */
778 /*                 main                 */
779 /*                                      */
780 /****************************************/
781 int             main (int argc, char *argv[])
782 {
783     char           *arg;
784     time_t          mytime;
785     char            timebuf[80];
786
787     aa_argv0 = argv[0];
788     setlocale (LC_ALL, "");
789     dtsearch_catd = catopen (FNAME_DTSRCAT, 0);
790     time (&mytime);
791     strftime (timebuf, sizeof (timebuf),
792         catgets(dtsearch_catd, MS_misc, 22, "%A, %b %d %Y, %I:%M %p"),
793         localtime (&mytime));
794     printf (catgets(dtsearch_catd, MS_tomita, 29,
795         "%s.  Run %s.\n") ,
796         aa_argv0, timebuf);
797     austext_exit_last = print_exit_code;
798
799     signal (SIGINT, DtSearchExit);
800     signal (SIGTERM, DtSearchExit);
801 /****memset (&usrblk, 0, sizeof(USRBLK));****/
802
803     /* Validate program number argument */
804     if (argc < 2) {
805 BAD_ARGS:
806         fprintf (aa_stderr, catgets(dtsearch_catd, MS_tomita, 30,
807             "\nUSAGE: %s [options]\n"
808             "  -i    Input file name.  If not specified, defaults to %s.\n"
809             "  -d[v] Print debug statements.\n"
810             "        -dv turns on verbose (record-by-record) debugging.\n"
811             "  -t<N> Max desired number of seconds of run time.\n"
812             "        Ctrl-C/Break will also stop deletion at next record.\n"
813             "  -n<N> Change number of records in a batch from %d to <N>.\n"
814             "  -y    Automatically answers 'yes' to Delete mode confirm prompt.\n"
815             "  -d    trace deletion operations.\n") ,
816             aa_argv0, FNAME_DISCARD_DATA,
817             FNAME_CONFIRM_LIST, FNAME_CONFIRM_LIST, DBACOUNT);
818         DtSearchExit (2);
819     }
820     prog = toupper (argv[1][0]);
821     if (prog != 'B' && prog != 'D')
822         goto BAD_ARGS;
823
824     /* Initialize defaults depending on program mode */
825     if (prog == 'B') {
826         infname = FNAME_DISCARD_DATA;
827         outfname = FNAME_CONFIRM_LIST;
828     }
829     else {
830         infname = FNAME_CONFIRM_LIST;
831         outfname = PROGNAME "654";
832     }
833     maxtime = 0L;
834
835     /* Save rest of command line arguments */
836     for (argc -= 2, argv += 2; argc > 0; argc--, argv++) {
837         arg = *argv;
838         switch (tolower (arg[1])) {
839             case 'i':
840                 infname = arg + 2;
841                 break;
842
843             case 'o':
844                 outfname = arg + 2;
845                 break;
846
847             case 'd':
848                 debug_mode = TRUE;
849                 usrblk.debug |= USRDBG_DELETE;
850                 if (arg[2] == 'v')
851                     usrblk.debug |= USRDBG_VERBOSE;
852                 break;
853
854             case 'y':
855                 yesarg = TRUE;
856                 break;
857
858             case 't':
859                 maxtime = atol (arg + 2);
860                 break;
861
862             case 'n':
863                 max_dbacount = atol (arg + 2);
864                 break;
865
866             default:
867                 fprintf (aa_stderr, catgets(dtsearch_catd, MS_tomita, 31,
868                     "\n%s Unknown argument '%s'.\n") ,
869                     PROGNAME"689", arg);
870                 goto BAD_ARGS;
871
872         }       /* end switch */
873     }   /* end arg parsing */
874
875     /* Open input file to test for its existence.
876      * For the Browse program, file ptr 'inf' == NULL
877      * means the file is not open.
878      */
879     if ((inf = fopen (infname, "r")) == NULL) {
880         if (prog == 'D') {
881             fprintf (aa_stderr, catgets(dtsearch_catd, MS_tomita, 32,
882                 "%s Unable to open input file '%s'.\n") ,
883                 PROGNAME"710", infname);
884             goto BAD_ARGS;
885         }
886     }
887
888     /* If browsing, get output file name and
889      * open it to test for write permission.
890      */
891     if (prog == 'B') {
892         if ((outf = fopen (outfname, "a ")) == NULL)
893             /* the blank in "a " works around old aix bug */
894         {
895             fprintf (aa_stderr,  catgets(dtsearch_catd, MS_tomita, 33,
896                 "\n%s Unable to open output file '%s'.\n") ,
897                 PROGNAME"721", outfname);
898             goto BAD_ARGS;
899         }
900     }
901
902     /* Initialize the opera engine, i.e. open the database */
903     printf ( catgets(dtsearch_catd, MS_tomita, 34,
904         "Initializing %s engine...\n"),
905         OE_prodname);
906     strcpy (usrblk.userid, "ToMiTa");
907     usrblk.request = OE_INITIALIZE;
908     usrblk.query = AUSAPI_VERSION;
909     Opera_Engine ();
910     if (usrblk.retncode != OE_OK)
911         retncode_abort (733);
912
913     PRINT_MESSAGES
914         if (prog == 'B')
915         browser ();
916     else
917         deleter (infname);
918
919     usrblk.request = OE_SHUTDOWN;
920     Opera_Engine ();
921     printf ( catgets(dtsearch_catd, MS_tomita, 36,
922         "Normal engine shutdown.\n") );
923     DtSearchExit (0);
924 }  /* main() */
925
926 /******************* TOMITA.C *******************/