/* -*- C -*- */ /* [SRI-NIC]PS:WHOIS.C.1145, 10-Dec-90 12:43:03, Edit by ZZZ * In find_temp() - use the temp flags, don't just OR them in (must be able * to do temp search without a flag that's already set). Changed calls * to find_temp() to OR their own flags in where appropriate. * This fixes the mysterious extra matches in the subdomain display by * eliminating the partial match flag from that temp search. * (*** same as 1144, but without intervening netblock/asn changes ***) * * [SRI-NIC]PS:WHOIS.C.1134, 8-Jun-90 20:29:21, Edit by ZZZ * Added find_big() to handle searches with more than MAX_MATCH matches. * Added boolean return codes to sub_matches(), display_matches(), and * summary() to preserve listing continutity across search&display * iterations. (ie. when a user says "no" to "see more?", it should stop!) * Added time_thru argument to same procs to allow them to tailor their * output to match whether it's the first display block or not. * Dropped MAX_MATCH back down to 2048. * * ***Note: To make other procedures take advantage of this large-match * capability, they need to be changed to use the find_big() and to accept * the aforementioned return codes throughout their output path. * * [SRI-NIC]PS:WHOIS.C.1133, 1-Jun-90 16:02:12, Edit by ZZZ * Bumped MAX_MATCH from 2048 to 3072 to reflect the db having more users * than that registered for some hosts. * * [SRI-NIC]PS:WHOIS.C.1132, 31-May-90 15:20:43, Edit by ZZZ * In get_hostname(), update the xfstat() call code in the fashion that it * was earlier in initialize(). (see update blurb for version 1127) * * [SRI-NIC]PS:WHOIS.C.1131, 26-Feb-90 12:18:32, Edit by ZZZ * Modified log_entry() to show invocation mode (WHOIS/HOST) in usage log. * Changed checking of TacReq to ValidCard for Milnet TAC user status in * individuals' display. And made namesake hosts show up (where they exist) * in the list of hosts under a given subdomain. (eg. SRI.COM will show up * now in listing of SRI.COM's hosts...) * * [SRI-NIC]PS:WHOIS.C.1129, 20-Feb-90 16:55:28, Edit by ZZZ * In d_imp(), display the Autodin information whenever it's available. * * [SRI-NIC]PS:WHOIS.NEWC.1128, 7-Feb-90 18:41:33, Edit by ZZZ * In d_individual(), a person is a MILNET TAC user if the first character * in the TACREQ field is a Y, not if the record merely has a TACID defined. * * [SRI-NIC]PS:WHOIS.NEWC.1127, 30-Jan-90 13:37:50, Edit by ZZZ * Changed initialize() code to reflect changes in xfstat() return values. * Added record-last-updated to output of all record types. * * [SRI-NIC]PS:WHOIS.NEWC.1126, 11-Dec-89 09:34:05, Edit by ZZZ * Changes to support conversion to Ansi C compiler where needed: Added * external function declarations, removed unreferenced local vars, and * added type-casting to parameters of procedure calls. * * [SRI-NIC]PS:WHOIS.NEWC.1122, 25-Sep-89 23:29:24, Edit by ZZZ * Change header for domain servers in network listing. A header argument * (char *) was added to d_dsrv() to provide the calling function the * freedom to choose its own header string. * * [SRI-NIC]PS:WHOIS.NEWC.1121, 15-Sep-89 13:47:31, Edit by ZZZ * Changes to support listing of domain servers for network records: * In db_items[] - added i_inaddrserver record. * In d_domain() - moved hunk of code to new routine d_dsrv(), and added * call to d_dsrv() to list i_servercontact field. * In d_network() - added call to d_dsrv() to list i_inaddrserver field. * * [SRI-NIC]PS:WHOIS.C.1118, 15-Aug-89 08:14:20, Edit by ZZZ * Changed "Alternate POC" title to "Alternate Contact", and added it * to the contacts list for D, N, and O records. * * [SRI-NIC]PS:WHOIS.C.1117, 10-Aug-89 06:01:51, Edit by ZZZ * Modifications to support changed LISTEN invocation for server mode: * In log_entry() - stdin is now .priin, so get host info from stdout. * In initialize() - set F_SERVER flag when invoked as SWHOIS. * In serve() - loop and suspend, to support LISTEN's continuing of forks. * * [SRI-NIC]PS:WHOIS.C.1116, 4-Aug-89 15:51:57, Edit by ZZZ * Added the AlternatePOC field to output for H, S, T, and W records. For * this, added AlternatePOC to contacts-lists for hosts and imps. Made * d_tac() refer to the imp_contacts[] list, rather than creating a * duplicate with the name tac_contacts[]... * In get_htype(): don't show INVISIBLE_HTYPES records even if X_OK. * * [SRI-NIC]PS:WHOIS.C.1115, 12-Jun-89 06:10:43, Edit by ZZZ * Modified f_tac() to call do_host() only if the search for the tac * fails. Conditionalized the part of do_host() that automatically * performs a partial-match search when it's not requested. Both of * these were causing unacceptably-long searches for simple requests. * Changed the no-subdomains message in d_s_domain() to be more useful. * * [SRI-NIC]PS:WHOIS.C.1114, 12-May-89 06:10:40, Edit by ZZZ * Changed "in front" to "before the name" in the see_more() help strings, * v3[] and v4[], to make it clearer just where the '*' or '%' should go. * Made sure all calls to see_more() include the flags for the original * match block: Those calls that used freshly-initialized blocks without * merging the original flags were changed to use the original block (omb). * This ensures that see_more(), as a prompting function, will always know * whether the user's original command was EXPAND or SUBDISPLAY... * * [SRI-NIC]PS:WHOIS.C.1113, 7-May-89 02:10:55, Edit by ZZZ * Added function strip_trailing_white() to remove the whitespace from the * end of the command line. The command parser did the job for all of the * arguments except the last. It's now the first thing done in whois(). * * [SRI-NIC]PS:WHOIS.C.1111, 5-Apr-89 05:56:21, Edit by ZZZ * Fixed do_mailbox() to try to find the exact string it's given before * trying to canonicalize the hostname. Only do the latter if the former * yields no results. In canonicalize_host(), don't print any errors * when the hostname lookup yields no matches - they don't do any good. * * [SRI-NIC]PS:WHOIS.C.1089, 30-Nov-88 20:24:30, Edit by IAN * Fixed do_mailbox() to handle "@ host" case with leading whitespace * before before hostname part. * * [SRI-NIC]PS:WHOIS.C.1082, 25-Nov-88 13:28:43, Edit by IAN * massive assorted internal re-writes; field-only searches now allowed * in conjunction with type-only ("TAC !foo"); new ARPANET and MILNET * flags for those-only net matching; much more tabelization of stuff; * fewer globasls - now flag-driven from global_flags instead of discreet * bools. * * [SRI-NIC]PS:WHOIS.C.1061, 17-Aug-88 21:21:52, Edit by IAN * check abort flag before yes/no'ing users; check it in type_file too; * various reworkings of the interrupt scheme. * * [SRI-NIC]PS:WHOIS.C.1059, 15-Aug-88 22:57:01, Edit by IAN * display_matches() and summary() now abortable; no output on ^E if no csb * * [SRI-NIC]PS:WHOIS.C.1058, 14-Aug-88 09:21:14, Edit by IAN * Added ^G abort and ^E status interrupt characters * * [SRI-NIC]PS:WHOIS.C.1057, 12-Aug-88 22:19:16, Edit by IAN * Fixed up s_mailbox() and other mailbox stuff, which were broken. * * [SRI-NIC]PS:WHOIS.C.1056, 12-Aug-88 21:26:26, Edit by IAN * Changed "No such host as "xxx" to Host "xxx" not recognized. * * [SRI-NIC]PS:WHOIS.C.1055, 9-Aug-88 11:39:49, Edit by IAN * For sub_matches calls, make ask_p be TRUE, e.g. show the first few * then ask if they want to see the rest. * * [SRI-NIC]PS:WHOIS.C.1054, 1-Aug-88 15:47:10, Edit by IAN * Have "WHOIS" turn on _WHOIS_F which means do WHOIS-style lookup: * anything goes, normal output. temporarily overrides host_mode * * [SRI-NIC]PS:WHOIS.C.1053, 1-Aug-88 14:59:12, Edit by IAN * If there are less than AUTO_SHOW members/users/hosts-on, etc, just * show them; only ask if there are more than that. see _FIND_ASKFUL_F * * [SRI-NIC]PS:WHOIS.C.1051, 29-Jul-88 13:55:22, Edit by IAN * Don't show any of the htypes in the INVISIBLE_HTYPES string unless x_ok * * [SRI-NIC]PS:WHOIS.C.1048, 12-Jul-88 16:44:59, Edit by IAN * No more "Program error, ..." bogosity. * * [SRI-NIC]PS:WHOIS.C.1047, 24-Jun-88 16:04:44, Edit by SKAHN * Changed #include to #include * * [SRI-NIC]PS:WHOIS.C.1046, 6-Jun-88 22:41:55, Edit by IAN * Allow trailing '*' in place of trailing '.', for people who do "foo*" * * [SRI-NIC]PS:WHOIS.C.1039, 4-May-88 17:21:03, Edit by SKAHN * Enclosed TOPS-20 dependencies in #if SYS_T20 * * [SRI-NIC]PS:WHOIS.C.1032, 25-Feb-88 13:54:27, Edit by IAN * Added new global flag, "interactive", which means not-server and * not-automated-service, e.g. there's a real human there who can * answer questions. * * [SRI-NIC]PS:WHOIS.C.1029, 25-Feb-88 12:53:04, Edit by IAN * When running as a server, just dump out the raw help file instead * of siccing the HELP program on it * * [SRI-NIC]PS:WHOIS.C.1022, 19-Feb-88 13:07:08, Edit by IAN * Changed save_idstr() to now always make a key composed of two * concatenated fields, the key-field + handle. That way we get * automatic 2nd-level sorting when names are identical. * * [SRI-NIC]PS:WHOIS.C.1016, 17-Feb-88 20:02:35, Edit by IAN * Have do_mailbox() show you the hostname it canonicalized out of your * given host specification. * * [SRI-NIC]PS:WHOIS.C.946, 12-Feb-88 19:46:25, Edit by IAN * Make '%' command show subdisplay of display, instead of having * specialized users-on-host meaning. * * [SRI-NIC]PS:WHOIS.C.882, 25-Jan-88 21:02:15, Edit by IAN * Rewrote parse_command() and new_parse() to flexibly allow mixed * order of new and old commands: "host *foo" and "*host foo" both work. * * [SRI-NIC]PS:WHOIS.C.864, 18-Jan-88 20:44:09, Edit by IAN * Fix English: "No match for partial name foo" not "No partial match * for name foo" in matches(). * * [SRI-NIC]PS:WHOIS.C.855, 31-Dec-87 11:07:33, Edit by IAN * Add "bye" as synonym to quit/exit/etc. WHOIS now takes all the same * exit keywords as the EXEC. * * [SRI-NIC]PS:WHOIS.C.854, 23-Dec-87 15:05:21, Edit by IAN * In do_mailbox(), for "foo" check for "foo%..." also. * * [SRI-NIC]PS:WHOIS.C.835, 14-Dec-87 21:24:35, Edit by IAN * In do_name(), check for , e.g. "smith,john" * and, when checking name, try "smith, john" instead. also in do_name, * when the input ends with , assume it is the * tail end of a last name and initial, e.g. "smith, j"; do the search * as a partial in that case, to catch all last names with a first name * starting with that letter. */ /* * WHOIS -- User DB lookup, HOST program, NICNAM server * * This is the main DB lookup program for the external world. It * looks up records in the main registration database and displays * them (with different displays for different record types). * * WHOIS also embodies these variations: * * HOST: HOST is like WHOIS except the default lookup type is "host", * and it does a completely different type of host record output. See * d_athost(). WHOIS tells that it's HOST by checking argv[0]. * * NICNAM: This is a network-server version of WHOIS. A listening * process (LISTEN in this case) does the dirty work, and redirects * primary I/O to the net. Execution is mostly the same as for the * user version, with these exceptions: * * there are no yes/no type questions asked of the user. * instead, the user gets a fixed message (see v3[] et * al in the verbiage section) saying how to always get * subdisplay output. * * it's a one-query, one-response protocol now. i'd like * to change this. perhaps if we provided a modified unix * client to take advantage of a new multiple query * protocol, then it could happen. the suggested protocol * change is to have a trailing '-' on the input string * mean that more queries will follow, so stick around. * * WHOIS tells it's a server by checking to see if primary I/O has been * redirected to a TCP: device. See initialize(). * * XWHOIS: XWHOIS is the same as WHOIS except that it uses the x-user * database, defined as X_DB_FILE. * * Note that you must load this program with C:LIBCKX in order to make it * extended addressing. It needs the extra memory. If it doesn't have it, * it will fail at low levels, often silently, the symptoms being that * subdisplays (like registered users for a host) are simply missing. * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * source: NICPROG:WHOIS.C * required libraries: C:LIBCKX.REL, C:LIBNIC.REL * executable: PUB:WHOIS.EXE * * documentation: NICPROG:WHOIS-7935.* * help file: see OPTIONS_FILE, WHOIS_HELP_FILE * and HOST_HELP_FILE * * author: Ian Macky * written: August 86 */ #include /* this C environment, SYS_xxx, etc */ #include /* toupper, isspace, etc */ #include /* network stuff 4 gethostbyaddr etc */ #include /* tchars stuff for TTY interrupts */ #include /* for doing TTY interrupts */ #include /* standard I/O package */ #include /* strchr, etc */ #include /* for dealing with realtime */ #include /* variable calling args */ #include /* stat() needs this... */ #include /* use xstat() to know if TCP */ #include /* for using ioctl()s */ #include /* local support routines */ #include /* IDB database package */ #include /* strCMP, etc */ #if SYS_T20 #define EXEC_PARSE_COMMAND_LINE /* EXEC-style command-line parsing */ #include /* is turned on by these 2 lines */ #include /* for T20-specific forkexec() */ #include /* for JSYS names, flags */ #endif /* SYS_T20 */ #define AUTO_EXP 0 /* don't do automatic matches */ #define HOST_HTYPES "HSWTP" #define INVISIBLE_HTYPES "L" #if SYS_T20 #define QUERY_USER "ANONYMOUS.NICGUEST" #define SERVICE_USER "OPERATOR" #define DB_FILE "DB-ID:ID.ACCESS" #define HOST_HELP_FILE "HLP:HOST.HLP" #define OPTIONS_FILE "NICPROG:WHOIS.OPTIONS" #define SERVER_LOG_FILE "USE-STATS:USE-STATS.TCP-NICNAM" #define WHOIS_HELP_FILE "HLP:WHOIS.HLP" #define WHOIS_LOG_FILE "USE-STATS:USE-STATS.NICNAM" #define X_DB_FILE "DB-ID:XI.ACCESS" #define HELP_KEYWORD "WHOIS" #define HELP_PROGRAM "SYS:HELP.EXE" #else #define QUERY_USER "ANONYMOUS.NICGUEST" #define SERVICE_USER "OPERATOR" #define DB_FILE "/fs1/a/id/id" #define OPTIONS_FILE "/fs1/a/nicprog/whois.options" #define SERVER_LOG_FILE "/fs1/a/nicprog/whois.stats-server" #define WHOIS_LOG_FILE "/fs1/a/nicprog/whois.stats-local" #define X_DB_FILE "/fs1/a/id/xi" #define HELP_KEYWORD "WHOIS" #define HOST_HELP_FILE "HLP:HOST.HLP" /* Ugh */ #define WHOIS_HELP_FILE "HLP:WHOIS.HLP" /* more ugh */ #define HELP_PROGRAM "SYS:HELP.EXE" /* Super ugh */ #endif #define CONTROL(char) (char ^ 0100) #define ABORT_CHAR CONTROL('G') #define STATUS_CHAR CONTROL('E') #define MAX_MATCH 2048 /* max # of matches we can handle */ #define AVERAGE_KEY 64 /* assume this many chars of a key */ #define KEY_BUF_SIZE (MAX_MATCH * AVERAGE_KEY) #define MAX_LINE 512 /* size of a generic char buffer */ #define MAX_ADDR 128 /* max # of addresses for a host */ #define PAGE_LENGTH 24 /* these are the default page length */ #define LINE_WIDTH 80 /* and heights, if modes' are 0 */ #define ASK_AFTER 5 /* ask to continue after this many */ #define AUTO_SHOW (ASK_AFTER * 2) /* if this many or less, just show */ #define BREAK_EVERY 10 /* break summaries every n people */ #define MBX_COLUMN 33 /* for summary, mailbox starts here */ #define ADDR_COLUMN 33 /* for hosts on imp, addr start here */ #define SUB_DOM_COLUMN 21 /* sub-domain output name start here */ #define NET_ARPANET 10 /* network#s of these networks */ #define NET_MIT 18 /* MIT-TEMP-NET */ #define NET_MILNET 26 /* used by net_display() */ #define FALSE 0 #define TRUE 1 typedef int bool; /* TRUE/FALSE only */ /* * global flag (F) bits */ #define F_ABORT (1 << 0) /* user wants to abort search? */ #define F_INT_ENABLED (1 << 1) /* interrupts enabled? */ #define F_DB_LOADED (1 << 2) /* db loaded & initialized yet? */ #define F_ONCE_THROUGH (1 << 3) /* input from JCL? if so, one pass. */ #define F_HOST_MODE (1 << 4) /* running as the host program? */ #define F_X_WHOIS (1 << 5) /* looking at DB of X stuff? */ #define F_NIC_USER (1 << 6) /* are you? if not, no frills. */ #define F_SERVER (1 << 7) /* are you? one query, one response */ #define F_INFERIOR (1 << 8) /* running as inferior fork? */ #define F_X_OK (1 << 9) /* allowed to see X records? */ #define F_INTERACTIVE (1 << 10) /* interactive session? user there? */ #define F_NLI (1 << 11) /* Not Logged In user? */ /* * global_flag-diddling macros */ #define SET(flag) global_flags |= (flag) #define CLEAR(flag) global_flags &= ~(flag) #define ON(flag) (global_flags & (flag)) #define OFF(flag) (!(global_flags & (flag))) /* * local flag (LF) bits */ #define LF_DUMP (1 << 0) /* void-dump-style output */ #define LF_PARTIAL (1 << 1) /* partial match on search */ #define LF_SUBSTRING (1 << 2) /* substring match on search */ #define LF_NO_ABBREV (1 << 3) /* keyword can't be abbreviated */ #define LF_NO_ARG (1 << 4) /* keywords takes no arg */ #define LF_EXPAND (1 << 5) /* do everything without asking */ #define LF_RECORDS_FULL (1 << 6) /* out of room in match block */ #define LF_NO_SUB (1 << 7) /* don't show subdisplay */ #define LF_FULL_OUTPUT (1 << 8) /* no summary, full output on all */ #define LF_SUMMARY (1 << 9) /* do a summary always. */ #define LF_FIND_FIRST (1 << 10) /* return first match in find() */ #define LF_KEYS_FULL (1 << 11) /* key buffer is full */ #define LF_SUBDISPLAY (1 << 12) /* show subdisplay of display */ #define LF_MAILBOX_ONLY (1 << 13) /* mailbox-only search */ #define LF_HANDLE_ONLY (1 << 14) /* handle-only search */ #define LF_NAME_ONLY (1 << 15) /* name-only search */ #define LF_COMMAND (1 << 16) /* keyword is a WHOIS command */ #define LF_FIND_ASKFUL (1 << 17) /* stop finding after ASK_AFTER */ #define LF_WHOIS (1 << 18) /* do WHOIS-style output */ #define LF_ARPANET (1 << 19) /* only want ARPANET hosts */ #define LF_MILNET (1 << 20) /* only want MILNET hosts */ #define LF_BARE_COMMAND (LF_COMMAND | LF_NO_ABBREV | LF_NO_ARG) #define LF_ONLY_FIELDS (LF_HANDLE_ONLY | LF_NAME_ONLY | LF_MAILBOX_ONLY) /* * this is the structure for storing search matches in. we keep one * lying around globally since that way there's no chance of not * being able to allocate it (with get_match_block()), plus we want * the parse flags kept in the flags word to be globally available. */ typedef struct MATCH_BLOCK { unsigned flags; /* flag word */ int cmd_index; /* index into commands[] of cmd */ int searches; /* # of searches into this mb */ char *htypes; /* pointer to valid htypes or NULL */ idfnd_t fndblk; /* find-block for searching */ char *target; /* target string */ iditm_t key_field; /* field to use for sort key */ int count; /* number of matches */ int records[MAX_MATCH]; /* offset into next two arrays */ idrix_t recidx[MAX_MATCH]; /* record indicies of matches */ char *keys[MAX_MATCH]; /* pointers to saved names */ char *key_cp; /* pointer into name_buf */ int key_count; /* # chars saved in key_buffer */ char key_buffer[AVERAGE_KEY * MAX_MATCH]; } match_block; /* * database field number stuff. i_foo gets the database's internal * field# for field "foo". load_database() goes over this array * and looks up the field#s given the names and fills them into * the i_foo locations. i_handle is special since it is guaranteed * to be 0. */ #if SYS_T20 /* Disambiguate variable names (1st 6 chars) on T20 */ #define i_alternatepoc i_altpoc #define i_hostname i_hname #define i_impname i_iname #define i_impnetnumber i_inet #define i_impnumber i_inumber #define i_netname i_nname #define i_netnumber i_nnumber #endif #define i_handle 0 /* handle is ALWAYS item #0 */ iditm_t i_address, i_admincontact, i_alternatepoc, i_autodin, i_comments, i_connectype, i_coordinator, i_cputype, i_domainame, i_groups, i_hadmin, i_host, i_hostname, i_htype, i_impname, i_impnetnumber, i_impnumber, i_inaddrserver, i_mailbox, i_name, i_netaddress, i_netname, i_netnumber, i_nicknames, i_opsys, i_parentdom, i_phone, i_protocols, i_servercontact, i_tacid, i_tacnumber, i_techcontact, i_updated, i_user, i_validcard, i_zonecontact; struct item_record { iditm_t *item; /* addr of iditm_t containing field# */ char *name; /* textual name of field, for lookup */ } db_items[] = { &i_address, "address", &i_admincontact, "admincontact", &i_alternatepoc, "alternatepoc", &i_autodin, "autodin", &i_comments, "comments", &i_connectype, "connectype", &i_coordinator, "coordinator", &i_cputype, "cputype", &i_domainame, "domainame", &i_groups, "groups", &i_hadmin, "hostadmin", &i_host, "host", &i_hostname, "hostname", &i_htype, "htype", &i_impname, "impname", &i_impnetnumber, "impnetnumber", &i_impnumber, "impnumber", &i_inaddrserver, "inaddrserver", &i_mailbox, "mailbox", &i_name, "name", &i_netaddress, "netaddress", &i_netname, "netname", &i_netnumber, "netnumber", &i_nicknames, "nicknames", &i_opsys, "opsys", &i_parentdom, "parentdom", &i_phone, "phone", &i_protocols, "protocols", &i_servercontact, "servercontact", &i_tacid, "tacid", &i_tacnumber, "tacnumber", &i_techcontact, "techcontact", &i_updated, "updated", &i_user, "user", &i_validcard, "validcard", &i_zonecontact, "zonecontact" }; /* * structure for displaying contacts for a record. since the * same person might fill more than one position, a structure with * all the ident-containing-fields we want displayed is given to * d_contacts(), which does the right thing, display-wise. * * the record and handle members are set by d_contacts(), and can * be initialized to anything. if the label is NULL, the database's * label for that field is used. */ struct contact_struct { iditm_t *field; /* field# that ID is kept in */ char *label; /* label to be used */ idrix_t record; /* db record# */ char *handle; /* cp to handle */ }; struct contact_struct host_contacts[] = { &i_hadmin, "Host Administrator", 0, NULL, &i_coordinator, "Coordinator", 0, NULL, &i_alternatepoc, "Alternate Contact", 0, NULL }; #define N_HOST_CONTACTS (sizeof(host_contacts)/sizeof(struct contact_struct)) struct contact_struct imp_contacts[] = { &i_coordinator, "Coordinator", 0, NULL, &i_alternatepoc, "Alternate Contact", 0, NULL }; #define N_IMP_CONTACTS (sizeof(imp_contacts)/sizeof(struct contact_struct)) struct contact_struct domain_contacts[] = { &i_admincontact, "Administrative Contact", 0, NULL, &i_techcontact, "Technical Contact", 0, NULL, &i_zonecontact, "Zone Contact", 0, NULL, &i_alternatepoc, "Alternate Contact", 0, NULL }; #define N_DOMAIN_CONTACTS (sizeof(domain_contacts)/sizeof(struct contact_struct)) struct contact_struct coordinator[] = { &i_coordinator, NULL, 0, NULL, &i_alternatepoc, "Alternate Contact", 0, NULL }; #define N_COORDINATOR (sizeof(coordinator)/sizeof(struct contact_struct)) /* * procedure declarations. internal declarations are in the * order in which the routines occur in this source. * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * externals */ extern int atoi(); /* alpha-to-int converter */ extern int exit(); /* terminate a process */ extern int forkexec(); /* spawns inferior exec fork */ extern int free(); /* de-allocate memory blocks */ extern int getpid(); /* get process identification */ extern int getpw(); /* get name from uid */ extern int getuid(); /* return userid (e.g. user#) */ extern int ioctl(); /* device control */ extern int kill(); /* send a signal to a process */ extern char *malloc(); /* ubiquitous, eh? */ extern int qsort(); /* quicker sort */ extern int write(); /* write output */ extern int xfstat(); /* get extended status (huh?) */ /* top level routines */ char *initialize(); /* do 1-time startup initialization */ bool serve(); /* do work as server */ bool child(); /* run as inferior fork */ bool whois(); /* do an actual lookup of a string */ bool suspend(); /* suspend pgm, return to superior */ /* command parser plus support */ int parse_command(); /* parse command, ret cmds[] index */ char *get_word(); /* extract a "word" from string */ bool do_partial(); /* handle ... trailing partial */ char *do_magic(); /* old (magic character) parser */ void strip_trailing_white(); /* strip spaces from end of string */ int s_tbluk(); /* look up string in a command_table */ int c_tbluk(); /* look up a char in a command_table */ /* help stuff */ bool help(); /* routine to handle "help" command */ bool options(); /* likewise for users' "?" */ bool type_file(); /* type out the given file */ bool run_program(); /* run a program given filespec&jcl */ /* high level finder routines */ bool f_anything(); /* finder for generic lookup */ bool f_usual(); /* check the "usual" (handle, name) */ bool f_imp(); /* find an "imp" */ bool f_domain(); /* find a "domain" */ bool do_host(); /* factored out worker for hosts */ bool do_name(); /* factored out worked for names */ bool f_network(); /* find a "network" */ bool f_org(); /* find an "organization" */ bool f_tac(); /* find a "tac" */ bool do_mailbox(); /* factored out worker for mailbox */ void find_mailbox(); /* do_mailbox() subroutine */ bool s_mailbox(); /* find a suffix-mailbox */ /* low-level finder routines */ idrix_t find(); /* the real core finding routine */ idrix_t find_temp(); /* find() with temp flags/target */ int find_big(); /* find() for large sets */ /* record- and key-saving routines */ bool save_match(); /* save record/key in match block */ bool save_record(); /* save record keyed by name/handle */ char *save_idstrs(); /* save an idstr_t or C string */ char *save_string(); /* in permanent key-string buffer */ /* high-level display routines */ bool display_matches(); /* display the results of a search */ void no_matches(); /* emits the "No matches..." line */ bool sub_matches(); /* like the above, for sub-matches */ void out_record(); /* display the given record */ void dump_record(); /* dump complete record */ bool summary(); /* summarize multiple matches */ int sum_line(); /* standard summary-line routine */ void s_rest(); /* do variable part of sum_line() */ void contact_summary(); /* special output for a contact */ void d_contacts(); /* multiple-contact typeout */ /* per-record-type display routines */ void d_individual(); /* display a person's record */ void d_machine(); /* display some sort of machine */ void d_host(); /* display a host */ void d_s_host(); /* host subdisplay */ void d_hinfo(); /* display standard info for host */ void d_imp(); /* display an imp */ int out_netname(); /* support routine: print netname */ void d_domain(); /* show a domain & hosts on it */ void d_dsrv(); /* domain-servers display */ void d_s_domain(); /* domain subdisplay */ int sub_domain(); /* summary routine for hosts on dom */ void d_network(); /* show network and hosts on it */ void d_s_network(); /* network subdisplay */ int host_line(); /* summary routine for hosts on net */ void d_org(); /* display an organization */ void d_s_org(); /* organization subdisplay */ void d_tac(); /* display a tac */ void d_updated(); /* display last-updated field */ /* special @HOST output routines */ void d_athost(); /* do @HOST-program-style d_host() */ void net_display(); /* address typout used by athost */ /* middle-level output routines */ int name_and_handle(); /* output "name (handle)" */ void standard_header(); /* name + handle + address header */ void comments(); /* display comments if any */ int label(); /* output name of field */ void d_labeled(); /* output label then contents */ void d_indented(); /* display multi-line field indented */ /* low-level output routines */ void putz(); /* Output item specified by item # */ bool yesno(); /* ask a yes-or-no question, int off */ void space(); /* space/tab over n spaces */ /* low-level database support */ void load_database(); /* load and initialize database */ bool get_record(); /* make the given record current */ bool get_item(); /* get item from current record */ int get_htype(); /* verify and return htype from cur */ bool right_net(); /* checks if correct network */ bool x_record(); /* is this an X-record? */ char *id2c(); /* turn idstr_t into C string */ /* log-file stuff, plus support */ void log_entry(); /* make a log-file entry */ void timestamp(); /* emit a timestamp to a stream */ void get_hostname(); /* get host on other side of stream */ void get_job_info(); /* do what it says */ /* miscellaneous support */ bool canonicalize_host(); /* canonicalize hostname into buf */ void match_initialize(); /* initialize the match block */ void search_initialize(); /* initialize a fndblk */ int net_part(); /* extract net portion of A.B.C.D */ bool see_more(); /* factored out want-to-see-more? */ void sort_matches(); /* run qsort over a match block */ int qcmp(); /* qsort/strCMP/structure interface */ match_block *get_match_block(); /* allocate a match_block */ void free_match_block(); /* release an allocated match_block */ char *get_jcl(); /* read RSCAN JCL into buffer */ /* interrupt stuff */ void int_clear(); /* reset interrupt flags */ void int_on(); /* turn on interrupts */ void int_off(); /* turn off interrupts */ void int_abort(); /* int: user wants to abort search */ void int_status(); /* int: user wants to see stats */ /* * command table declarations. the first table entry is for when no * keywords have been specified at all -- the default action. */ struct command_table { char *name; /* keyword */ char magic; /* equivalent magic char */ char *htypes; /* valid htypes */ int (*function)(); /* function it calls */ unsigned flags; /* flags it sets */ } commands[] = { NULL, 0, NULL, f_anything, LF_NO_ARG, "arpanet", 0, HOST_HTYPES, NULL, LF_ARPANET, "bye", 0, NULL, suspend, LF_BARE_COMMAND, "domain", 0, "D", f_domain, 0, "dump", '#', NULL, NULL, LF_DUMP, "exit", 0, NULL, suspend, LF_BARE_COMMAND, "expand", '*', NULL, NULL, LF_EXPAND, "full", '=', NULL, NULL, LF_FULL_OUTPUT, "gateway", 0, "W", do_host, 0, "group", 0, "GO", f_org, 0, "handle", '!', NULL, NULL, LF_HANDLE_ONLY, "help", 0, NULL, help, LF_BARE_COMMAND, "host", 0, HOST_HTYPES, do_host, 0, /* --- This one catches "who is foo" and strips leading whitespace ------- */ "is", ' ', NULL, NULL, 0, "IMP", 0, "S", f_imp, 0, "mailbox", 0, NULL, NULL, LF_MAILBOX_ONLY, "milnet", 0, HOST_HTYPES, NULL, LF_MILNET, "name", '.', NULL, NULL, LF_NAME_ONLY, NULL, '~', NULL, NULL, LF_NO_SUB, "network", 0, "N", f_network, 0, "options", '?', NULL, options, LF_BARE_COMMAND, "organization", 0, "GO", f_org, 0, "partial", 0, NULL, NULL, LF_PARTIAL, "person", 0, "I", NULL, 0, "PSN", 0, "S", f_imp, 0, "q", 0, NULL, suspend, LF_BARE_COMMAND, "quit", 0, NULL, suspend, LF_BARE_COMMAND, /* --- This is for dummies who type "RETURN" to exit --------------------- */ "return", 0, NULL, suspend, LF_BARE_COMMAND, "subdisplay", '%', NULL, NULL, LF_SUBDISPLAY, "summary", '$', NULL, NULL, LF_SUMMARY, "TAC", 0, "T", f_tac, 0, "whois", 0, NULL, NULL, LF_WHOIS }; #define N_COMMANDS (sizeof(commands) / sizeof(struct command_table)) /* * HTYPE tables */ struct htype_struct { char htype; /* htypes in this class */ char *name; /* name of this record type */ iditm_t *item1; /* first item for summary line */ iditm_t *item2; /* second item for summary line */ void (*main)(); /* main display procedure */ void (*sub)(); /* subdisplay, if any */ } htypes[] = { 'D', "domain", NULL, &i_domainame, d_domain, d_s_domain, 'G', "group", &i_mailbox, &i_phone, d_org, d_s_org, 'H', "host", &i_hostname, &i_netaddress, d_host, d_s_host, 'I', "person", &i_mailbox, &i_phone, d_individual, NULL, 'N', "network", &i_netname, &i_netnumber, d_network, d_s_network, 'O', "organization", &i_mailbox, &i_phone, d_org, d_s_org, 'P', "TEP", &i_hostname, &i_netaddress, d_host, d_s_host, 'S', "PSN", &i_impnumber,&i_impnetnumber, d_imp, NULL, 'T', "TAC", NULL, &i_phone, d_tac, NULL, 'W', "gateway", &i_hostname, &i_netaddress, d_host, d_s_host, }; #define N_HTYPES (sizeof(htypes) / sizeof(struct htype_struct)) /* * globals */ unsigned global_flags; /* global flags */ int width = LINE_WIDTH; /* TTY line width */ int height = PAGE_LENGTH; /* page height */ int column; /* output column counter */ char *run_mode; /* for differentiated logging */ /* * the following are necessary to pass information to the sorting * routine called by qsort in sort_matches(), and for displaying * current match_block stats in int_status(). */ match_block *cmb; /* pointer to in-use match_block */ match_block *sorting_mb; /* pointer to sorting-mb */ struct version w_version = { 3, 5, 1090, 1 /* major, minor, edit, who */ }; /* * verbiage */ char indent[] = " "; /* indent sub-text this much */ #define INDENT (sizeof(indent) - 1) /* length less trailing null */ char divider[] = "\n----------\n\n"; /* summary divider */ char more[] = "--- More? "; /* want to see more? */ char w_prompt[] = "Whois: "; char h_prompt[] = "Host: "; char w_intro[] = "\ Enter a handle, name, mailbox, or other field, optionally preceded\n\ by a keyword, like \"host sri-nic\". Type \"?\" for short, 2-page\n\ details, \"HELP\" for full documentation, or hit RETURN to exit.\n\ ---> Do ^E to show search progress, ^G to abort a search or output <---"; char h_intro[] = "\ Enter a handle, name, hostname, address, or other field, \"?\" for a\n\ short options list, \"HELP\" for full help, \"WHOIS xxx\" for WHOIS-type\n\ output, or hit RETURN to exit."; char single_out[] = "\n\ To single out one record, look it up with \"!xxx\", where xxx is the\n\ handle, shown in parenthesis following the name, which comes first."; char v3[] = "\n\ To see this host record with registered users, repeat the command with\n\ a star ('*') before the name; or, use '%' to show JUST the registered users."; char v4[] = "\n\ To see this organization record with registered members, repeat the command\n\ with a star ('*') before the name; or, use '%' to show JUST the registered\n\ members."; char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; /* * top level routines * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * optionally given an arg, look up the givetn string. if we're a * server (determined by initialize()) then do one-query one-response. * else for a user, if they gave us the string in JCL just look it up * and stop, otherwise type an intro prompt and loop asking for input, * punting when they hit or give generic QUIT command. */ int main(argc, argv) int argc; char **argv; { char *arg, *target, *intro, *prompt, buf[MAX_LINE]; arg = initialize(argc, argv); /* do 1-time initialization */ if (ON(F_SERVER)) /* if running as a server */ return serve(); /* then do the work & done. */ if (ON(F_INFERIOR)) /* if running as inferior */ return child(); /* play the role of child. */ if (ON(F_HOST_MODE)) { intro = h_intro; prompt = h_prompt; /* set up the verbiage pointers */ } else { /* depending on what mode we're */ intro = w_intro; /* running in. */ prompt = w_prompt; } /* * loop; if no arg yet, ask for it. to exit, we do our own HALTF% * and then continue the for (;;) loop, since the C runtime itself * isn't continuable. */ for (;;) { /* loop forever */ if (arg) { whois(arg); /* if there's an arg, whois */ if (ON(F_NLI)) putchar('\n'); } if (!arg || ON(F_NLI)) { /* no arg, so prompt & get */ nx_nicbanner("WHOIS", &w_version); /* announce ourself */ if (OFF(F_NIC_USER)) /* NIC staff already know */ puts(intro); /* how to use WHOIS. */ while ((target = getline(buf, MAX_LINE, prompt)) && *target) whois(target); /* loop doing lookups */ } fflush(stdout); /* make sure all gets out. */ suspend(); /* halt now, continuable. */ arg = get_jcl(buf); /* see if there's new JCL */ } } /* * do one-time initialization. */ char *initialize(argc, argv) int argc; char **argv; { int n, user_no, ablock[5]; bool service_user; struct tchars tch; char *arg; /* user's JCL-line arg */ char user[100]; global_flags = 0; /* reset all global flags */ user_no = getuid(); /* our user number */ user[0] = 0; getpw(user_no, user); /* Find user name */ #if SYS_T20 if (!user_no) SET(F_NLI); /* if 0, Not Logged In. */ #endif service_user = (strCMP(user,SERVICE_USER)==0); /* SERVICE user? */ if (user_no && (strCMP(user,QUERY_USER)!=0) && !service_user) SET(F_NIC_USER); #if SYS_T20 { struct xstat xs; unsigned int dev; /* xfstat() calls DVCHR, so LH(st_dev) has input device type. */ /* If it's a TCP device, we're running in server mode. */ if (!xfstat(fileno(stdin),&xs)) { dev = ((unsigned int) xs.st.st_dev) >> 18; if (dev == (0600000 + ST_DEV_TCP)) /* 600000 is 20X device code */ SET(F_SERVER); } } #endif if (ON(F_NIC_USER) && OFF(F_SERVER)) /* allowed to see X records? */ SET(F_X_OK); run_mode = "WHOIS"; /* default run mode */ if (argc > 0) { /* command name's 1st in JCL */ if (!strCMP(argv[0], "HOST")) { /* host program is one of us */ SET(F_HOST_MODE); run_mode = "HOST"; } else if (ON(F_X_OK) && !strCMP(argv[0], "XWHOIS")) { SET(F_X_WHOIS); /* XWHOIS looks at X DB */ run_mode = "XWHOIS"; } else if (!strCMP(argv[0], "INFERIOR")) SET(F_INFERIOR); /* we're to run as inferior */ } if (OFF(F_SERVER | F_INFERIOR) && !service_user) SET(F_INTERACTIVE); if (argc > 1) { /* if runing from JCL, the */ arg = argv[1]; /* arg is the one and only */ if (ON(F_NIC_USER)) SET(F_ONCE_THROUGH); /* argv arg, ala urtsud. */ } else arg = NULL; /* else no input yet; so ask */ #if SYS_T20 if (ON(F_INTERACTIVE)) { ablock[1] = _PRIOU; /* where output goes */ if (jsys(RFMOD, ablock)) { /* get mode word */ if (n = (ablock[2] & TT_WID) >> TT_WID_S) width = n; /* get width from mode word */ if (n = (ablock[2] & TT_LEN) >> TT_LEN_S) height = n; /* get page length */ } } #endif if (ON(F_INTERACTIVE)) { ioctl(0, TIOCGETC, &tch); /* get current stuff */ tch.t_quitc = ABORT_CHAR; /* make quit char ^G */ tch.t_intrc = STATUS_CHAR; /* make int char ^E */ ioctl(0, TIOCSETC, &tch); /* set it back now */ signal(SIGQUIT, int_abort); /* go here on abort */ signal(SIGINT, int_status); /* here for status */ } return arg; /* return arg or NULL if none */ } /* * when running as a server, get a single query and return a single * response, then done. a blank line from the user returns the * help file. */ bool serve() { char *target, buf[MAX_LINE]; setlinebuf(stdin); /* line-at-a-time input, pls */ setvbuf(stdout, NULL, _IOFBF, 0); /* full buffering on output */ target = getline(buf, MAX_LINE, 0); /* get the arg line */ return whois(target); } /* * handle running as inferior fork. we get input from pipe, line at * a time, and send out output to stdout like normal. after each * lookup we return control to superior by stopping (SIGTSTP), and * are continued when needed again with a SIGCONT. */ bool child() { char *target, buf[MAX_LINE]; static int our_pid = 0; bool result = FALSE; setlinebuf(stdin); /* line-at-a-time from pipe. */ if (!our_pid) /* if don't known our own */ our_pid = getpid(); /* process ID, figure it now. */ while (target = getline(buf, MAX_LINE, 0)) { /* while there's input */ result = whois(target); /* do the lookup then */ kill(our_pid, SIGTSTP); /* halt back to superior. */ } /* do it again when continued */ return result; /* return final result. */ } /* * Suspend program and return to superior. Permits continuation. */ bool suspend() { nx_halt(); } /* * given a string, do the lookup. returns truth value. */ bool whois(s) char *s; { char *arg, *htypes; bool result = FALSE; match_block mb; unsigned flags; int cmd; strip_trailing_white(s); /* make sure it's usable */ log_entry(s); /* make a log entry */ if (!*s && ON(F_SERVER)) /* a blank line to the */ return help(); /* server gives help */ cmd = parse_command(s, &arg, &flags); /* cmd=0 for default action */ if (flags & LF_COMMAND) /* if it's a command, */ result = ((*commands[cmd].function)()); /* just do it */ else if (commands[cmd].function || (flags & LF_ONLY_FIELDS)) { if (!*arg) { puts("Sorry, you can't search for the null string."); return FALSE; } if (OFF(F_DB_LOADED)) /* if it hasn't been loaded */ load_database(); /* yet, load up the db now. */ /* --- Unless overriden, host-mode and ARPA/MIL modes are for hosts ----- */ if (!(htypes = commands[cmd].htypes)) if (ON(F_HOST_MODE) || (flags & (LF_ARPANET | LF_MILNET))) htypes = HOST_HTYPES; match_initialize(&mb, arg, i_name, flags, htypes); mb.cmd_index = cmd; /* command used in search */ int_on(); /* turn on interrupts */ f_usual(&mb); /* first try the usual */ /* --- Only not field-only and has additional finder, use it ------------- */ if (!(flags & LF_ONLY_FIELDS)) (*commands[cmd].function)(&mb); int_off(); cmb = NULL; /* not searching anymore */ display_matches(&mb, sum_line, TRUE, FALSE); /* display results! */ result = (mb.count > 0); /* won if found anything */ } else /* do-nothing keyword???? */ printf("OK, %s!\n", commands[cmd].name); return result; } /* * command parser plus support * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * command parser. */ int parse_command(s, rest, flags) char *s, **rest; unsigned *flags; { char *cp, *next, word[MAX_LINE]; int i, cmd = 0; int (*function)() = NULL; *flags = 0; /* initialize flag word */ if (do_partial(cp = s)) /* handle ... partial thing */ *flags |= LF_PARTIAL; /* were dots, so set flag */ if (strchr(cp, '@')) /* if contains an at */ *flags |= LF_MAILBOX_ONLY; /* then a mbx-only search */ do { if (!(cp = do_magic(cp, &cmd, &function, flags))) break; /* handle/strip magic chars */ next = get_word(cp, word); /* then isolate this word */ if (((i = s_tbluk(word)) < 0) || /* if not a keyword or */ (!(commands[i].flags & LF_NO_ARG) && !*next)) /* arg-starved */ break; /* then do default action */ if (commands[i].function) { /* run a function? if so, */ if (function) /* and already have one, */ break; /* then must be text. */ function = commands[cmd = i].function; /* this is the funct */ } *flags |= commands[i].flags; /* set flags for keyword */ } while (i && (cp = next)); *rest = cp; /* set users pointer to it */ return cmd; } /* * given a string s, strip off any trailing partial-match characters, * e.g. dots. returns TRUE if found any, else FALSE; allow '*' in * place of '.' for people who do "foo*" */ bool do_partial(s) char *s; { int i, c; char *cp; cp = s + (i = strlen(s)) - 1; /* point to last char */ if ((c = *cp) == '.' || c == '*') { /* trailing dot means */ while (--i > 0 && *--cp == c) ; /* step back over 'em all */ if (i > 0) { /* if "foo..." then */ *++cp = '\0'; /* remove the dots and */ return TRUE; /* flag partial match */ } } return FALSE; } /* * Old-style parse wants command of the form "text" * where char is "!" for handle-only, "#" for dump record, etc. */ char *do_magic(s, cmd, routine, flags) char *s; int *cmd, *flags; int (**routine)(); { char *cp; int c, i; for (cp = s; (c = *cp) && (i = c_tbluk(c)) >= 0; cp++) { if (commands[i].function) { /* if a function char but */ if (*routine) break; /* already have function!! */ *routine = commands[*cmd = i].function; /* type char */ } *flags |= commands[i].flags; /* maybe set some flags */ } return cp; } /* * given a cp to a string, write a copy of the next "word" to the given * buffer, skipping leading whitespace. a "word" is some positive * number of contiguous non-whitespace characters. next_cp is set * to point to after the word, and the function returns TRUE if * a word was found, else FALSE (with the dest buffer unchanged). */ char *get_word(cp, buf) char *cp, *buf; { char *original_buf = buf; while (isspace(*cp)) cp++; /* skip leading whitespace */ while (*cp && !isspace(*cp)) /* copy "word" to dest */ *buf++ = *cp++; /* buffer... */ if (buf != original_buf) { /* if found&wrote any chars */ *buf = '\0'; /* then tie off dest */ while (isspace(*cp)) cp++; /* remove trailing white */ return cp; /* pt rest of string & win */ } else return NULL; /* else take lose return */ } /* * strip_trailing_white(s) * * given a string pointer, make sure there are no whitespace characters * immediately behind the terminating null. * */ void strip_trailing_white(s) char *s; { int i; char c; for (i = (strlen(s) - 1); i >= 0; i--) if (((c = s[i]) != ' ') && (c != '\t')) break; s[++i] = '\0'; } /* * s(tring)_tbluk * * given a keyword and pointer to a command table and the size of that * table, see if the keyword is in that table. checks all keywords in * the table to test for multiple matches (ambiguity); for a single match * returns the index into that command table, 0 for no match, -1 for * ambiguous. * * TBLUK% is the TOPS-20 JSYS which does this sort of table-lookup. */ int s_tbluk(key) char *key; { int i, length, cmd = 0; /* 0+1 is default action */ char *cp; length = strlen(key); for (cmd = i = 0; i < N_COMMANDS; i++) { if (!(cp = commands[i].name)) /* point to command */ continue; /* might not be one, tho */ if (!strnCMP(key, cp, length)) { /* case insensitive compare */ if ((commands[i].flags & LF_NO_ABBREV) && length < strlen(cp)) continue; /* can't be abbreviated */ if (cmd) return -1; /* ambiguous, multiple match */ else cmd = i; /* save cmd */ } } return cmd; } /* * c(har)_tbluk * * like s_tbluk, but looks for a single-char match with the magic * char section of a command table. */ int c_tbluk(c) char c; { int i; for (i = 0; i < N_COMMANDS; i++) if (c == commands[i].magic) return i; /* 1-based, not 0-based */ return -1; } /* * help file stuff plus support * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * type out a mini command summary */ bool options(mb) match_block *mb; { if (!type_file(OPTIONS_FILE, mb->flags)) { printf("Sorry, can't find options file %s\n", OPTIONS_FILE); puts("Try the help file perhaps? Do \"HELP\""); return FALSE; } return TRUE; } /* * type out the help file */ bool help(mb) match_block *mb; { char *help_file = NULL; if (ON(F_HOST_MODE)) help_file = HOST_HELP_FILE; else if (OFF(F_INTERACTIVE)) help_file = WHOIS_HELP_FILE; if (help_file) { if (!type_file(help_file, mb->flags)) { printf("Sorry, can't find help file %s -- %s\n", help_file, strerror(-1)); return FALSE; } } else if (!run_program(HELP_PROGRAM, HELP_KEYWORD)) { printf("Sorry, couldn't run help program %s\n", HELP_PROGRAM); return FALSE; } return TRUE; } /* * given the filespec of an .EXE file to load and run, and the text * which is to be argv[1] (where arvg[0] gets the exe filespec), * runs the program. returns TRUE/FALSE for success. */ bool run_program(prog, text) char *prog, *text; { #if SYS_T20 struct frkxec frkb; char rscan_buf[MAX_LINE]; frkb.fx_name = prog; /* filespec of .exe */ frkb.fx_flags = FX_WAIT | FX_T20_RSCAN; /* wait for fork to terminate */ sprintf(frkb.fx_blkadr = rscan_buf, "HELP %s\n", text); frkb.fx_envp = NULL; /* environment vector */ frkb.fx_fdin = frkb.fx_fdout = -1; /* no new stdin out stdout */ return (forkexec(&frkb) == -1) ? FALSE : TRUE; #else return FALSE; #endif } /* * type out the given file, returning truth as to success */ bool type_file(file, flags) char *file; unsigned flags; { FILE *f; char line[MAX_LINE]; int c, line_count; if (!(f = fopen(file, "r"))) /* open file to be typed */ return FALSE; /* oops, can't... */ if (OFF(F_INTERACTIVE) || (flags & LF_EXPAND)) /* if you said *help or */ while ((c = getc(f)) != EOF) /* we're a server, then */ putchar(c); /* just blast it all out! */ else { /* else do it with -more-. */ for (line_count = 0; OFF(F_ABORT) && fgets(line, MAX_LINE, f);) { if (height && ++line_count == height - 1) { if (ON(F_ABORT) || !yesno(more)) break; /* aborting or no more, pls */ line_count = 1; /* new screen, first line */ } fputs(line, stdout); /* blast out the line */ } /* while there is input */ } fclose(f); /* all done with file */ return TRUE; } /* * high-level finder routines * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * this is the default-action case for when the user does not specify * a specific type of record. try to match on absolutely everything, * since we have no idea what they are looking for, but stick to fields * which have sort-tables! */ bool f_anything(mb) match_block *mb; { if (ON(F_HOST_MODE) && !(mb->flags & LF_WHOIS)) /* running as HOST? */ do_host(mb); /* only look for host stuff */ else { do_mailbox(mb); do_host(mb); find(mb, i_impname); /* bonus checks!! */ find(mb, i_netname); find(mb, i_netnumber); find(mb, i_impnetnumber); } return TRUE; /* Always true for now */ } /* * check for the "usual" things. this finder is called by most of * the other to do searches that are in common in (almost) all cases. */ bool f_usual(mb) match_block *mb; { if (!(mb->flags & LF_ONLY_FIELDS)) { find(mb, i_handle); /* "usual" consists of handle */ do_name(mb); /* and name... */ do_mailbox(mb); } if (mb->flags & LF_HANDLE_ONLY) find(mb, i_handle); /* "usual" consists of handle */ if (mb->flags & LF_NAME_ONLY) do_name(mb); /* and name... */ if (mb->flags & LF_MAILBOX_ONLY) do_mailbox(mb); return TRUE; /* Always true for now */ } /* * name matching is special. if they give a name with no comma, e.g. * "Smith", and they don't want partial match, then what we will * check for is any name starting with text+"," e.g. "Smith," since * names are store in the database as "last, first". Also, we check * for their text then a space, to catch the first word of a multi- * word name, plus their text and a dot, for initial matching. */ bool do_name(mb) match_block *mb; { char *at, *tp, strbuf[MAX_LINE]; int n; /* * if we're given , insert a space * after the comma, assuming they just messed up last,first. */ if ((at = strchr(mb->target, ',')) && at != mb->target && isalpha(*(at-1)) && isalpha(*++at)) { strncpy(strbuf, mb->target, n = at - mb->target); strcpy(strbuf+n, " "); /* copy old string up to the comma, then */ strcat(strbuf, at); /* add a space and the rest of the original */ at = mb->target; /* save old target; at is used as a flag! */ mb->target = strbuf; /* strbuf contains the new target */ } else at = NULL; /* flag that we didn't clobber original target */ /* * if they gave us something with a trailing initial (the last char * is a letter and the char before is a space), then do a partial * match so we find everyone with a first name starting with that. * otherwise, just do the lookup normally. */ if ((n = strlen(mb->target)) > 3 && *(tp = mb->target + n - 3) == ',' && *++tp == ' ' && isalpha(*++tp)) find_temp(mb, i_name, mb->flags | LF_PARTIAL, NULL); else find(mb, i_name); /* ---DO IT--- */ /* * first see if there's an exact (or partial, if requested) match * for name. if partial, then that's it! we're done. else... */ if (!(mb->flags & LF_PARTIAL)) { /* * if a full match, then do some additional searches. check for the * text with a comma after it (like for a lastname), a dot after it * like for an initial, or a space after it (for some words in a multi- * word name). all of these searches should look at the same parts * of the name sort table, which only has to be loaded once, so it * should be pretty fast. */ if (mb->target != strbuf) /* if using original, */ strcpy(strbuf, mb->target); /* copy it now to strbuf. */ tp = strbuf + strlen(strbuf); /* point after the text */ *tp = ' '; /* add a trailing space */ *(tp + 1) = '\0'; /* & a null after the space */ find_temp(mb, i_name, mb->flags | LF_PARTIAL, strbuf); *tp = ','; /* now try a comma */ find_temp(mb, i_name, mb->flags | LF_PARTIAL, strbuf); *tp = '.'; /* now try a dot */ find_temp(mb, i_name, mb->flags | LF_PARTIAL, strbuf); } if (at) mb->target = at; /* restore original target */ return TRUE; /* Always true for now */ } bool do_mailbox(mb) match_block *mb; { char *cp, *from, *to, *atsign, *host; char mailbox[MAX_LINE], new_host[MAX_LINE]; /* * if they just said "foo", then we want to look for "foo@anything", * so gen up "foo@" in a buffer and do a partial search. if they * did "foo.", then just look it up. */ if (!strchr(mb->target, '@')) { /* explicit mailbox? */ if (mb->flags & LF_PARTIAL) { /* no. partial match? */ find(mb, i_mailbox); return; } strcpy(mailbox, mb->target); /* copy the original word */ strcat(mailbox, "@"); /* then add an atsign */ find_temp(mb, i_mailbox, mb->flags | LF_PARTIAL, mailbox); *strchr(mailbox, '@') = '%'; /* try with a '%' now */ find_temp(mb, i_mailbox, mb->flags | LF_PARTIAL, mailbox); return; } /* --- find start of hostname, after the at and any whitespace ------------ */ for (atsign = host = strrchr(mb->target, '@'); isspace(*++host);) ; /* --- back up over space after username before atsign -------------------- */ for (cp = atsign - 1; cp >= mb->target && isspace(*cp); cp--) ; /* --- construct final mailbox -------------------------------------------- */ for (from = mb->target, to = mailbox; from <= cp; ) *to++ = *from++; /* copy username part */ *to++ = '@'; /* add atsign */ strcpy(to, host); /* then add official mailbox */ find_mailbox(mb,mailbox); /* try to find exact match */ if (!mb->count) /* any matches? */ { /* nope. */ /* ---- now canonicalize and use offical name if different */ if (!(mb->flags & LF_PARTIAL) /* Skip this if partial */ && canonicalize_host(host, new_host, mb->flags)) { /* First output the result of our first failed search. */ display_matches(mb, sum_line, TRUE, FALSE); printf("[Looking for official hostname %s instead of \"%s\"]\n", new_host, host); host = new_host; } /* --- construct final mailbox -------------------------------- */ for (to = from = mb->target; from <= cp; ) *to++ = *from++; /* copy username part */ *to++ = '@'; /* add atsign */ strcpy(to, host); /* then add official mailbox */ find_mailbox(mb,mb->target); } return(TRUE); } /* * if starts with @, "@host", then call the suffix mailbox checker, * else if ends is @, "ian@" do a simple partial search, else do the * full lookup on the cleaned up mailbox. */ void find_mailbox(mb, mailbox) match_block *mb; char *mailbox; { char *cp; if (*mailbox == '@') s_mailbox(mb, mailbox); else { /* Use partial match if arg is "user@" or "user@h..." */ cp = mailbox + strlen(mailbox) - 1; /* point to last char */ find_temp(mb, i_mailbox, mb->flags | ((*cp == '@') ? LF_PARTIAL : 0), mailbox); } } /* * suffix mailbox, "@bar", which is a bitch. we let FNDSBX do the * work of finding potential matches (potential only since "@foo" * could match "@foonix"), then grab the records and verify that * it's really the suffix we want. */ bool s_mailbox(mb, target) match_block *mb; char *target; { idstr_t key; idrix_t record; int length; char *cp; if (ON(F_INTERACTIVE)) puts("[Scanning database... this will take a little while!]"); search_initialize(&mb->fndblk, ID_FNDT_SUBS|ID_FNDF_NOREC, i_mailbox, target); length = strlen(target); /* length of target */ while (id_recfind(&mb->fndblk)) { /* potential match */ record = mb->fndblk.fnd_rix; if (!(mb->flags & LF_PARTIAL)) { /* Get ptr to place where '@' will be if "@foo" matches "x@foo" */ key = mb->fndblk.fnd_str; /* Get entry string */ cp = ID_SCP(key) + ID_SLN(key) - length; if (*cp != '@') continue; /* key must END with target */ } if (!get_htype(record)) continue; /* Ignore if invisible htype */ if (!save_record(mb, record)) break; /* save this record */ } return TRUE; /* Always true for now */ } bool f_imp(mb) match_block *mb; { find(mb, i_nicknames); /* nickname will do */ find(mb, i_impname); /* impname or */ find(mb, i_impnumber); /* impnumber or */ return TRUE; /* Always true for now */ } bool f_domain(mb) match_block *mb; { char strbuf[MAX_LINE]; find(mb, i_domainame); /* find domains */ strcpy(strbuf, mb->target); /* If given FOO look for FOO. also */ strcat(strbuf, "."); find_temp(mb, i_domainame, mb->flags | LF_PARTIAL, strbuf); return TRUE; /* Always true for now */ } bool do_host(mb) match_block *mb; { find(mb, i_hostname); find(mb, i_nicknames); find(mb, i_netaddress); find(mb, i_domainame); #if AUTO_EXP /* Automatically expand the target for a partial-match */ /* search, even though the user didn't ask for it... */ if (!(mb->flags & LF_PARTIAL)) { strcpy(strbuf, mb->target); tp = strbuf + strlen(strbuf); *tp = '.'; /* let's try tacking on a dot */ *(tp + 1) = '\0'; /* make into real string */ find_temp(mb, i_hostname, mb->flags | LF_PARTIAL, strbuf); find_temp(mb, i_nicknames, mb->flags | LF_PARTIAL, strbuf); find_temp(mb, i_domainame, mb->flags | LF_PARTIAL, strbuf); find_temp(mb, i_netaddress, mb->flags | LF_PARTIAL, strbuf); *tp = '-'; /* try adding a hyphen */ find_temp(mb, i_hostname, mb->flags | LF_PARTIAL, strbuf); find_temp(mb, i_nicknames, mb->flags | LF_PARTIAL, strbuf); find_temp(mb, i_domainame, mb->flags | LF_PARTIAL, strbuf); } #endif return TRUE; /* Always true for now */ } bool f_network(mb) match_block *mb; { char strbuf[MAX_LINE]; find(mb, i_netname); /* network name */ find(mb, i_netnumber); /* or number will do */ if (!(mb->flags & LF_PARTIAL)) { /* allow "net 10" */ strcpy(strbuf, mb->target); /* make a copy */ strcat(strbuf, "."); /* then try it with a dot */ find_temp(mb, i_netnumber, LF_PARTIAL, strbuf); } return TRUE; /* Always true for now */ } bool f_org(mb) match_block *mb; { find(mb, i_nicknames); return TRUE; /* Always true for now */ } bool f_tac(mb) match_block *mb; { if (!find(mb, i_tacnumber)) do_host(mb); return TRUE; /* Always true for now */ } /* * low-level finder routines * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * do the real work of finding. does an ID_FNDT_FULL or ID_FNDT_PREF * depending on LF_PARTIAL. throws out X-records when you're not * allowed to see them, and records which don't have one of the * given htypes. matches are saved via save_match() and returns 0. */ idrix_t find(mb, field) match_block *mb; iditm_t field; { idrix_t record; int c, old_count, ftyp; if (ON(F_ABORT)) /* don't do any more */ return 0; /* searches if aborting. */ /* Continue previous search, or start new one? */ if (!(mb->flags & LF_FIND_ASKFUL) || mb->count != AUTO_SHOW) { switch (mb->flags & (LF_PARTIAL | LF_SUBSTRING)) { case LF_PARTIAL: ftyp = ID_FNDT_PREF; break; case LF_SUBSTRING: ftyp = ID_FNDT_SUBS; break; default: ftyp = ID_FNDT_FULL; } search_initialize(&mb->fndblk, ftyp, field, mb->target); } mb->searches++; /* doing another search */ for (old_count = mb->count; OFF(F_ABORT) && id_recfind(&mb->fndblk);) { record = mb->fndblk.fnd_rix; /* recidx for match */ if (!(c = get_htype(record)) || (mb->htypes && !strchr(mb->htypes, c))) continue; /* doesn't match! */ if ((mb->flags & (LF_ARPANET | LF_MILNET)) && !right_net(mb)) continue; if (!save_record(mb, record) || (mb->flags & LF_FIND_FIRST) || ((mb->flags & LF_FIND_ASKFUL) && mb->count == AUTO_SHOW)) break; } return (mb->count > old_count) ? record : 0; } /* * does a find() with temporary flags and/or target */ idrix_t find_temp(mb, field, flags, target) match_block *mb; iditm_t field; unsigned flags; char *target; { unsigned original_flags; char *original_target; idrix_t result; original_target = mb->target; /* save original target */ if (target) mb->target = target; /* substitute alternate */ original_flags = mb->flags; /* save original flags */ mb->flags = flags; /* use temporary flags */ result = find(mb, field); /* do the real finding. */ mb->flags = original_flags; /* restore original flags */ mb->target = original_target; /* restore original target */ return result; } /* * does find() function, but for targets that might have more than * MAX_MATCHES matches. Instead of forbidding more searches when * called with a full match block, it clears the match block and * iterates for the next batch. To accomplish this, it sets the * full-block flag when it fills up, not on the subsequent call * the way the other save_match() does for find(). * When LF_RECORDS_FULL is set on return, the calling routine is * responsible for dealing with the contents before calling this * routine again. * Note also that the running count of matches is maintained in the * count argument. The new count the return value, too. */ int find_big(mb, field, count) match_block *mb; iditm_t field; int count; { idrix_t record; int c, old_count, ftyp; if (ON(F_ABORT)) return(0); /* Continue previous search, or start new one? */ if (count == 0) { switch (mb->flags & (LF_PARTIAL | LF_SUBSTRING)) { case LF_PARTIAL: ftyp = ID_FNDT_PREF; break; case LF_SUBSTRING: ftyp = ID_FNDT_SUBS; break; default: ftyp = ID_FNDT_FULL; } search_initialize(&mb->fndblk, ftyp, field, mb->target); } /* See if we need to wrap the search this time */ if (mb->count == MAX_MATCH) { mb->count = mb->key_count = 0; mb->flags &= ~LF_RECORDS_FULL; mb->key_cp = mb->key_buffer; } mb->searches++; /* doing another search */ for (old_count = count; OFF(F_ABORT) && id_recfind(&mb->fndblk);) { record = mb->fndblk.fnd_rix; /* recidx for match */ if (!(c = get_htype(record)) || (mb->htypes && !strchr(mb->htypes, c))) continue; /* doesn't match! */ if ((mb->flags & (LF_ARPANET | LF_MILNET)) && !right_net(mb)) continue; if (!save_record(mb, record)) break; ++count; if ((mb->flags & LF_FIND_FIRST) || ((mb->flags & LF_FIND_ASKFUL) && count == AUTO_SHOW)) break; if (mb->count >= MAX_MATCH) { mb->flags |= LF_RECORDS_FULL; break; } } return(count - old_count); } /* * record and key saving routines. save records in the match block, * and keys in the key buffer in the given match block. * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * save a match in the match structures with the given text for * a sorting key. */ bool save_match(mb, record, key) match_block *mb; idrix_t record; char *key; { int i; if (mb->flags & LF_RECORDS_FULL) /* already full? */ return FALSE; if (mb->count >= MAX_MATCH) { /* don't think so. room? */ printf("Can't save more than %d matches.\n", MAX_MATCH); mb->flags |= LF_RECORDS_FULL; /* flag that we know this */ return FALSE; } for (i = 0; i < mb->count; i++) /* check for duplicates */ if (record == mb->recidx[i]) return TRUE; /* already have this one */ if (record != id_recidx && !get_record(record)) return FALSE; mb->records[mb->count] = mb->count; /* not sorted yet. */ mb->recidx[mb->count] = record; /* save unique recidx */ mb->keys[mb->count++] = key; /* save the sorting key */ return TRUE; } /* * save a record keyed by its name, or handle if no name */ bool save_record(mb, record) match_block *mb; idrix_t record; { char *key; key = save_idstrs(mb, mb->key_field, i_handle); if (!save_match(mb, record, key)) return FALSE; /* failed to save */ return TRUE; } /* * save the contents of the given field in the key-string buffer */ char *save_idstrs(mb, fld1, fld2) match_block *mb; iditm_t fld1, fld2; { char *p, *key; idstr_t itm1, itm2; if (mb->flags & LF_KEYS_FULL) return NULL; /* no room at the inn */ if (get_item(fld1, &itm1)) /* get the first field */ mb->key_count += ID_SLN(itm1) + 1; /* store it, account 4 null */ else fld1 = -1; /* oops, isn't there */ if (fld2 != -1) { /* get second field */ if (!get_item(fld2, &itm2)) fld2 = -1; else mb->key_count += ID_SLN(itm2); } if (mb->key_count > KEY_BUF_SIZE) { fputs("Ran out of room in key_buffer[]!\n", stderr); mb->flags |= LF_KEYS_FULL; return NULL; } key = p = mb->key_cp; /* start of key */ if (fld1 != -1) { strncpy(p, ID_SCP(itm1), ID_SLN(itm1)); /* save first field */ p += ID_SLN(itm1); /* point to after it */ } if (fld2 != -1) { /* save 2nd field if any */ strncpy(p, ID_SCP(itm2), ID_SLN(itm2)); p += ID_SLN(itm2); } *p++ = '\0'; /* tie off key string */ mb->key_cp = p; /* update pointer to next */ return key; /* free and return cp 2 key */ } /* * save the given string in the key-string buffer */ char *save_string(mb, s) match_block *mb; char *s; { char *old_cp; int n; if (mb->flags & LF_KEYS_FULL) return NULL; n = strlen(s) + 1; /* # of chars written */ mb->key_count += n; /* update count */ if (mb->key_count > KEY_BUF_SIZE) { fputs("Ran out of room in key_buffer[]!\n", stderr); mb->flags |= LF_KEYS_FULL; return NULL; } strcpy(mb->key_cp, s); /* copy to perm buffer */ old_cp = mb->key_cp; /* save where we wrote */ mb->key_cp += n; /* advance key_cp */ return old_cp; } /* * high-level display routines * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * display routines. call this after doing a high-level f_something; * if there were no matches, it generates the "No [partial ] match * for %s" message. it returns the number of matches. */ void no_matches(mb) match_block *mb; { char *p; int c; if (mb->count) return; /* this is for NO matches?? */ fputs("No match for ", stdout); if (mb->flags & LF_PARTIAL) fputs("partial ", stdout); if (mb->flags & LF_ARPANET) { fputs("ARPANET ", stdout); if (mb->flags & LF_MILNET) fputs("or ", stdout); } if (mb->flags & LF_MILNET) fputs("MILNET ", stdout); if (commands[mb->cmd_index].name) printf("%s ", commands[mb->cmd_index].name); if (mb->flags & LF_ONLY_FIELDS) { if (mb->flags & LF_HANDLE_ONLY) fputs("handle ", stdout); if (mb->flags & LF_NAME_ONLY) { if (mb->flags & LF_HANDLE_ONLY) fputs("or ", stdout); fputs("name ", stdout); } if (mb->flags & LF_MAILBOX_ONLY) { if (mb->flags & (LF_HANDLE_ONLY | LF_NAME_ONLY)) fputs("or ", stdout); fputs("mailbox ", stdout); } } putchar('"'); /* show funny characters */ for (p = mb->target; c = *p; p++) if (isprint(c)) putchar(c); else { putchar('^'); putchar(CONTROL(c)); } puts("\"."); } /* * display the results of the last search. cmb is expected to * point to the match_block of the results to be displayed. if there's * more than one record, sort them by name and do a 1-line summary of * each. if there's just one record, do the appropriate display, * depending on the record type. */ bool display_matches(mb, function, ask_flag, divider_flag) match_block *mb; int (*function)(); bool ask_flag, divider_flag; { int i; bool keep_going = TRUE; int_on(); /* turn on interrupts */ if (!mb->count) no_matches(mb); else if (mb->count == 1 && !(mb->flags & LF_SUMMARY)) out_record(mb, mb->recidx[0]); /* single match or always */ else { sort_matches(mb); /* sort multiple matches */ if (!(mb->flags & LF_FULL_OUTPUT)) { keep_going = summary(mb, function, ask_flag, divider_flag); if (OFF(F_NIC_USER) && (ON(F_ONCE_THROUGH) || OFF(F_INTERACTIVE))) puts(single_out); } else { for (i = 0; OFF(F_ABORT) && i < mb->count; i++) { if (i) fputs(divider, stdout); out_record(mb, mb->recidx[mb->records[i]]); } } } int_off(); return(keep_going); } /* * this is the standard output routine for displaying the results * of a subdisplay, like users of host, hosts on net, etc. */ bool sub_matches(mb, what, summary_function, ask_flag, divider_flag, time_thru) match_block *mb; char *what; int (*summary_function)(); bool ask_flag, divider_flag; int time_thru; { bool maxed_out = FALSE; bool keep_going = FALSE; if (!mb->count) printf("Program error, couldn't find any %ss.\n", what); else { if (mb->count == 1) printf("%s%sThere is one %s%s:\n\n", time_thru ? "\n" : "", indent, time_thru ? "more " : "", what); else { maxed_out = (mb->count == MAX_MATCH); printf("%s%sThere are %s%d %s%ss:\n", time_thru ? "\n" : "", indent, maxed_out ? "at least " : "", mb->count, time_thru ? "more " : "", what); if (maxed_out && !time_thru) printf("%s(Each block of %d will be sorted separately)\n", indent, MAX_MATCH); printf("\n"); sort_matches(mb); } mb->flags &= ~LF_SUBDISPLAY; /* don't do sub-sub-display */ mb->flags |= LF_SUMMARY; /* and always summarize */ keep_going = display_matches(mb, summary_function, ask_flag, divider_flag); } return(keep_going); } /* * output the given single record. if they want it dumped, then * use dump_record(), else use the appropriate display routine * depending on the htype. */ void out_record(mb, record) match_block *mb; idrix_t record; { int c, i; void (*main)() = standard_header, (*sub)() = NULL; if (!get_record(record)) return; /* get the single record. */ if (ON(F_NIC_USER) && (mb->flags & LF_DUMP)) { /* want a raw dump? */ dump_record(); /* yep */ return; /* and we're done! */ } if (!(c = get_htype(NULL))) { /* use current record */ puts("Sorry, you shouldn't see this record."); return; } if (strchr(HOST_HTYPES, c) && ON(F_HOST_MODE) && !(mb->flags & LF_WHOIS)) main = d_athost; else for (i = 0; i < N_HTYPES; i++) if (htypes[i].htype == c) { main = htypes[i].main; sub = htypes[i].sub; break; } if (mb->flags & LF_SUBDISPLAY) /* show the single match first */ summary(mb, sum_line, FALSE, FALSE); else ((*main)()); /* call the main display */ if (sub) { /* is there a sub-display? */ if (!(mb->flags & LF_NO_SUB)) { putchar('\n'); ((*sub)(mb)); /* then show it... */ } } else /* no sub-display. */ if (mb->flags & LF_SUBDISPLAY) { /* did they expect one? */ if (i < N_HTYPES) printf("There is no subdisplay for a %s record!\n", htypes[i].name); else puts("There is no subdisplay for that record type!"); } } /* * dump the current record as "(n) label: text", where n is the internal * field# (not the field name) */ void dump_record() { iditm_t item; idstr_t itmbuf; for (item = -1; OFF(F_ABORT) && id_itmnext(&item, &itmbuf);) { printf("(%d) ", item); id_fputs(id_idfname(item), stdout); fputs(": ", stdout); id_fputs(itmbuf, stdout); putchar('\n'); } } /* * summary gives a 1-line summary of each person in the block. * * if (ask_p) then the user is asked, after ASK_AFTER records * have been displayed, if he wants to continue. If the total * number of records is <= AUTO_SHOW, then they're all just * showed, with no asking. * * if (divide_p), then after every BREAK_EVERY lines a divider * is output, to break it up for easier reading. */ bool summary(mb, routine, ask_p, divide_p) match_block *mb; int (*routine)(); bool ask_p, divide_p; { int i; int_clear(); for (i = 0; OFF(F_ABORT) && i < mb->count; i++) { if (ask_p && ON(F_INTERACTIVE) && (i == ASK_AFTER) && !(mb->flags & LF_EXPAND) && mb->count > AUTO_SHOW && !yesno("There are %d more matches. Show them? ", mb->count - ASK_AFTER)) return(FALSE); if (divide_p && i && !(i % BREAK_EVERY)) fputs(divider, stdout); /* output divider */ if (!(*routine)(mb->recidx[mb->records[i]])) return(FALSE); /* output routine bombed! */ } return(TRUE); } /* * this is the standard routine given to summary() to call for each * match. it does "name (handle) x y" where x and y are different * fields, depending on the record type. */ int sum_line(record) idrix_t record; { int i, c; if (get_record(record)) { column = name_and_handle(); /* returns length written */ c = get_htype(NULL); for (i = 0; i < N_HTYPES; i++) if (c == htypes[i].htype) { s_rest(htypes[i].item1, htypes[i].item2); return 1; } putchar('\n'); return 1; } printf("Error - could not retrieve record %d.\n", record); return 0; } /* * this is the routine which outputs the variable fields x and y. * the extra cruft for dealing with field2 is because phone fields * are often weird, with leading CRLFs and so on. */ void s_rest(f1, f2) iditm_t *f1, *f2; { idstr_t buf1, buf2; char *p, *start; int len, body; if (f1 && get_item(*f1, &buf1) && id_siget(*f1, &buf1, &buf2)) { space(MBX_COLUMN - 1 - column); id_fputs(buf2, stdout); column += ID_SLN(buf2); } if (get_item(*f2, &buf1)) { for (len = ID_SLN(buf1), start = ID_SCP(buf1); len > 0 && isspace(*start); len--, start++) ; /* skip leading cruft */ for (body = 0, p = start; len > 0 && *p != '\r' && *p != '\n'; p++, len--, body++) ; /* only use the first line! */ if ((column + body) >= width - 1) { putchar('\n'); /* if won't fit on this line, */ column = 0; /* go to the next */ } space(width - 2 - column - body); while (--body >= 0) /* output the field */ putchar(*start++); } putchar('\n'); } /* * this is the special display used for host records and such when * displaying the liaison etc. it's a quicky 2-line summary with * no columnation: "name (handle) mailbox" and phone on the next line. */ void contact_summary() { idstr_t itmbuf, mbxbuf; printf("%s%s", indent, indent); /* double-indent for this */ if (x_record()) fputs("X:", stdout); if (get_item(i_name, &itmbuf)) id_fputs(itmbuf, stdout); else fputs("[No name]", stdout); fputs(" (", stdout); if (get_item(i_handle, &itmbuf)) id_fputs(itmbuf, stdout); else fputs("ID!?", stdout); fputs(") ", stdout); if (get_item(i_mailbox, &itmbuf) && id_siget(i_mailbox, &itmbuf, &mbxbuf)) id_fputs(mbxbuf, stdout); else fputs("[No mailbox]", stdout); printf("\n%s%s", indent, indent); /* double-indent for fone */ if (get_item(i_phone, &itmbuf)) id_fputs(itmbuf, stdout); else fputs("[No phone]", stdout); putchar('\n'); /* get to a new line */ } /* * special hack for displaying multiple contacts for a record. in * case a single person holds more than one title, it does * * Foo, Bar: * person's information * * instead of * * Foo: * person's information * Bar: * same person's information again */ void d_contacts(cb, n) struct contact_struct cb[]; int n; { iditm_t current; idstr_t itmbuf; char *cp, strbuf[MAX_LINE]; int i, j, count; current = id_recidx; /* save the current record */ cp = strbuf; /* use this buffer */ /* * first grab and save all the handle-strings from the fields, since * this record is only current NOW, and will cease to be as soon as * we start to look up any of those people. */ for (i = 0; i < n; i++) { if (get_item(*cb[i].field, &itmbuf)) { /* if field exists */ cb[i].handle = cp; /* save cp to buf */ id2c(cp, itmbuf); /* copy it here. */ cp += ID_SLN(itmbuf) + 1; /* move to next pos in buf */ } else cb[i].handle = NULL; /* didn't find it. */ } /* * now look up those handles we just got, and turn them into * record indicies. -1 means not found or x-user or whatever. */ for (i = 0; i < n; i++) { if (cb[i].handle && id_recgethdl(id_strfs(cb[i].handle)) && get_htype(NULL)) cb[i].record = id_recidx; else cb[i].record = -1; } /* * now do the display */ count = 0; for (i = 0; i < n; i++) { if (cb[i].record >= 0) { if (!count++) putchar('\n'); /* blank line b4 display */ fputs(indent, stdout); if (cb[i].label) fputs(cb[i].label, stdout); else label(*cb[i].field); for (j = i + 1; j < n; j++) if (cb[i].record == cb[j].record) { fputs(", ", stdout); if (cb[j].label) fputs(cb[j].label, stdout); else label(*cb[j].field); cb[j].record = -1; /* -1 so no re-found! */ } puts(":"); get_record(cb[i].record); /* make record current */ contact_summary(); /* show that person */ } } get_record(current); /* re-get the old record */ } /* * per-record-type display routines. these are the high-level * routines which do the large-scale nice outputs for each different * htype. * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * display routine for an individual, htype = "I". */ void d_individual() { idstr_t itmstr; char *itmchar; standard_header(); if (get_item(i_validcard, &itmstr)) { itmchar = ID_SCP(itmstr); if ((itmchar[0] == 'y') || (itmchar[0] == 'Y')) printf("%sMILNET TAC user\n", indent); } comments(); /* display comments if any */ d_updated(); /* show record-update date */ } void d_host() { standard_header(); putchar('\n'); d_hinfo(); /* display host info */ printf("%sSystem: ", indent); putz(i_cputype); printf(" running "); putz(i_opsys); putchar('\n'); d_contacts(host_contacts, N_HOST_CONTACTS); comments(); /* show comments in any */ d_updated(); /* show record-update date */ } /* * if there are any records out there that have this host for * their host field, then: if they didn't request auto-expansion * of users, ask if they want to see them. if so, show 'em. */ void d_s_host(omb) match_block *omb; { char strbuf[MAX_LINE]; idstr_t itmbuf; match_block *mb; int count; int time_thru = 0; get_item(i_handle, &itmbuf); id2c(strbuf, itmbuf); /* save host's ident here */ if (!(mb = get_match_block())) /* get a match block */ return; /* can't play ball without! */ match_initialize(mb, strbuf, i_name, omb->flags | LF_FIND_ASKFUL, NULL); if (!find(mb, i_host)) /* find first AUTO_SHOWful */ printf("%sNo registered users.\n", indent); /* oops, none */ else if (((count = mb->count) != AUTO_SHOW) || (see_more(mb, v3, "registered users of this host"))) { do { count += find_big(mb, i_host, count); if (!sub_matches(mb, "registered user", sum_line, (count <= MAX_MATCH), TRUE, time_thru++)) break; } while (mb->count == MAX_MATCH); if (count >= MAX_MATCH) printf("\n%sTotal of %d records.\n\n", indent, count); } free_match_block(mb); /* done with match block */ } void d_hinfo() { idstr_t nicbuf, dombuf; d_labeled(i_hostname, NULL, 0); get_item(i_nicknames, &nicbuf); get_item(i_domainame, &dombuf); if (ID_SLN(nicbuf) > 0 || ID_SLN(dombuf) > 0) { printf("%sNicknames: ", indent); if (ID_SLN(dombuf) > 0) { id_fputs(dombuf, stdout); if (ID_SLN(nicbuf) > 0) fputs(", ", stdout); } if (ID_SLN(nicbuf) > 0) id_fputs(nicbuf, stdout); putchar('\n'); } d_labeled(i_netaddress, "Address", ','); } void d_imp() { idstr_t itmbuf; idfnd_t fndblk; char *cp, address[MAX_LINE], strbuf[MAX_LINE]; int host, net, third; bool first; standard_header(); putchar('\n'); if (get_item(i_autodin, &itmbuf)) { fputs(indent, stdout); printf("Autodin: %s\n\n",id2c(strbuf,itmbuf)); } fputs(indent, stdout); putz(i_impname); printf(" is PSN/IMP "); putz(i_impnumber); printf(" on network "); net = out_netname(i_impnetnumber); /* display network as #/name */ putchar('\n'); /* should only be class A */ d_contacts(imp_contacts, N_IMP_CONTACTS); comments(); d_updated(); /* show record-update date */ get_item(i_impnumber, &itmbuf); /* get the imp# */ cp = id2c(strbuf, itmbuf); /* copy it to buffer */ first = FALSE; /* first host not found yet */ for (host = 0; host < 128; host++) { sprintf(address, "%d.%d.0.%s", net, host, cp); search_initialize(&fndblk, ID_FNDT_FULL, i_netaddress, address); while (id_recfind(&fndblk)) { /* Find and get record */ if (!get_htype(NULL)) continue; if (!first) { /* if this's the 1st */ printf("\n%sHosts on this PSN:\n\n", indent); first = TRUE; /* this was it */ } host_line(NULL); /* host summary line */ /* Look for others behind this one, with nonzero third octets */ for (third = 1; third < 128; third++) { sprintf(address,"%d.%d.%d.%s",net,host,third,cp); search_initialize(&fndblk,ID_FNDT_FULL,i_netaddress,address); while (id_recfind(&fndblk) && get_htype(NULL)) host_line(NULL); } } } if (!first) printf("\n%sNo hosts found on this PSN.\n", indent); } void d_updated() { idstr_t itmbuf; char strbuf[MAX_LINE]; char upyr[3],upmo[3],updy[3]; get_item(i_updated, &itmbuf); id2c(strbuf, itmbuf); strncpy(upyr, &strbuf[0], 2); strncpy(upmo, &strbuf[2], 2); strncpy(updy, &strbuf[4], 2); upyr[2] = upmo[2] = updy[2] = '\0'; printf("\n Record last updated on %s-%s-%s.\n", updy,months[atoi(upmo)-1],upyr); } /* * given the # of a field containing a net#, type out that number, * then the name of the network. returns the first octet of the net# */ int out_netname(field) iditm_t field; { idstr_t netbuf; idrix_t current; char strbuf[MAX_LINE]; int net; current = id_recidx; /* save current recidx */ if (!get_item(field, &netbuf)) { /* get the network number */ puts("?"); /* into idstr_t netbuf. */ return 0; } net = net_part(strbuf, netbuf); /* get important part */ fputs(strbuf, stdout); /* display it. */ if (id_recgetitm(i_netnumber, netbuf)) { /* now get the name of this */ printf(" ("); /* network and display it */ putz(i_netname); printf(")"); get_record(current); /* re-get old record */ } return net; /* return the first octet */ } void d_domain() { standard_header(); putchar('\n'); d_labeled(i_domainame, "Domain Name", 0); d_contacts(domain_contacts, N_DOMAIN_CONTACTS); d_updated(); /* show record-update date */ d_dsrv(i_servercontact,"Domain servers in listed order"); comments(); } void d_dsrv(item, header) iditm_t item; char *header; { idstr_t buf1, buf2; idrix_t record; /* idfnd_t fndblk; */ int i, n_handles, n_hosts; char *cp, *handles[MAX_ADDR], strbuf[MAX_LINE]; /* match_block *mb; */ if (get_item(item, &buf1)) { record = id_recidx; /* save current rec */ n_handles = 0; /* # of host handles */ cp = strbuf; /* where to store em */ while (id_siget(item,&buf1,&buf2) && n_handles < MAX_ADDR) { handles[n_handles++] = id2c(cp, buf2); /* save handle */ cp += ID_SLN(buf2) + 1; /* advance cp */ } n_hosts = 0; /* # we really see */ for (i = 0; i < n_handles; i++) { /* loop over handles */ if (!id_recgethdl(id_strfs(handles[i])) || !get_htype(NULL)) continue; if (!n_hosts++) printf("\n%s%s:\n\n", indent, header); host_line(NULL); /* output da facts */ } if (!n_hosts) printf("\n%sNo known domain servers.\n", indent); get_record(record); /* re-get org record */ } } void d_s_domain(omb) match_block *omb; { idstr_t buf1; char strbuf[MAX_LINE]; match_block *mb; if (!(mb = get_match_block())) /* get a match block */ return; /* oops! */ get_item(i_domainame, &buf1); /* domain name */ id2c(strbuf, buf1); /* save it here */ if (!strchr(strbuf, '.')) { /* top-level? */ /* * if the domainame doesn't have a dot, then it must be a top-level * domain, so ask the user if they want to see all the domains under * that top-level domain. */ match_initialize(mb,strbuf,i_domainame,omb->flags|LF_FIND_ASKFUL,"D"); if (!find(mb, i_parentdom)) /* find first AUTO_SHOWful */ { printf("%sFor information concerning this domain, please consult\n", indent); printf("%sthe Administrative Contact listed above.\n",indent); } else switch (mb->count) { /* some matches. */ case AUTO_SHOW: /* maybe lots more, so ask */ if (!see_more(mb, v3, "known domains under this top-level domain")) break; /* don't want to see em */ find(mb, i_parentdom); /* do, so find all the rest */ default: sub_matches(mb, "known sub-domain", sub_domain, TRUE, FALSE, 0); } free_match_block(mb); /* done with this */ } else { /* * the domainame DID have a dot, so this is not a top-level domain, * but a secondary domain. we ask the user if they want to see all * known hosts under that domain. */ *strbuf = '.'; /* we want .dom */ id2c(strbuf + 1, buf1); /* reput it after . */ match_initialize(mb, strbuf, i_hostname, LF_SUBSTRING | LF_FIND_FIRST, HOST_HTYPES); if (find(mb,i_domainame) || find(mb,i_hostname) || find(mb,i_nicknames)) { if (see_more(omb, v3, "known hosts under this secondary domain")) { match_initialize(mb, strbuf, i_hostname, LF_SUBSTRING, HOST_HTYPES); find_temp(mb,i_hostname, 0, strbuf+1); find(mb, i_domainame); find(mb, i_hostname); find(mb, i_nicknames); sub_matches(mb, "known host", host_line, TRUE, FALSE, 0); } } else printf("%sNo known hosts under this secondary domain.\n", indent); free_match_block(mb); /* done with this */ } } int sub_domain(record) idrix_t record; { idstr_t itmbuf; if (record && !get_record(record)) { printf("Error - could not retrieve record %d.\n"); return 0; } if (x_record()) printf("%sX:", indent + 2); else fputs(indent, stdout); column = INDENT; if (!get_item(i_domainame, &itmbuf)) { putchar('!'); /* flag that typeout is handle */ get_item(i_handle, &itmbuf); /* this HAS to work! */ column++; } id_fputs(itmbuf, stdout); column += ID_SLN(itmbuf); space(SUB_DOM_COLUMN - 1 - column); putz(i_name); putchar('\n'); return 1; } void d_network() { standard_header(); putchar('\n'); d_labeled(i_netname, NULL, 0); d_labeled(i_netnumber, NULL, 0); d_contacts(coordinator, N_COORDINATOR); d_dsrv(i_inaddrserver,"Domain System inverse mapping provided by"); comments(); d_updated(); /* show record-update date */ } void d_s_network(omb) match_block *omb; { idstr_t itmbuf; idfnd_t fndblk; idrix_t record; char *key, strbuf[MAX_LINE], buf[MAX_LINE]; int c, b1, b2, b3, b4; match_block *mb; bool ignored; get_item(i_netnumber, &itmbuf); net_part(strbuf, itmbuf); /* get important part */ strcat(strbuf, "."); /* add a dot to the end */ itmbuf = id_strfs(strbuf); /* convert to idstr_t */ if (!(mb = get_match_block())) return; match_initialize(mb, strbuf, i_hostname, omb->flags, HOST_HTYPES); search_initialize(&fndblk, ID_FNDT_PREF, i_netaddress, strbuf); for (ignored = FALSE; id_recfind(&fndblk);) { record = fndblk.fnd_rix; if (!(c = get_htype(record)) || !strchr(HOST_HTYPES, c)) continue; if (!mb->count && !see_more(omb, v3, "hosts on this network")) { ignored = TRUE; break; } id2c(buf, fndblk.fnd_str); /* turn entry into C string */ if (sscanf(buf, "%d.%d.%d.%d", &b1, &b2, &b3, &b4) != 4) continue; if (b1 == NET_ARPANET || b1 == NET_MILNET) sprintf(buf, "%3d%3d%3d%3d", b1, b4, b2, b3); else sprintf(buf, "%3d%3d%3d%3d", b1, b2, b3, b4); if (!(key = save_string(mb, buf)) || !save_match(mb, record, key)) break; } if (!ignored) { if (!mb->count) printf("%sNo hosts found on this network.\n", indent); else sub_matches(mb, "known host", host_line, TRUE, FALSE, 0); } free_match_block(mb); /* done with match block */ } int host_line(record) idrix_t record; { idstr_t itmbuf; if (record && !get_record(record)) { printf("Error - could not retrieve record %d.\n", record); return 0; } if (x_record()) printf("%sX:", indent + 2); else fputs(indent, stdout); column = INDENT; if (!get_item(i_hostname, &itmbuf)) { putchar('!'); /* flag that typeout is handle */ get_item(i_handle, &itmbuf); /* this HAS to work! */ column++; } column += ID_SLN(itmbuf); id_fputs(itmbuf, stdout); space(ADDR_COLUMN - 1 - column); putz(i_netaddress); putchar('\n'); return 1; } void d_org() { standard_header(); d_contacts(coordinator, N_COORDINATOR); comments(); d_updated(); /* show record-update date */ } /* * now let's see about the members of this org... */ void d_s_org(omb) match_block *omb; { char *org_id, strbuf[MAX_LINE]; idstr_t itmbuf; match_block *mb; get_item(i_handle, &itmbuf); /* handle of this record */ org_id = id2c(strbuf, itmbuf); /* save host's ident here */ if (!(mb = get_match_block())) return; match_initialize(mb, org_id, i_name, omb->flags | LF_FIND_ASKFUL, NULL); if (!find(mb, i_groups)) /* find first AUTO_SHOWful */ printf("%sNo known members of this organization.\n", indent); else switch (mb->count) { /* some matches. */ case AUTO_SHOW: /* maybe lots more, so ask */ if (!see_more(omb, v4, "registered members of this organization")) break; /* don't want to see em */ find(mb, i_groups); /* do, so find all the rest */ default: sub_matches(mb, "known member", sum_line, TRUE, TRUE, 0); } free_match_block(mb); /* done with this */ } void d_tac() { standard_header(); putchar('\n'); d_hinfo(); d_labeled(i_tacnumber, "TAC number", 0); d_labeled(i_cputype, "Hardware", 0); d_contacts(imp_contacts, N_IMP_CONTACTS); comments(); d_updated(); /* show record-update date */ } /* * this is the special display routine used when running as the HOST * program, plus support routines. * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * this is the display routine you get always when this program is * invoked as "host". it outputs the record in a radically different * format; try it yourself to see. */ void d_athost() { idstr_t str1, str2; idrix_t record; char *cp, *addresses[MAX_ADDR], strbuf[MAX_LINE]; match_block *mb; int i, j; if (x_record()) fputs("X:", stdout); if (get_htype(NULL) == 'S') { putz(i_impname); printf(" is PSN "); putz(i_impnumber); printf(" on network "); out_netname(i_impnetnumber); } else { putz(i_hostname); get_item(i_domainame, &str1); get_item(i_nicknames, &str2); if (ID_SLN(str1) || ID_SLN(str2)) { fputs(" (nicknames ", stdout); if (ID_SLN(str1)) id_fputs(str1, stdout); if (ID_SLN(str2)) { if (ID_SLN(str1)) fputs(", ", stdout); id_siget(i_nicknames, &str2, &str1); id_fputs(str1, stdout); while (id_siget(i_nicknames, &str2, &str1)) { fputs(", ", stdout); id_fputs(str1, stdout); } } putchar(')'); } printf("\n%sA ", indent); putz(i_cputype); printf(" running "); putz(i_opsys); putchar('.'); } putchar('\n'); if (get_item(i_netaddress, &str1)) { record = id_recidx; /* save current record */ cp = strbuf; /* use this buffer */ i = 0; /* count of # of addresses */ while (id_siget(i_netaddress, &str1, &str2)) { addresses[i++] = id2c(cp, str2); /* copy it to a buffer */ cp += ID_SLN(str2) + 1; /* advance pointer */ } if (i) { if (!(mb = get_match_block())) return; for (j = 0; j < i; j++) net_display(mb, addresses[j]); free_match_block(mb); /* done with this */ get_record(record); /* re-get current record */ } } d_labeled(i_connectype, "Connect Type", 0); d_labeled(i_protocols, NULL, ','); /* comma is separator */ } /* * given an A.B.C.D type address string, output that address then * the network it's on following, plus some extra info. */ #define CLASS_A 1 /* class A network starts here */ #define CLASS_B 128 /* class B starts here; + 2nd byte */ #define CLASS_C 192 /* class C starts here; + 3rd byte */ void net_display(mb, address) match_block *mb; char *address; { int network, host, b3, imp, value; char netnum[MAX_LINE]; if (sscanf(address, "%d.%d.%d.%d", &network, &host, &b3, &imp) != 4) return; if (network < CLASS_B) sprintf(netnum, "%d.0.0.0", network); else if (network < CLASS_C) sprintf(netnum, "%d.%d.0.0", network, host); else sprintf(netnum, "%d.%d.%d.0", network, host, b3); printf("%s%s", indent, address); match_initialize(mb, netnum, i_netnumber, 0, "N"); find(mb, i_netnumber); if (mb->count == 1 && get_record(mb->recidx[0])) { printf(" ("); putz(i_netname); switch (network) { case NET_ARPANET: case NET_MILNET: printf(", host %d, IMP %d", host, imp); break; case NET_MIT: printf(", %o/%o", host, imp); break; } putchar(')'); } if (ON(F_NIC_USER)) { value = imp + (b3 << 8) + (host << 2*8) + (network << 3*8); printf("\t\t[%o,,%o]", (value >> 18), (value & 0777777)); } putchar('\n'); } /* * middle-level record output routines. this routines do oft-used * outputs from the current record. * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * output the name and handle of the current record as "name (handle)", * returning the total number of characters written. */ int name_and_handle() { idstr_t itmbuf; int n = 0; if (x_record()) { fputs("X:", stdout); n += 2; } if (get_item(i_name, &itmbuf)) { id_fputs(itmbuf, stdout); n += ID_SLN(itmbuf); } else { fputs("[No name]", stdout); n += 9; } if (get_item(i_handle, &itmbuf)) { fputs(" (", stdout); id_fputs(itmbuf, stdout); putc(')', stdout); n += ID_SLN(itmbuf) + 3; } else { fputs(" (ID?) ", stdout); n += 7; } return n; /* # of chars written */ } /* * * this is the standard header for record display. name, handle, * plus mailbox if an individual, then the address indented starting * at the next line. */ void standard_header() { idstr_t itmbuf, mbxbuf; name_and_handle(); if (get_item(i_mailbox, &itmbuf)) { id_siget(i_mailbox, &itmbuf, &mbxbuf); fputs("\t\t", stdout); id_fputs(mbxbuf, stdout); } else if (get_htype(NULL) == 'I') fputs("\t\t[No mailbox]", stdout); putchar('\n'); if (get_item(i_address, &itmbuf)) d_indented(itmbuf); if (get_item(i_phone, &itmbuf)) { if (strchr("\n\r", *ID_SCP(itmbuf))) d_indented(itmbuf); /* if phone starts with blank line, */ else { /* is multi-line phone... */ fputs(indent, stdout); id_fputs(itmbuf, stdout); putchar('\n'); } } } /* * if comments exist, output a blank line then the indented comments */ void comments() { idstr_t itmstr; if (get_item(i_comments, &itmstr)) { putchar('\n'); d_indented(itmstr); } } /* * output the db's label for the given field, or if there's * no label, which shouldn't ever happen. */ int label(field) iditm_t field; { idstr_t itmstr; itmstr = id_idfname(field); /* look up name */ if (ID_SLN(itmstr)) { /* if it has a name */ id_fputs(itmstr, stdout); /* then let's see it */ return ID_SLN(itmstr); /* return length of string */ } else { printf("%03d", field); /* else do this, something weird! */ return 3; /* wrote 3 digits */ } } /* * display the given field indented with "label: " before it. if * label is NULL, then use the db's label. if divider exists, then * the text is broken on dividers such that it doesn't wrap across * the right margin. */ void d_labeled(field, cp, divider) iditm_t field; char *cp, divider; { idstr_t itmbuf; char *word_cp; int i, len, word_len; if (!get_item(field, &itmbuf)) return; /* no contents. */ fputs(indent, stdout); /* indent */ if (cp) { fputs(cp, stdout); /* if they gave a label, use */ i = strlen(cp); } else i = label(field); /* it, else use the db's */ fputs(": ", stdout); i += INDENT + 2; /* account for extra text */ if (!divider) { /* if no divider, */ id_fputs(itmbuf, stdout); /* assume it's one line */ putchar('\n'); return; } len = ID_SLN(itmbuf); /* length of text */ cp = ID_SCP(itmbuf); /* re-use cp as ptr to field */ column = i; /* we're starting here. */ do { word_len = 0; /* length of portion */ word_cp = cp; /* cp to start of portion */ while (--len >= 0) { word_len++; if (*cp++ == divider) break; } if (column + word_len > width) { /* too far? */ putchar('\n'); /* yes, get to a new line. */ column = 0; /* reflect that */ space(i); /* indent out again */ } column += word_len; /* column after writing */ while (--word_len >= 0) putchar(*word_cp++); /* output the portion */ } while (len > 0); /* while there's source text */ putchar('\n'); /* get to a new line */ } /* * display the given text (assumed to be multiple lines) indented */ void d_indented(itmbuf) idstr_t itmbuf; { char c, *cp; int len; for (cp = ID_SCP(itmbuf), len = ID_SLN(itmbuf); len > 0 && isspace(*cp); cp++, len--) /* skip leading whitespace */ ; while (len > 0) { /* while there's more to type out */ fputs(indent, stdout); /* indent this line */ while (len-- > 0 && (c = *cp++) != '\r') putchar(c); /* print line until EOL or no more */ len--; cp++; /* skip the LF in the data */ putchar('\n'); } } /* * low-level output support routines * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * ask a yes-or-no questions, with interrupts off, so that interaction * with the user resets abortness. */ bool yesno(format, va_alist) char *format; va_dcl /* note no semi! */ { char buf[MAX_LINE]; bool result, int_state; if (int_state = ON(F_INT_ENABLED)) /* save interrupt status */ int_off(); /* interrupts of for this */ sprintf(buf, format, va_alist); /* make the prompt */ result = nx_yesno(buf); /* ask the question */ if (int_state) /* if wanted interrupts, */ int_on(); /* turn them back on now */ return result; /* return true life data */ } /* * this is the routine that was formerly bound to %z in printf() */ void putz(item) iditm_t item; { idstr_t string; if (get_item(item, &string)) id_fputs(string, stdout); else putc('?', stdout); } /* * * move over n spaces, using tabs if possible. if we've already * gone too far (and so passed a negative arg), then do a single * space, just for a break. */ #define TAB_WIDTH 8 /* width of a standard tab stop */ void space(n) int n; { int old_column, pre, body; if (n < 0) n = 1; /* too far, just one then */ old_column = column; /* save this */ column += n; /* we'll always move */ pre = TAB_WIDTH - (old_column % TAB_WIDTH); /* # of spaces to next stop */ if (pre <= n) { /* don't if that's too far */ putchar('\t'); /* get to a starting stop */ n -= pre; /* eqv to this many spaces */ body = n / TAB_WIDTH; /* need some body tabs? */ if (body > 0) { n -= (body * TAB_WIDTH); /* account for them */ while (--body >= 0) putchar('\t'); /* now splat them out */ } } while (--n >= 0) putchar(' '); /* now space over the rest */ } /* * low-level database support routines * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * load the database and define the items in the db_items[] array, * which sets up all the _xxx field locations with the internal * field numbers from the database. */ void load_database() { int i; if (!id_pkginit(NULL) || !id_dbget(ON(F_X_WHOIS) ? X_DB_FILE : DB_FILE)) { fprintf(stderr, "Failed to load database: %s", id_errcp); exit(0); } for (i = 0; i < (sizeof(db_items) / sizeof(struct item_record)); i++) { if ((*db_items[i].item = id_idfnum(id_strfs(db_items[i].name))) < 0) { fprintf(stderr, "Database doesn't know what a '%s' is!", db_items[i].name); exit(0); } } SET(F_DB_LOADED); } /* * * make the given record current. if you want this error to be * continuable, change the exit(0) to a return FALSE; */ bool get_record(record) idrix_t record; { if (record != id_recidx && !id_recget(record)) { fprintf(stderr, "Failed to make record current - %s\n", id_errcp); exit(0); } return TRUE; } /* * get the given item from the current record, returning TRUE if * it exists and is non-empty. */ bool get_item(item, itmbuf) iditm_t item; idstr_t *itmbuf; { if (!id_itmget(item, itmbuf) || ID_SLN(*itmbuf) <= 0) { *itmbuf = ID_STRLIT(""); return FALSE; /* say we didn't get it */ } return TRUE; } /* * check to see if a given record is permissable (not an X record, * or an X with X-records are allowed. returns the real htype if * so, else 0. */ int get_htype(record) idrix_t record; { idstr_t itmstr; char *cp; int c; if (record && record != id_recidx && !get_record(record)) return 0; if (!get_item(i_htype, &itmstr)) return 0; cp = ID_SCP(itmstr); /* point to htype */ c = islower(*cp) ? toupper(*cp) : *cp; /* Damn broken BSD toupper */ if (c == 'X') { /* if an X-record */ if (OFF(F_X_OK)) return 0; /* sorry, can't see it */ c = *++cp; /* get the real htype */ c = islower(c) ? toupper(c) : c; /* Damn broken BSD toupper */ } else if (strchr(INVISIBLE_HTYPES, c)) return 0; /* don't see these htypes */ return c; /* return the htype. */ } /* * if the user wants an ARPA- or MIL-only search, check the current * record to see if it's one of those. returns TRUE if not host-type * record, or host-type and on specified net(s), else FALSE. */ bool right_net(mb) match_block *mb; { idstr_t itmbuf, addrbuf; int netnum; if (!id_itmget(i_netaddress, &itmbuf) && !id_itmget(i_impnetnumber, &itmbuf)) return TRUE; /* then test doesn't apply. */ while (id_siget(i_netaddress, &itmbuf, &addrbuf)) { if (sscanf(ID_SCP(addrbuf), "%d.", &netnum) == 1) { if ((mb->flags & LF_ARPANET) && netnum == NET_ARPANET) return TRUE; if ((mb->flags & LF_MILNET) && netnum == NET_MILNET) return TRUE; } } return FALSE; } /* * return true if the current record is an X-record */ bool x_record() { idstr_t itmbuf; return get_item(i_htype, &itmbuf) && ( *ID_SCP(itmbuf) == 'X' || *ID_SCP(itmbuf) == 'x') ; } /* * given a pointer to a buffer (if NULL, use a single static buffer) * and an idstr_t, turn that idstr_t into a real C string in the buffer, * returning a char* pointer to it */ char *id2c(cp, itmbuf) char *cp; idstr_t itmbuf; { strncpy(cp, ID_SCP(itmbuf), ID_SLN(itmbuf)); *(cp + ID_SLN(itmbuf)) = '\0'; /* tie off */ return cp; /* return pointer to buf */ } /* * log file stuff, with support * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * make a log entry, showing what arg was given us */ void log_entry(s) char *s; { FILE *f; char *log_file, hostname[MAX_LINE]; static char tty[MAX_LINE], username[MAX_LINE]; static int jobnum = -1; log_file = ON(F_SERVER) ? SERVER_LOG_FILE : WHOIS_LOG_FILE; if (f = fopen(log_file, "a")) { timestamp(f); if (ON(F_SERVER)) { get_hostname(stdin, hostname); fprintf(f, " [%s](%s)\tArg: \"%s\"\n", hostname, run_mode, s); } else { if (jobnum == -1) /* if don't know it, */ get_job_info(&jobnum, tty, username); fprintf(f, " %d %s %s (%s)\tArg: \"%s\"\n", jobnum, tty, username, run_mode, s); } fclose(f); } } /* * get the hostname of whose on the other side of stream f and put * it in buf as an official hostname, or b1.b2.b3.b4 if we can't get * the name. */ void get_hostname(f, buf) FILE *f; char *buf; { int b1, b2, b3, b4; unsigned host; char *cp; struct hostent *p; #if SYS_T20 struct xstat xs; unsigned int dev; if (!xfstat(fileno(f), &xs)) { dev = ((unsigned int) xs.st.st_dev) >> 18; if (dev == (0600000 + ST_DEV_TCP)) { /* 6000000 is 20X dev code */ host = xs.xst_fhost; /* foreign host# */ cp = buf; *cp = (b1 = (host >> 3*8) & 0377); /* turn into crufty char */ *++cp = (b2 = (host >> 2*8) & 0377); /* array which is how the */ *++cp = (b3 = (host >> 8) & 0377); /* gethost stuff likes it */ *++cp = (b4 = host & 0377); *++cp = '\0'; if (p = gethostbyaddr(buf, 4, AF_INET)) /* if got an official name */ strcpy(buf, p->h_name); /* then copy it to dest */ else /* else put the numeric addr */ sprintf(buf, "%d.%d.%d.%d", b1, b2, b3, b4); } } else #endif /* SYS_T20 */ strcpy(buf, "0.0.0.0"); /* lost big, use this. */ } /* * Write a standard T20 timestamp to the given stream using ODTIM%. * Returns return value from actual jsys() call. */ void timestamp(f) FILE *f; { time_t now; struct tm *ts; now = time(0); /* current time */ ts = localtime(&now); /* get now in pieces */ fprintf(f, "%2d-%s-%02d %02d:%02d:%02d", ts->tm_mday, months[ts->tm_mon], ts->tm_year % 100, ts->tm_hour, ts->tm_min, ts->tm_sec); } /* * get your job information */ void get_job_info(jobnum, tty, username) int *jobnum; char *tty, *username; { extern char *ttyname(), *getlogin(); char *cp; *jobnum = getpid() #if SYS_T20 & 0777 #endif ; if ((cp = ttyname(0)) == NULL) cp = "tty??"; strcpy(tty, cp); #if SYS_T20 cp = tty + strlen(cp) - 1; /* Get ptr to last char of string */ if (*cp == ':') *cp = '\0'; /* Remove colon from device name, if any */ #endif if (cp = getlogin()) strcpy(username, cp); else sprintf(username, "UID-%d", getuid()); } /* * miscellaneous support routines * * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * * * given cp a pointer to a hostname string, find the host record * which matches that hostname/nickname/address. Returns TRUE if * the hostname was recognized, and canonicalized to something * other than we what we were given. */ bool canonicalize_host(cp, buf, flags) char *cp, *buf; unsigned flags; { bool canonicalized = FALSE; idstr_t itmbuf; /* desired field */ idrix_t old; /* current record */ match_block *mb; /* large */ if (mb = get_match_block()) { /* couldnt make one? */ match_initialize(mb, cp, i_handle, 0, HOST_HTYPES); mb->flags = flags; /* search flags */ do_host(mb); /* find 'em! */ if (mb->count > 1) { printf("The host \"%s\" could be any of these %d:\n\n", cp, mb->count); summary(mb, sum_line, TRUE, FALSE); /* standard stuff */ putchar('\n'); } else if (mb->count == 1) { old = id_recidx; /* current record */ if (get_record(mb->recidx[0]) && get_item(i_hostname, &itmbuf) && strnCMP(cp, ID_SCP(itmbuf), ID_SLN(itmbuf))) { id2c(buf, itmbuf); /* get & sequester */ canonicalized = TRUE; } /* re-get original */ if (old) get_record(old); /* record. */ } free_match_block(mb); /* done with this */ } return canonicalized; } /* * initialize a match block. */ void match_initialize(mb, target, key_field, flags, htypes) match_block *mb; char *target, *htypes; iditm_t key_field; unsigned flags; { mb->flags = flags; mb->htypes = htypes; mb->searches = mb->count = mb->key_count = 0; mb->key_cp = mb->key_buffer; /* point cp to start of buffer */ mb->target = target; /* set new target string */ mb->key_field = key_field; /* set which field to key on */ cmb = mb; /* globalize current match_block */ } /* * initialize a search block. */ void search_initialize(fndblk, type, field, string) idfnd_t *fndblk; iditm_t field; char *string; { fndblk->fnd_init = type; fndblk->fnd_itm = field; fndblk->fnd_str = id_strfs(string); } /* * given an idstr_t which is a netaddress, extract the portions of * it which comprise just the netnumber, and write them to the * given buf. That is, for a class A network, just write A. For * a class B, write A.B, for a class C, write A.B.C */ int net_part(buf, itmstr) char *buf; idstr_t itmstr; { int b1, b2, b3; id2c(buf, itmstr); /* copy it to a C buf. */ if (sscanf(buf, "%d.%d.%d.", &b1, &b2, &b3) != 3) return -1; if (b1 >= CLASS_C) sprintf(buf, "%d.%d.%d", b1, b2, b3); else if (b1 >= CLASS_B) sprintf(buf, "%d.%d", b1, b2); else sprintf(buf, "%d", b1); return b1; } /* * ask a "want to see more?" type question. this constucts a prompt, * and asks the user a yes/no question, returning the truth of their * answer. */ bool see_more(mb, server_msg, user_msg) match_block *mb; char *server_msg, *user_msg; { if (!(mb->flags & (LF_SUBDISPLAY | LF_EXPAND))) { if (OFF(F_INTERACTIVE)) { puts(server_msg); /* how to use '*' */ return FALSE; } else { if (!yesno("Would you like to see the %s? ", (int) user_msg)) return FALSE; /* no! */ putchar('\n'); } } return TRUE; /* yes! */ } /* * sort the given match_block by the strings pointed to by keys[] */ void sort_matches(mb) match_block *mb; { sorting_mb = mb; /* make sure this is globalized */ qsort((char *) mb->records, mb->count, sizeof(int), qcmp); } /* * * routine called by qsort to compare two entries. s1 and s2 are * pointers into the records[] array, which contains indicies into * recidx[] and keys[]. */ int qcmp(s1, s2) char *s1, *s2; { return strCMP(sorting_mb->keys[*((int *) s1)], sorting_mb->keys[*((int *) s2)]); } /* * allocate a match block */ match_block *get_match_block() { match_block *mb; if (!(mb = (match_block *) malloc(sizeof(match_block)))) { fputs("Couldn't allocate enough memory for match block!\n", stderr); return NULL; } return mb; } /* * de-allocate a match-block */ void free_match_block(mb) match_block *mb; { if (cmb == mb) /* if this used to be the current match */ cmb = NULL; /* block, then it's going away now.... */ free((char *) mb); } /* * see if there's more JCL. if there is, read it into the given * buffer. */ char *get_jcl(buf) char *buf; { #if SYS_T20 int n, ablock[5]; char *p; ablock[1] = _RSINI; /* make data available as priin */ if (!jsys(RSCAN, ablock)) return NULL; ablock[1] = _RSCNT; /* return # of chars in RSCAN buf */ if (!jsys(RSCAN, ablock) | ((n = ablock[1]) < 1)) return NULL; /* failed or no chars */ for (p = buf; --n >= 0; p++) *p = getchar(); /* read from priin and stuff in buf */ *--p = '\0'; /* remove trailing LF & tie off buf. */ for (p = buf; *p; p++) if (*p == ' ') /* if has a space, */ return ++p; /* arg comes next. */ #endif return NULL; } /* * Interrupt stuff */ void int_on() { int_clear(); SET(F_INT_ENABLED); /* interrupts in actions now */ } void int_off() { CLEAR(F_INT_ENABLED); int_clear(); } void int_clear() { id_abortf = FALSE; /* reset abortness flags */ CLEAR(F_ABORT); } #define ABORT_MESSAGE " *Aborting*\n" void int_abort() { if (ON(F_INT_ENABLED) && OFF(F_ABORT)) { id_abortf = TRUE; /* set that user wants to abort */ SET(F_ABORT); write(fileno(stderr), ABORT_MESSAGE, sizeof(ABORT_MESSAGE) - 1); } } #define FORMAT_PREFIX #define plural(n) n, (n == 1) ? "" : "es" void int_status() { if (ON(F_INT_ENABLED) && cmb) { char *p, buf[MAX_LINE]; int length1, length2 = 0; time_t now; now = time(NULL); /* get current time, turn into str */ sprintf(buf, " %.8s -- ", ctime(&now) + 11); /* HH:MM:SS */ p = buf + (length1 = strlen(buf)); /* point to tail to string */ if (!cmb->count) strcpy(p, "No matches yet"); else sprintf(p, "%d match%s so far", plural(cmb->count)); sprintf(p += (length2 = strlen(p)), " in %d search%s.\n", plural(cmb->searches)); write(fileno(stderr), buf, length1 + length2 + strlen(p)); } }