diff options
Diffstat (limited to 'libtecla-1.6.1/demo2.c')
-rw-r--r-- | libtecla-1.6.1/demo2.c | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/libtecla-1.6.1/demo2.c b/libtecla-1.6.1/demo2.c new file mode 100644 index 0000000..63a9a0c --- /dev/null +++ b/libtecla-1.6.1/demo2.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. + * + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons + * to whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT + * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL + * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING + * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION + * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder + * shall not be used in advertising or otherwise to promote the sale, use + * or other dealings in this Software without prior written authorization + * of the copyright holder. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <errno.h> +#include <locale.h> + +#include <unistd.h> +#include <sys/stat.h> + +#include "libtecla.h" + +/* + * If the library is being built with file-system access excluded, this + * demo program won't have anything to demonstrate. + */ +#ifdef WITHOUT_FILE_SYSTEM +int main(int argc, char *argv[]) +{ + fprintf(stderr, "\n" + " This program normally demonstrates tecla's path-lookup\n" + " facility. However libtecla has been installed with\n" + " file-system facilities explicitly excluded, so there is\n" + " nothing to demonstrate.\n\n"); + return 1; +} +#else +/* + * Encapsulate the resources needed by this demo. + */ +typedef struct { + GetLine *gl; /* The line editor */ + PathCache *pc; /* A cache of executables in the user's path */ + PcaPathConf *ppc; /* The configuration argument of pca_path_completions() */ +} DemoRes; + +/* + * The following functions allocate and free instances of the above + * structure. + */ +static DemoRes *new_DemoRes(void); +static DemoRes *del_DemoRes(DemoRes *res); + +/* + * Search backwards for the start of a pathname. + */ +static char *start_of_path(const char *string, int back_from); + +/* + * Find the array indexes of the first character of the first + * space-delimited word in the specified string, and of the character + * that follows it. + */ +static int get_word_limits(const char *string, int *wa, int *wb); + +/* + * This is the demonstration completion callback function (defined below). + */ +static CPL_MATCH_FN(demo_cpl_fn); + +/* The function which displays the introductory text of the demo */ + +static void show_demo_introduction(GetLine *gl); + +/*....................................................................... + * This demo takes no arguments. It reads lines of input until the + * word 'exit' is entered, or C-d is pressed. It replaces the default + * tab-completion callback with one which when invoked at the start of + * a line, looks up completions of commands in the user's execution + * path, and when invoked in other parts of the line, reverts to + * normal filename completion. Whenever a new line is entered, it + * extracts the first word on the line, looks it up in the user's + * execution path to see if it corresponds to a known executable file, + * and if so, displays the full pathname of the file, along with the + * remaining arguments. + */ +int main(int argc, char *argv[]) +{ + char *line; /* A line of input */ + DemoRes *res; /* The resources of the demo */ + int wa,wb; /* The delimiting indexes of a word in line[] */ + int major,minor,micro; /* The version number of the library */ +/* + * Allocate the resources needed by this demo. + */ + res = new_DemoRes(); + if(!res) + return 1; +/* + * If the user has the LC_CTYPE or LC_ALL environment variables set, + * enable display of characters corresponding to the specified locale. + */ + (void) setlocale(LC_CTYPE, ""); +/* + * Lookup and display the version number of the library. + */ + libtecla_version(&major, &minor, µ); + printf("\n Welcome to the path-search demo of libtecla version %d.%d.%d\n", + major, minor, micro); +/* + * Display some introductory text, left-justifying it within the current + * width of the terminal and enclosing it in a box of asterixes. + */ + show_demo_introduction(res->gl); +/* + * Read lines of input from the user and print them to stdout. + */ + do { +/* + * Get a new line from the user. + */ + line = gl_get_line(res->gl, "$ ", NULL, 0); + if(!line) + break; +/* + * Work out the extent of the first word in the input line, and + * try to identify this as a command in the path, displaying the + * full pathname of the match if found. + */ + if(get_word_limits(line, &wa, &wb) == 0) { + char *cmd = pca_lookup_file(res->pc, line + wa, wb-wa, 0); + if(cmd) { + printf("Command=%s\n", cmd); + printf("Arguments=%s", line+wb); + } else { + printf("Command not found\n"); + }; + }; +/* + * If the user types "exit", quit the program. + */ + if(strcmp(line, "exit\n")==0) + break; + } while(1); +/* + * Clean up. + */ + res = del_DemoRes(res); + return 0; +} + +/*....................................................................... + * This completion callback searches for completions of executables in + * the user's path when invoked on a word at the start of the path, and + * performs normal filename completion elsewhere. + */ +static CPL_MATCH_FN(demo_cpl_fn) +{ +/* + * Get the resource object that was passed to gl_customize_completion(). + */ + DemoRes *res = (DemoRes *) data; +/* + * Find the start of the filename prefix to be completed, searching + * backwards for the first unescaped space, or the start of the line. + */ + char *start = start_of_path(line, word_end); +/* + * Skip spaces preceding the start of the prefix. + */ + while(start > line && isspace((int)(unsigned char) start[-1])) + start--; +/* + * If the filename prefix is at the start of the line, attempt + * to complete the filename as a command in the path. Otherwise + * perform normal filename completion. + */ + return (start == line) ? + pca_path_completions(cpl, res->ppc, line, word_end) : + cpl_file_completions(cpl, NULL, line, word_end); +} + +/*....................................................................... + * Search backwards for the potential start of a filename. This + * looks backwards from the specified index in a given string, + * stopping at the first unescaped space or the start of the line. + * + * Input: + * string const char * The string to search backwards in. + * back_from int The index of the first character in string[] + * that follows the pathname. + * Output: + * return char * The pointer to the first character of + * the potential pathname, or NULL on error. + */ +static char *start_of_path(const char *string, int back_from) +{ + int i, j; +/* + * Search backwards from the specified index. + */ + for(i=back_from-1; i>=0; i--) { + int c = string[i]; +/* + * Stop on unescaped spaces. + */ + if(isspace((int)(unsigned char)c)) { +/* + * The space can't be escaped if we are at the start of the line. + */ + if(i==0) + break; +/* + * Find the extent of the escape characters which precedes the space. + */ + for(j=i-1; j>=0 && string[j]=='\\'; j--) + ; +/* + * If there isn't an odd number of escape characters before the space, + * then the space isn't escaped. + */ + if((i - 1 - j) % 2 == 0) + break; + }; + }; + return (char *)string + i + 1; +} + +/*....................................................................... + * Create a new DemoRes object containing the resources needed by the + * demo. + * + * Output: + * return DemoRes * The new object, or NULL on error. + */ +static DemoRes *new_DemoRes(void) +{ + DemoRes *res; /* The object to be returned */ +/* + * Allocate the container. + */ + res = (DemoRes *)malloc(sizeof(DemoRes)); + if(!res) { + fprintf(stderr, "new_DemoRes: Insufficient memory.\n"); + return NULL; + }; +/* + * Before attempting any operation that might fail, initialize the + * container at least up to the point at which it can safely be passed + * to del_DemoRes(). + */ + res->gl = NULL; + res->pc = NULL; + res->ppc = NULL; +/* + * Create the line editor, specifying a max line length of 500 bytes, + * and 10000 bytes to allocate to storage of historical input lines. + */ + res->gl = new_GetLine(500, 10000); + if(!res->gl) + return del_DemoRes(res); +/* + * Enable text attribute formatting directives in prompt strings. + */ + gl_prompt_style(res->gl, GL_FORMAT_PROMPT); +/* + * Allocate a cache of the executable files found in the user's path. + */ + res->pc = new_PathCache(); + if(!res->pc) + return del_DemoRes(res); +/* + * Populate the cache with the contents of the user's path. + */ + if(pca_scan_path(res->pc, getenv("PATH"))) + return del_DemoRes(res); +/* + * Arrange for susequent calls to pca_lookup_file() and pca_path_completions() + * to only report files that are executable by the user. + */ + pca_set_check_fn(res->pc, cpl_check_exe, NULL); +/* + * Allocate a configuration object for use with pca_path_completions(). + */ + res->ppc = new_PcaPathConf(res->pc); + if(!res->ppc) + return del_DemoRes(res); +/* + * Replace the builtin filename completion callback with one which + * searches for completions of executables in the user's path when + * invoked on a word at the start of the path, and completes files + * elsewhere. + */ + if(gl_customize_completion(res->gl, res, demo_cpl_fn)) + return del_DemoRes(res); + return res; +} + +/*....................................................................... + * Delete a DemoRes object. + * + * Input: + * res DemoRes * The object to be deleted. + * Output: + * return DemoRes * The deleted object (always NULL). + */ +static DemoRes *del_DemoRes(DemoRes *res) +{ + if(res) { + res->gl = del_GetLine(res->gl); + res->pc = del_PathCache(res->pc); + res->ppc = del_PcaPathConf(res->ppc); + free(res); + }; + return NULL; +} + +/*....................................................................... + * Return the limits of the word at the start of a given string, ignoring + * leading white-space, and interpretting the first unescaped space, tab or + * the end of the line, as the end of the word. + * + * Input: + * string const char * The string to tokenize. + * Input/Output: + * wa,wb int * The indexes of the first character of the word, + * and the character which follows the last + * character of the word, will be assigned to + * *wa and *wb, respectively. + * Output: + * return int 0 - A word was found. + * 1 - No word was found before the end of the + * string. + */ +static int get_word_limits(const char *string, int *wa, int *wb) +{ + int escaped = 0; /* True if the next character is escaped */ +/* + * Skip leading white-space. + */ + for(*wa=0; isspace((int)(unsigned char)string[*wa]); (*wa)++) + ; +/* + * Find the first unescaped space, stopping early if the end of the + * string is reached. + */ + for(*wb = *wa; ; (*wb)++) { + int c = string[*wb]; + if(c=='\\') + escaped = !escaped; + else if((!escaped && isspace((int)(unsigned char)c)) || c=='\0') + break; + }; + return *wa == *wb; +} + +/*....................................................................... + * Display introductory text to the user, formatted according to the + * current terminal width and enclosed in a box of asterixes. + * + * Input: + * gl GetLine * The resource object of gl_get_line(). + */ +static void show_demo_introduction(GetLine *gl) +{ + int start; /* The column in which gl_display_text() left the cursor */ + int i; +/* + * Break the indtroductory text into an array of strings, so as to + * avoid overflowing any compiler string limits. + */ + const char *doc[] = { + "This program demonstrates the use of the pca_lookup_file() function ", + "for finding executables in the UNIX PATH. It also demonstrates ", + "tab completion of the names of executables found in the path. For ", + "example, if you type:\n\n ta\n\nthen hit the tab key, you will be ", + "presented with a list of executables such as tar and tail whose names ", + "start with the string \"ta\". If you decide to add an \"r\" to select ", + "the tar command, then you type return, the full pathname of the tar ", + "program will be printed.\n\nThe file demo2.c contains the code ", + "of this program, and is fully commented to enable its use as ", + "a working example of how to use the facilities documented in the ", + "pca_lookup_file man page.\n"}; +/* + * Form the top line of the documentation box by filling the area of + * the line between a " *" prefix and a "* " suffix with asterixes. + */ + printf("\n"); + gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); +/* + * Justify the documentation text within margins of asterixes. + */ + for(start=0,i=0; i<sizeof(doc)/sizeof(doc[0]) && start >= 0; i++) + start = gl_display_text(gl, 0, " * ", " * ", ' ', 80, start,doc[i]); +/* + * Draw the bottom line of the documentation box. + */ + gl_display_text(gl, 0, " *", "* ", '*', 80, 0, "\n"); + printf("\n"); +} + +#endif /* ifndef WITHOUT_FILE_SYSTEM */ |