1 #if (!defined(lint) && !defined(SABER))
2 static char rcsid_module_c[] = "$Header$";
5 /* This is the file utils.c for the SMS Client, which allows a nieve
6 * user to quickly and easily maintain most parts of the SMS database.
7 * It Contains: Many useful utility functions.
10 * By: Chris D. Peterson
16 * Copyright 1988 by the Massachusetts Institute of Technology.
18 * For further information on copyright and distribution
19 * see the file mit-copyright.h
28 #include "mit-copyright.h"
34 #include <netdb.h> /* for gethostbyname. */
36 /* Function Name: AddQueue
37 * Description: Adds an element to a queue
38 * Arguments: elem, pred - element and its predecessor.
44 struct qelem * elem, *pred;
52 elem->q_forw = pred->q_forw;
56 /* Function Name: RemoveQueue
57 * Description: removes an element from a queue.
66 if (elem->q_forw != NULL)
67 (elem->q_forw)->q_back = elem->q_back;
68 if (elem->q_back != NULL)
69 (elem->q_back)->q_forw = elem->q_forw;
72 /* Function Name: FreeInfo
73 * Description: Frees all elements of a NULL terminated arrary of char*'s
74 * Arguments: info - array who's elements we are to free.
83 FreeAndClear(info++, TRUE);
86 /* Function Name: FreeAndClear - I couldn't resist the name.
87 * Description: Clears pointer and optionially frees it.
88 * Arguments: pointer - pointer to work with.
89 * free_it - if TRUE then free pointer.
94 FreeAndClear(pointer, free_it)
105 /* Function Name: QueueTop
106 * Description: returns a qelem pointer that points to the top of
108 * Arguments: elem - any element of a queue.
109 * Returns: top element of a queue.
116 if (elem == NULL) /* NULL returns NULL. */
118 while (elem->q_back != NULL)
123 /* Function Name: FreeQueueElem
124 * Description: Frees one element of the queue.
125 * Arguments: elem - the elem to free.
133 char ** info = (char **) elem->q_data;
135 if (info != (char **) NULL) {
136 FreeInfo( info ); /* free info fields */
137 free(elem->q_data); /* free info array itself. */
139 RemoveQueue(elem); /* remove this element from the queue */
140 free(elem); /* free its space. */
143 /* Function Name: FreeQueue
144 * Description: Cleans up the queue
145 * Arguments: elem - any element of the queue.
153 struct qelem *temp, *local = QueueTop(elem);
155 while(local != NULL) {
156 temp = local->q_forw;
157 FreeQueueElem(local);
162 /* Function Name: QueueCount
163 * Description: Counts the number of elements in a queue
164 * Arguments: elem - any element in the queue.
173 elem = QueueTop(elem);
174 while (elem != NULL) {
181 /* Function Name: StoreInfo
182 * Description: Stores information from an sms query into a queue.
183 * Arguments: argc, argv, - information returned from the query returned
185 * data - the previous element on the queue, this data will be
186 * stored in a qelem struct immediatly after this elem.
187 * If NULL then a new queue will be created. This value
188 * is updated to the current element at the end off the
190 * Returns: SMS_CONT, or SMS_ABORT if it has problems.
194 StoreInfo(argc, argv, data)
199 char ** info = (char **) malloc( MAX_ARGS_SIZE * sizeof(char *));
200 struct qelem ** old_elem = (struct qelem **) data;
201 struct qelem * new_elem = (struct qelem *) malloc (sizeof (struct qelem));
204 if ( (new_elem == (struct qelem *) NULL) || (info == (char **) NULL) ) {
205 Put_message("Could Not allocate more memory.");
206 FreeQueue(*old_elem);
207 *old_elem = (struct qelem *) NULL;
211 for (count = 0; count < argc; count++)
212 info[count] = Strsave(argv[count]);
213 info[count] = NULL; /* NULL terminate this sucker. */
215 new_elem->q_data = (char *) info;
216 AddQueue(new_elem, *old_elem);
218 *old_elem = new_elem;
222 /* Function Name: CountArgs
223 * Description: Retrieve the number of args in a null terminated
225 * Arguments: info - the argument list.
226 * Returns: number if args in the list.
235 while (*info != NULL) {
243 /* Function Name: Scream
244 * Description: Bitch Loudly and exit, it is intended as a callback
245 * function for queries that should never return a value.
247 * Returns: doesn't exit.
253 com_err(program_name, 0,
254 "\nAn SMS update returned a value -- programmer botch\n");
259 /* Function Name: PromptWithDefault
260 * Description: allows a user to be prompted for input, and given a
262 * Arguments: prompt - the prompt string.
263 * buf, buflen - buffer to be returned and its MAX size?
264 * default value for the answer.
265 * Returns: the value returned by prompt_input.
269 PromptWithDefault(prompt, buf, buflen, def)
277 (void) sprintf(tmp, "%s [%s]: ", prompt, def ? def : "");
278 ans = Prompt_input(tmp, buf, buflen);
280 if (YesNoQuestion("Are you sure you want to exit", 1)) {
284 Put_message("Continuing input...");
285 return(PromptWithDefault(prompt, buf, buflen, def));
288 (void) strcpy(buf, def);
289 else if (!strcmp(buf, "\"\""))
294 /* Function Name: YesNoQuestion
295 * Description: This prompts the user for the answer to a yes-no or
296 * true-false question.
297 * Arguments: prompt - the prompt for the user.
298 * bool_def - the default value either TRUE or FALSE.
299 * Returns: TRUE or FALSE or -1 on error
303 YesNoQuestion(prompt, bool_def)
310 if (!PromptWithDefault(prompt, ans, 2, bool_def ? "y" : "n"))
320 Put_message("Please answer 'y' or 'n'.");
325 /* Function Name: YesNoQuitQuestion
326 * Description: This prompts the user for the answer to a yes-no or
327 * true-false question, with a quit option.
328 * Arguments: prompt - the prompt for the user.
329 * bool_def - the default value either TRUE or FALSE.
330 * Returns: TRUE or FALSE or -1 on error or QUIT
331 * NOTE: It is not possible to have quit the default, but then I don't
332 * seem to need this functionality.
336 YesNoQuitQuestion(prompt, bool_def)
343 if (!PromptWithDefault(prompt, ans, 2, bool_def ? "y" : "n"))
356 Put_message("Please answer 'y', 'n' or 'q'.");
362 /* Function Name: Confirm
363 * Description: This function asks the user to confirm the action
364 * he is about to take, used for things like deleting.
365 * Arguments: prompt - the prompt string.
366 * Returns: TRUE/FALSE - wether or not the confirmation occured.
373 return( !verbose || (YesNoQuestion(prompt,FALSE) == TRUE) );
376 /* Function Name: ValidName
377 * Description: This function checks to see if we have a valid list name.
378 * Arguments: s - the list name.
379 * Returns: TRUE if Valid.
387 Put_message("Please use a non-empty name.");
388 else if (index(s, ' '))
389 Put_message("You cannot use space (' ') in this name.");
390 else if (index(s, WILDCARD))
391 Put_message("Wildcards not accepted here.");
397 /* Function Name: ToggleVerboseMode
398 * Description: This function toggles the verbose mode.
400 * Returns: DM_NORMAL.
410 Put_message("Delete functions will first confirm\n");
412 Put_message("Delete functions will be silent\n");
417 /* Function Name: NullFunc
418 * Description: dummy callback routine
429 /* Function Name: SlipInNewName
430 * Description: Slips the new name into the number 2 slot of a list, and
431 * returns a pointer to the new list.
432 * Arguments: info - list that needs this name slipped into it.
433 * name - the name to slip into the list.
434 * Returns: a pointer to the new list.
435 * NOTE: This screws up the numbers of the elements of the array in a
440 SlipInNewName(info, name)
446 /* This also pushes the NULL down. */
447 for (i = CountArgs(info); i > 1; i--) {
450 info[1] = name; /* now slip in the name. */
453 /* Function Name: GetValueFromUser
454 * Description: This function gets a value from a user for the field
456 * Arguments: prompt - prompt for user.
457 * pointer - pointer to default value, will be returned
459 * Returns: SUB_ERROR if break hit (^C).
463 GetValueFromUser(prompt, pointer)
464 char * prompt, ** pointer;
468 if (PromptWithDefault(prompt, buf, BUFSIZ, *pointer) == -1)
472 * If these are the same then there is no need to allocate a new string.
474 * a difference that makes no difference, IS no difference.
477 if (strcmp(buf, *pointer) != 0) {
478 if (*pointer != NULL)
480 *pointer = Strsave(buf);
485 /* Function Name: GetYesNoValueFromUser
486 * Description: This function gets a value from a user for the field
488 * Arguments: prompt - prompt for user.
489 * pointer - pointer to default value, will be returned
491 * Returns: SUB_ERROR if break hit (^C).
495 GetYesNoValueFromUser(prompt, pointer)
496 char * prompt, ** pointer;
498 char user_prompt[BUFSIZ];
501 if ( strcmp (*pointer, DEFAULT_YES) == 0 )
506 sprintf(user_prompt, "%s (y/n)", prompt);
508 switch (YesNoQuestion(user_prompt, default_val)) {
510 if (*pointer != NULL)
512 *pointer = Strsave(DEFAULT_YES);
515 if (*pointer != NULL)
517 *pointer = Strsave(DEFAULT_NO);
526 /* Function Name: GetFSVal
527 * Description: asks about a specific filesystem value.
528 * Arguments: name - string for this type of filesystem.
529 * mask - mask for this type of filesystem.
530 * current - current filesystem state. (for defaults).
531 * new - new filesystem state.
532 * Returns: TRUE if successful.
536 GetFSVal(name, mask, current, new)
538 int mask, current, *new;
540 char temp_buf[BUFSIZ];
541 sprintf(temp_buf, "Is this a %s filsystem", name);
542 switch (YesNoQuestion(temp_buf, ( (mask & current) == mask) )) {
547 break; /* zero by default. */
554 /* Function Name: GetFSTypes
555 * Description: Allows user to specify filsystem types.
556 * Arguments: current - current value of filsystem, freed here.
557 * Returns: SUB_ERROR on ^C.
564 int c_value, new_val = 0; /* current value of filesys type (int). */
565 char ret_value[BUFSIZ];
567 if (*current == NULL)
570 c_value = atoi(*current);
572 if (GetFSVal("student", SMS_FS_STUDENT, c_value, &new_val) == FALSE)
574 if (GetFSVal("faculty", SMS_FS_FACULTY, c_value, &new_val) == FALSE)
576 if (GetFSVal("staff", SMS_FS_STAFF, c_value, &new_val) == FALSE)
578 if (GetFSVal("miscellaneous", SMS_FS_MISC, c_value, &new_val) == FALSE)
581 FreeAndClear(current, TRUE);
582 sprintf(ret_value, "%d", new_val);
583 *current = Strsave(ret_value);
587 /* Function Name: CanonicalizeHostname
588 * Description: This function takes a machine name and canonicalize's it.
589 * Arguments: machine - name of the machine to work on.
590 * Returns: new name or NULL if nameserver returns error
594 CanonicalizeHostname(machine)
597 struct hostent *hostinfo;
599 hostinfo = gethostbyname(machine);
600 /* If this fails then we just return what we were passed. */
601 if (hostinfo != (struct hostent *) NULL)
602 machine = hostinfo->h_name;
606 /* Function Name: Strsave
607 * Description: save a string.
608 * Arguments: string - the string to save.
609 * Returns: The malloced string, now safely saved, or NULL.
616 register char *newstr = malloc((unsigned) strlen(str) + 1);
618 if (newstr == (char *) NULL)
619 return ((char *) NULL);
621 return (strcpy(newstr, str));
624 /* Function Name: Print
625 * Description: prints out all the arguments on a single line.
626 * Arguments: argc, argv - the standard SMS arguments.
627 * callback - the callback function - NOT USED.
633 Print(argc, argv, callback)
635 char **argv, *callback;
641 (void) strcpy(buf,argv[0]); /* no newline 'cause Put_message adds one */
642 for (i = 1; i < argc; i++)
643 (void) sprintf(buf,"%s %s",buf,argv[i]);
644 (void) Put_message(buf);
649 /* Function Name: PrintByType
650 * Description: This function prints all members of the type specified
651 * by the callback arg, unless the callback is NULL, in which
652 * case it prints all members.
653 * Arguments: argc, argc - normal arguments for sms_callback function.
654 * callback - either a type of member or NULL.
655 * Returns: SMS_CONT or SMS_QUIT.
660 PrintByType(argc, argv, callback)
662 char **argv, *callback;
664 if (callback == NULL)
665 return( Print(argc, argv, callback) );
666 if (strcmp(argv[0], callback) == 0)
667 return( Print(argc, argv, callback) );
671 /* Function Name: PrintHelp
672 * Description: Prints Help Information in a NULL terminated
674 * Arguments: message.
675 * Returns: DM_NORMAL.
682 Print(CountArgs(message), message, (char *) NULL);
686 /* Function Name: Loop
687 * Description: This function goes through the entire queue, and
688 * and executes the given function on each element.
689 * Arguments: elem - top element of the queue.
690 * func - the function to execute.
699 while (elem != NULL) {
700 char ** info = (char **) elem->q_data;
707 /* Function Name: QueryLoop
708 * Description: This functions loops through a queue containing
709 * information about some item that we want to perform
710 * an operation on, and then calls the correct routine
711 * perform that operation.
712 * Arguments: top - top of the queue of information.
713 * print_func - print function.
714 * op_function - operation to be performed.
715 * query_string - string the prompts the user whether or not
716 * to perform this operation.
719 * print_opt - should expect one arguent, the info array
721 * is expected to return the name of the item.
722 * op_func - should expect two arguments.
723 * 1) the info array of char *'s.
724 * 2) a boolean the is true if there only
725 * one item in this queue, used for delete
727 * query_string - this should be of such a form that when the
728 * name of the object and '(y/n/q) ?' are appended
729 * then it should still make sense, an example is
734 QueryLoop(elem, print_func, op_func, query_string)
737 FCharStar print_func;
741 char temp_buf[BUFSIZ], *name;
743 one_item = (QueueCount(elem) == 1);
744 while (elem != NULL) {
745 char **info = (char **) elem->q_data;
748 (*op_func) (info, one_item);
750 name = (*print_func) (info); /* call print function. */
751 sprintf(temp_buf,"%s %s (y/n/q)", query_string, name);
752 switch(YesNoQuitQuestion(temp_buf, FALSE)) {
754 (*op_func) (info, one_item);
759 Put_message("Aborting...");
767 /* Function Name: NullPrint
768 * Description: print function that returns nothing.
769 * Arguments: info - a pointer to the info array - Not used.
781 /* Function Name: GetTypeValues
782 * Description: gets legal values for a typed object, keeping a cache
783 * Arguments: type name
784 * Returns: argv of values
792 char *argv[3], *p, **pp, *strsave();
793 struct qelem *elem, *oelem;
794 static struct qelem *cache = NULL;
795 struct cache_elem { char *cache_name; struct qelem *cache_data; } *ce;
797 for (elem = cache; elem; elem = elem->q_forw) {
798 ce = (struct cache_elem *)elem->q_data;
799 if (!strcmp(ce->cache_name, tname))
800 return(ce->cache_data);
807 if (stat = do_sms_query("get_alias", 3, argv, StoreInfo, (char *)&elem)) {
808 com_err(program_name, stat, " in GetTypeValues");
812 for (elem = QueueTop(elem); elem; elem = elem->q_forw) {
813 pp = (char **) elem->q_data;
818 elem = (struct qelem *) malloc(sizeof(struct qelem));
819 ce = (struct cache_elem *) malloc(sizeof(struct cache_elem));
820 ce->cache_name = tname;
821 ce->cache_data = QueueTop(oelem);
822 elem->q_data = (char *)ce;
823 AddQueue(elem, cache);
824 cache = QueueTop(elem);
825 return(ce->cache_data);
829 /* Function Name: GetTypeFromUser
830 * Description: gets a typed value from the user
831 * Arguments: prompt string, type name, buffer pointer
835 GetTypeFromUser(prompt, tname, pointer)
840 char def[BUFSIZ], buffer[BUFSIZ], *p, *argv[3];
844 strcpy(def, *pointer);
845 strcpy(buffer, prompt);
846 strcat(buffer, " (");
847 for (elem = GetTypeValues(tname); elem; elem = elem->q_forw) {
848 strcat(buffer, elem->q_data);
850 strcat(buffer, ", ");
853 if (strlen(buffer) > 64)
854 sprintf(buffer, "%s (? for help)", prompt);
855 GetValueFromUser(buffer, pointer);
856 if (**pointer == '?') {
857 sprintf(buffer, "Type %s is one of:", tname);
859 for (elem = GetTypeValues(tname); elem; elem = elem->q_forw) {
860 Put_message(elem->q_data);
862 *pointer = strsave(def);
863 return(GetTypeFromUser(prompt, tname, pointer));
865 for (elem = GetTypeValues(tname); elem; elem = elem->q_forw) {
866 if (!cistrcmp(elem->q_data, *pointer))
869 sprintf(buffer, "\"%s\" is not a legal value for %s. Use one of:",
872 for (elem = GetTypeValues(tname); elem; elem = elem->q_forw) {
873 Put_message(elem->q_data);
875 sprintf(buffer, "Are you sure you want \"%s\" to be a legal %s",
877 if (YesNoQuestion("Do you want this to be a new legal value", 0) &&
878 YesNoQuestion(buffer, 0)) {
882 for (p = argv[2]; *p; p++)
885 if (stat = do_sms_query("add_alias", 3, argv, Scream, NULL)) {
886 com_err(program_name, stat, " in add_alias");
888 elem = (struct qelem *) malloc(sizeof(struct qelem));
889 elem->q_data = strsave(*pointer);
890 AddQueue(elem, GetTypeValues(tname));
891 Put_message("Done.");
894 *pointer = strsave(def);
895 return(GetTypeFromUser(prompt, tname, pointer));
899 do_sms_query(name, argc, argv, proc, hint)
909 status = sms_query(name, argc, argv, proc, hint);
910 if (status != SMS_ABORTED && status != SMS_NOT_CONNECTED)
912 status = sms_connect();
914 com_err(whoami, status, " while re-connecting to server");
917 status = sms_auth(whoami);
919 com_err(whoami, status, " while re-authenticating to server");
923 status = sms_query(name, argc, argv, proc, hint);