/* * * PROGRAM: InterBase server manager * MODULE: ibmgr.c * DESCRIPTION: Main routine and parser * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. * * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port * * $Id: ibmgr.cpp,v 1.8.2.1 2003/10/09 13:10:26 alexpeshkoff Exp $ */ #include "firebird.h" #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #include "../jrd/ib_stdio.h" #include #include #include #ifdef HAVE_PWD_H #include #endif #include "../jrd/common.h" #include "../jrd/gds.h" #include "../utilities/ibmgr.h" #include "../utilities/ibmgrswi.h" #include "../jrd/license.h" #include "../utilities/srvrmgr_proto.h" #define MAXARGS 20 /* max number of args allowed on command line */ #define MAXSTUFF 1000 /* longest interactive command line */ /* Codes returned by get_switchesi() FB_SUCCESS is defined in common.h */ #define ERR_SYNTAX (-1) #define ERR_OTHER (-2) /* Codes returned by parse_cmd_line() FB_SUCCESS is defined in common.h */ #define ACT_NONE (-1) #define ACT_QUIT 1 #define ACT_PROMPT 2 static void copy_str_upper(TEXT *, TEXT *); static int get_line(int *, SCHAR **, TEXT *); static SSHORT get_switches(int, TEXT **, IN_SW_TAB, IBMGR_DATA *, BOOLEAN *); static SSHORT parse_cmd_line(int, TEXT **); static void print_config(void); static void print_help(void); static IBMGR_DATA ibmgr_data; int CLIB_ROUTINE main( int argc, char **argv) { /************************************** * * m a i n * ************************************** * * Functional description * If there is no command line, prompt for one, read it * and make an artificial argc/argv. Otherwise, pass * the specified argc/argv to IBMGR_exec_line (see below). * **************************************/ int local_argc; SCHAR *local_argv[MAXARGS]; TEXT stuff[MAXSTUFF]; TEXT msg[MSG_LEN]; TEXT *user = NULL, *password = NULL, *host = NULL; /* pointers for getenv */ SSHORT ret; struct passwd *pw; #ifdef VMS argc = VMS_parse(&argv, argc); #endif /* Let's see if we have something in environment variables */ user = getenv("ISC_USER"); password = getenv("ISC_PASSWORD"); /* MMM - do not allow to change host now host = getenv ("ISC_HOST"); */ /* Let's get a real user name. This info is used by START server command. Because server is not running we can not check the password, thus we require a real user to be root or FIREBIRD_USER_NAME or INTERBASE_USER_NAME or INTERBASE_USER_SHORT */ if ((pw = getpwuid(getuid())) == NULL) { ib_perror("getpwuid"); SRVRMGR_msg_get(MSG_GETPWFAIL, msg); ib_fprintf(OUTFILE, "%s\n", msg); exit(FINI_ERROR); } strcpy(ibmgr_data.real_user, pw->pw_name); if (!strcmp(pw->pw_name, "root") || !strcmp(pw->pw_name, FIREBIRD_USER_NAME) || !strcmp(pw->pw_name, INTERBASE_USER_NAME) || !strcmp(pw->pw_name, INTERBASE_USER_SHORT)) strcpy(ibmgr_data.user, SYSDBA_USER_NAME); else copy_str_upper(ibmgr_data.user, pw->pw_name); if (user) copy_str_upper(ibmgr_data.user, user); if (password) strcpy(ibmgr_data.password, password); else ibmgr_data.password[0] = '\0'; if (host) strcpy(ibmgr_data.host, host); else strcpy(ibmgr_data.host, "localhost"); /* Shutdown is not in progress and we are not attached to service yet. But obviously we will need attachment. */ ibmgr_data.shutdown = FALSE; ibmgr_data.attached = NULL; ibmgr_data.reattach |= (REA_HOST | REA_USER | REA_PASSWORD); /* Special case a solitary -z switch. Print the version and then drop into prompt mode. */ if (argc == 2 && *argv[1] == '-' && (argv[1][1] == 'Z' || argv[1][1] == 'z')) { parse_cmd_line(argc, argv); argc--; } if (argc > 1) { ret = parse_cmd_line(argc, argv); if (ret == FB_SUCCESS) { ret = SRVRMGR_exec_line(&ibmgr_data); if (ret) { SRVRMGR_msg_get(ret, msg); ib_fprintf(OUTFILE, "%s\n", msg); } /* We also need to check the shutdown flag here (if operation was -shut -[noat|notr]) and, depending on what we want to do, either wait here on some sort of a shutdown event, or go to the prompt mode */ SRVRMGR_cleanup(&ibmgr_data); exit(FINI_OK); } else if (ret != ACT_PROMPT) exit(FINI_OK); } for (;;) { if (get_line(&local_argc, local_argv, stuff)) break; if (local_argc > 1) { ret = parse_cmd_line(local_argc, local_argv); if (ret == ACT_QUIT) break; if (ret == FB_SUCCESS) { ret = SRVRMGR_exec_line(&ibmgr_data); if (ret) { SRVRMGR_msg_get(ret, msg); ib_fprintf(OUTFILE, "%s\n", msg); } } } } SRVRMGR_cleanup(&ibmgr_data); exit(FINI_OK); } static int get_line( int *argc, SCHAR ** argv, TEXT * stuff) { /************************************** * * g e t _ l i n e * ************************************** * * Functional description * Read the current line and put its pieces into an argc/argv * structure. Reads a max of MAXARGS - 1 pieces (argv [0] is * unused), and a max of MAXSTUFF characters, at which point * **************************************/ USHORT count; TEXT *cursor, c; BOOLEAN first; TEXT msg[MSG_LEN]; SRVRMGR_msg_get(MSG_PROMPT, msg); ib_printf("%s ", msg); /* "FBMGR> " */ /* if (sw_service_gsec) ib_putc ('\001', ib_stdout); */ ib_fflush(ib_stdout); *argc = 1; cursor = stuff; count = MAXSTUFF - 1; first = TRUE; /* for each input character, if it's white space (or any non-printable, non-newline for that matter), ignore it; if it's a newline, we're done; otherwise, put it in the current argument */ while (*argc < MAXARGS && count > 0) { c = ib_getc(ib_stdin); if (c > ' ' && c <= '~') { /* note that the first argument gets a '-' appended to the front to fool the switch checker into thinking it came from the command line */ for (argv[(*argc)++] = cursor; count > 0; count--) { if (first) { first = FALSE; if (c != '?') { *cursor++ = '-'; count--; } } *cursor++ = c; c = ib_getc(ib_stdin); if (c <= ' ' || c > '~') break; } *cursor++ = '\0'; count--; } if (c == '\n') break; if (c == EOF) { if (SYSCALL_INTERRUPTED(errno)) { errno = 0; continue; } else return TRUE; } } *cursor = '\0'; return FALSE; } static SSHORT get_switches( int argc, TEXT ** argv, IN_SW_TAB in_sw_table, IBMGR_DATA * ibmgr_data, BOOLEAN * quitflag) { /************************************** * * g e t _ s w i t c h e s * ************************************** * * Functional description * Parse the input line arguments. * Returns: FB_SUCCESS if success * ERR_SYNTAX in case of syntax error. * ERR_OTHER non-syntax error. * **************************************/ TEXT *p, *q, *string, msg[MSG_LEN]; IN_SW_TAB in_sw_tab; USHORT in_sw; USHORT last_sw, sw_version; int l; SSHORT err_msg_no; BOOLEAN any_switches; /* Look at each argument. It's either a switch or a parameter. parameters must always follow a switch, but not all switches need parameters. */ *quitflag = FALSE; last_sw = IN_SW_IBMGR_0; sw_version = FALSE; for (--argc; argc > 0; argc--) { string = *++argv; if (*string == '?') ibmgr_data->operation = OP_HELP; else if (*string != '-') { /* this is not a switch, so it must be a parameter for the previous switch, if any */ switch (last_sw) { case IN_SW_IBMGR_POOL: if (strlen(string) >= MAXPATHLEN) { SRVRMGR_msg_get(MSG_FLNMTOOLONG, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; } strcpy(ibmgr_data->print_file, string); break; case IN_SW_IBMGR_HOST: /* If the name of the host is the same we are currently attached to, do nothing MMM - it has to be more sophisticated check against compete host name including domain and/or network addreass, but... May be later */ if (strcmp(ibmgr_data->host, string)) { strcpy(ibmgr_data->host, string); ibmgr_data->reattach |= REA_HOST; } break; case IN_SW_IBMGR_PASSWORD: /* If the password is the same, do nothing */ if (strcmp(ibmgr_data->password, string)) { strcpy(ibmgr_data->password, string); ibmgr_data->reattach |= REA_PASSWORD; } break; case IN_SW_IBMGR_USER: /* MMM - we need to compare new entered user with one we already have and do not set the reattach flag if they are the same */ copy_str_upper(ibmgr_data->user, string); if (strlen(ibmgr_data->user) > 127) { SRVRMGR_msg_get(MSG_INVUSER, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; } ibmgr_data->reattach |= REA_USER; break; case IN_SW_IBMGR_0: SRVRMGR_msg_get(MSG_INVPAR, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; /* The following switches do not take any parameters */ case IN_SW_IBMGR_START: case IN_SW_IBMGR_ONCE: case IN_SW_IBMGR_FOREVER: case IN_SW_IBMGR_SIGNORE: case IN_SW_IBMGR_SHUT: case IN_SW_IBMGR_NOW: case IN_SW_IBMGR_NOAT: case IN_SW_IBMGR_NOTR: case IN_SW_IBMGR_IGNORE: case IN_SW_IBMGR_SET: case IN_SW_IBMGR_SHOW: case IN_SW_IBMGR_QUIT: case IN_SW_IBMGR_HELP: case IN_SW_IBMGR_Z: SRVRMGR_msg_get(MSG_SWNOPAR, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; default: #ifdef DEV_BUILD ib_fprintf(OUTFILE, "ASSERT: file %s line %ld: last_sw = %d\n", __FILE__, __LINE__, last_sw); #endif ; } last_sw = IN_SW_IBMGR_0; } else { /* iterate through the switch table, looking for matches */ in_sw = IN_SW_IBMGR_0; for (in_sw_tab = in_sw_table; q = in_sw_tab->in_sw_name; in_sw_tab++) { p = string + 1; /* handle orphaned hyphen case */ if (!*p--) break; /* compare switch to switch name in table */ l = 0; while (*p) { if (!*++p) { if (l >= in_sw_tab->in_sw_min_length) in_sw = in_sw_tab->in_sw; else in_sw = IN_SW_IBMGR_AMBIG; } if (UPPER(*p) != *q++) break; l++; } /* end of input means we got a match. stop looking */ if (!*p) break; } /* If the previous switch requires parameters and we are here, this is an error */ switch (last_sw) { case IN_SW_IBMGR_PASSWORD: case IN_SW_IBMGR_USER: case IN_SW_IBMGR_HOST: case IN_SW_IBMGR_POOL: SRVRMGR_msg_get(MSG_REQPAR, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; } switch (in_sw) { case IN_SW_IBMGR_START: case IN_SW_IBMGR_SHUT: case IN_SW_IBMGR_SET: case IN_SW_IBMGR_SHOW: case IN_SW_IBMGR_QUIT: case IN_SW_IBMGR_HELP: case IN_SW_IBMGR_PRINT: /* Only one operation can be specified on command line */ if (ibmgr_data->operation) { SRVRMGR_msg_get(MSG_OPSPEC, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; } switch (in_sw) { case IN_SW_IBMGR_START: ibmgr_data->operation = OP_START; break; case IN_SW_IBMGR_SHUT: ibmgr_data->operation = OP_SHUT; break; case IN_SW_IBMGR_SET: ibmgr_data->operation = OP_SET; break; case IN_SW_IBMGR_SHOW: ibmgr_data->operation = OP_SHOW; break; case IN_SW_IBMGR_QUIT: ibmgr_data->operation = OP_QUIT; *quitflag = TRUE; break; case IN_SW_IBMGR_HELP: ibmgr_data->operation = OP_HELP; break; case IN_SW_IBMGR_PRINT: ibmgr_data->operation = OP_PRINT; break; } break; case IN_SW_IBMGR_PASSWORD: case IN_SW_IBMGR_USER: case IN_SW_IBMGR_HOST: /* The above switches are separate case: they can be used as operation or parameter switches. If an operation has already been specified, we assume it is a switch else we set an implied operation type OP_SET. We also can not change any the above parameters if a server shutdown is in progress. */ if (ibmgr_data->shutdown) { SRVRMGR_msg_get(MSG_SHUTDOWN, msg); ib_fprintf(OUTFILE, "%s\n", msg); SRVRMGR_msg_get(MSG_CANTCHANGE, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_OTHER; } if (!ibmgr_data->operation) ibmgr_data->operation = OP_SET; /* Check if any of these switches has been already specified. Note that change in any of these psrameters will require new attachment to service */ err_msg_no = 0; switch (in_sw) { case IN_SW_IBMGR_HOST: if (ibmgr_data->par_entered & ENT_HOST) { err_msg_no = MSG_INVSWSW; break; } ibmgr_data->par_entered |= ENT_HOST; break; case IN_SW_IBMGR_PASSWORD: if (ibmgr_data->par_entered & ENT_PASSWORD) { err_msg_no = MSG_INVSWSW; break; } ibmgr_data->par_entered |= ENT_PASSWORD; break; case IN_SW_IBMGR_USER: if (ibmgr_data->par_entered & ENT_USER) { err_msg_no = MSG_INVSWSW; break; } ibmgr_data->par_entered |= ENT_USER; break; } if (err_msg_no) { SRVRMGR_msg_get(err_msg_no, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; } break; case IN_SW_IBMGR_ONCE: case IN_SW_IBMGR_FOREVER: case IN_SW_IBMGR_SIGNORE: case IN_SW_IBMGR_NOW: case IN_SW_IBMGR_NOAT: case IN_SW_IBMGR_NOTR: case IN_SW_IBMGR_IGNORE: case IN_SW_IBMGR_POOL: /* These switches are operation modifiers or suboperations. Each of them makes sense only in a contex of certain operation. So, let's make sure it is a right contex. But first let's see if any operation has been specified */ if (!ibmgr_data->operation) { SRVRMGR_msg_get(MSG_NOOPSPEC, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; } /* if a suboperation has already been specified get the hell out of here. */ if (ibmgr_data->suboperation) { SRVRMGR_msg_get(MSG_INVSWSW, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; } err_msg_no = 0; switch (in_sw) { case IN_SW_IBMGR_ONCE: case IN_SW_IBMGR_FOREVER: case IN_SW_IBMGR_SIGNORE: if (ibmgr_data->operation != OP_START) { err_msg_no = MSG_INVSWOP; break; } if (in_sw == IN_SW_IBMGR_ONCE) ibmgr_data->suboperation = SOP_START_ONCE; else if (in_sw == IN_SW_IBMGR_SIGNORE) ibmgr_data->suboperation = SOP_START_SIGNORE; else ibmgr_data->suboperation = SOP_START_FOREVER; break; case IN_SW_IBMGR_NOW: case IN_SW_IBMGR_NOAT: case IN_SW_IBMGR_NOTR: case IN_SW_IBMGR_IGNORE: if (ibmgr_data->operation != OP_SHUT) { err_msg_no = MSG_INVSWOP; break; } if (in_sw == IN_SW_IBMGR_NOW) ibmgr_data->suboperation = SOP_SHUT_NOW; else if (in_sw == IN_SW_IBMGR_NOAT) ibmgr_data->suboperation = SOP_SHUT_NOAT; else if (in_sw == IN_SW_IBMGR_NOTR) ibmgr_data->suboperation = SOP_SHUT_NOTR; else ibmgr_data->suboperation = SOP_SHUT_IGN; break; case IN_SW_IBMGR_POOL: if (ibmgr_data->operation != OP_PRINT) { err_msg_no = MSG_INVSWOP; break; } ibmgr_data->suboperation = SOP_PRINT_POOL; break; } if (err_msg_no) { SRVRMGR_msg_get(err_msg_no, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; } break; case IN_SW_IBMGR_Z: /* This is also a separate case - it could be operation switch or just switch. Also, does not matter how many times version switch was specified, we output version only once */ if (!ibmgr_data->operation) ibmgr_data->operation = OP_VERSION; if (!sw_version) { SRVRMGR_msg_get(MSG_VERSION, msg); ib_fprintf(OUTFILE, "%s %s\n", msg, GDS_VERSION); } break; case IN_SW_IBMGR_0: SRVRMGR_msg_get(MSG_INVSW, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; case IN_SW_IBMGR_AMBIG: SRVRMGR_msg_get(MSG_AMBSW, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; default: #ifdef DEV_BUILD ib_fprintf(OUTFILE, "ASSERT: file %s line %ld: in_sw = %d\n", __FILE__, __LINE__, in_sw); #endif ; } last_sw = in_sw; } if (*quitflag) break; } /* If the previous switch requires parameters and we are here, this is an error */ switch (last_sw) { case IN_SW_IBMGR_PASSWORD: case IN_SW_IBMGR_USER: case IN_SW_IBMGR_HOST: case IN_SW_IBMGR_PRINT: case IN_SW_IBMGR_POOL: SRVRMGR_msg_get(MSG_REQPAR, msg); ib_fprintf(OUTFILE, "%s\n", msg); return ERR_SYNTAX; } #ifdef DEV_BUILD if (!ibmgr_data->operation) ib_fprintf(OUTFILE, "ASSERT: file %s line %ld: no operation has been specified\n", __FILE__, __LINE__); #endif return FB_SUCCESS; } static void print_config(void) { /************************************** * * p r i n t _ c o n f i g * ************************************** * * Functional description * **************************************/ ib_fprintf(OUTFILE, "\nHost:\t%s\nUser:\t%s\n\n", ibmgr_data.host, ibmgr_data.user); #ifdef DEBUG ib_fprintf(OUTFILE, "\nReal user:\t%s\nPassword:\t%s\n\n", ibmgr_data.real_user, ibmgr_data.password); ib_fprintf(OUTFILE, "Attached:\t%s\nNew attach:\t%s %s %s\nShutdown:\t%s\n\n", ibmgr_data.attached ? "yes" : "no", ibmgr_data.reattach & REA_HOST ? "HOST" : "no", ibmgr_data.reattach & REA_PASSWORD ? "PASSWORD" : "no", ibmgr_data.reattach & REA_USER ? "USER" : "no", ibmgr_data.shutdown ? "yes" : "no"); #endif } static void print_help(void) { /************************************** * * p r i n t _ h e l p * ************************************** * * Functional description * **************************************/ TEXT msg[MSG_LEN]; TEXT msg2[MSG_LEN]; ib_fprintf(OUTFILE, "\n\n"); ib_fprintf(OUTFILE, "Usage: fbmgr -command [-option [parameter]]\n\n"); ib_fprintf(OUTFILE, "or fbmgr\n"); ib_fprintf(OUTFILE, " FBMGR> command [-option [parameter]]\n\n"); ib_fprintf(OUTFILE, " shut [-now] shutdown server\n"); ib_fprintf(OUTFILE, " show show host and user\n"); ib_fprintf(OUTFILE, " user set user name\n"); ib_fprintf(OUTFILE, " password set DBA password\n"); ib_fprintf(OUTFILE, " help prints help text\n"); ib_fprintf(OUTFILE, " quit quit prompt mode\n\n"); ib_fprintf(OUTFILE, "Command switches 'user' and 'password' can also be used\n"); ib_fprintf(OUTFILE, "as an option switches for commands like start or shut.\n"); ib_fprintf(OUTFILE, "For example, to shutdown server you can: \n\n"); ib_fprintf(OUTFILE, "fbmgr -shut -password \n\n"); ib_fprintf(OUTFILE, "or\n\n"); ib_fprintf(OUTFILE, "fbmgr\n"); ib_fprintf(OUTFILE, "FBMGR> shut -password \n\n"); ib_fprintf(OUTFILE, "or\n\n"); ib_fprintf(OUTFILE, "fbmgr\n"); ib_fprintf(OUTFILE, "FBMGR> password \n"); ib_fprintf(OUTFILE, "FBMGR> shut\n\n\n"); } static SSHORT parse_cmd_line( int argc, TEXT ** argv) { /************************************** * * p a r s e _ c m d _ l i n e * ************************************** * * Functional description * Parses the command line. After it was parsed * executes commands that do require server access * (help, show, etc.) and checks some conditions * for other command. * * returns: * * FB_SUCCESS on normal completion, * ACT_QUIT if user chooses to quit * ACT_PROMT used by main() to switch into * the prompt mode * ACT_NONE on error or * if no further actions necessary * (like help, version, etc.) * **************************************/ TEXT msg[MSG_LEN]; BOOLEAN quitflag = FALSE; SSHORT ret; ibmgr_data.operation = OP_NONE; ibmgr_data.suboperation = SOP_NONE; ibmgr_data.par_entered = 0; ret = get_switches(argc, argv, (IN_SW_TAB ) &ibmgr_in_sw_table, &ibmgr_data, &quitflag); if (ret != FB_SUCCESS) { if (ret == ERR_SYNTAX) { SRVRMGR_msg_get(MSG_SYNTAX, msg); ib_fprintf(OUTFILE, "%s\n", msg); } return ACT_NONE; } switch (ibmgr_data.operation) { case OP_SHUT: if (strcmp(ibmgr_data.user, SYSDBA_USER_NAME)) { SRVRMGR_msg_get(MSG_NOPERM, msg); ib_fprintf(OUTFILE, "%s\n", msg); ret = ACT_NONE; break; } /* The only allowed suboperation when shutdown has been started is to IGNORE it. */ if (ibmgr_data.shutdown && ibmgr_data.suboperation != SOP_SHUT_IGN) { SRVRMGR_msg_get(MSG_SHUTDOWN, msg); ib_fprintf(OUTFILE, "%s\n", msg); SRVRMGR_msg_get(MSG_CANTSHUT, msg); ib_fprintf(OUTFILE, "%s\n", msg); ret = ACT_NONE; } break; case OP_START: if ((strcmp(ibmgr_data.real_user, "root") && strcmp(ibmgr_data.real_user, FIREBIRD_USER_NAME) && strcmp(ibmgr_data.real_user, INTERBASE_USER_NAME) && strcmp(ibmgr_data.real_user, INTERBASE_USER_SHORT)) || strcmp(ibmgr_data.user, SYSDBA_USER_NAME)) { SRVRMGR_msg_get(MSG_NOPERM, msg); ib_fprintf(OUTFILE, "%s\n", msg); ret = ACT_NONE; break; } break; case OP_HELP: print_help(); ret = ACT_NONE; break; case OP_SHOW: print_config(); ret = ACT_NONE; break; case OP_VERSION: ret = ACT_NONE; break; case OP_SET: ret = ACT_PROMPT; break; } if (quitflag) if (ibmgr_data.shutdown) { SRVRMGR_msg_get(MSG_SHUTDOWN, msg); ib_fprintf(OUTFILE, "%s\n", msg); SRVRMGR_msg_get(MSG_CANTQUIT, msg); ib_fprintf(OUTFILE, "%s\n", msg); ret = ACT_NONE; } else ret = ACT_QUIT; return ret; } static void copy_str_upper( TEXT * str1, TEXT * str2) { /************************************** * * c o p y _ s t r _ u p p e r * ************************************** * * Functional description * Copies string str2 to address str1 * converting str2 to upper case * **************************************/ for (; *str2; str1++, str2++) *str1 = UPPER(*str2); *str1 = '\0'; }