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