From 5062276bdb0461f955a674120ceaeb4c4b9471ce Mon Sep 17 00:00:00 2001 From: ketmar Date: Wed, 2 Jul 2008 12:34:59 +0300 Subject: [PATCH] new built-ins: SORT/Sort, COMMAND/Command --- ChangeLog | 16 +++ Jamnew.sample | 12 +++ builtins.c | 327 ++++++++++++++++++++++++++++++++++++++++++++++------------ builtins.h | 9 +- lists.c | 281 ++++++++++++++++++++++--------------------------- lists.h | 37 ++++--- 6 files changed, 439 insertions(+), 243 deletions(-) create mode 100644 Jamnew.sample diff --git a/ChangeLog b/ChangeLog index f12f7d4..f31c5a1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,2 +1,18 @@ [+] PWD/Pwd built-in added; usage: pwd = [ PWD ] ; + [+] SORT/Sort built-in added; usage: + sorted = [ SORT $(list) ] ; + [+] COMMAND/Command built-in added; usage: + res = [ COMMAND "shellcmd" ]; + options: + exit-status, exit-code: + return exit code as last list element + no-output: + don't return command output + no-trim: + don't trim command output; + default: trim left and right + no-trim-left: + don't left-trim command output + no-trim-right: + don't right-trim command output diff --git a/Jamnew.sample b/Jamnew.sample new file mode 100644 index 0000000..5300344 --- /dev/null +++ b/Jamnew.sample @@ -0,0 +1,12 @@ +pwd = [ PWD ] ; +Echo "currend dir" $(PWD) ; + + +list = c b a d ; +Echo "unsorted:" $(list) "|" ; +sorted = [ SORT $(list) ] ; +Echo "sorted:" $(sorted) "|" ; + + +cmd = [ COMMAND "which sdl-config" exit-code ] ; +Echo "cmd:" $(cmd) "|" ; diff --git a/builtins.c b/builtins.c index 76edeb5..4d6a2b3 100644 --- a/builtins.c +++ b/builtins.c @@ -57,65 +57,74 @@ int glob (const char *s, const char *c); -void load_builtins () { - bindrule( "Always" )->procedure = - bindrule( "ALWAYS" )->procedure = - parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TOUCHED); - - bindrule( "Depends" )->procedure = - bindrule( "DEPENDS" )->procedure = - parse_make( builtin_depends, P0, P0, P0, C0, C0, 0); - - bindrule( "echo" )->procedure = - bindrule( "Echo" )->procedure = - bindrule( "ECHO" )->procedure = - parse_make( builtin_echo, P0, P0, P0, C0, C0, 0); - - bindrule( "exit" )->procedure = - bindrule( "Exit" )->procedure = - bindrule( "EXIT" )->procedure = - parse_make( builtin_exit, P0, P0, P0, C0, C0, 0); - - bindrule( "Glob" )->procedure = - bindrule( "GLOB" )->procedure = - parse_make( builtin_glob, P0, P0, P0, C0, C0, 0); - - bindrule( "Includes" )->procedure = - bindrule( "INCLUDES" )->procedure = - parse_make( builtin_depends, P0, P0, P0, C0, C0, 1); - - bindrule( "Leaves" )->procedure = - bindrule( "LEAVES" )->procedure = - parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES); - - bindrule( "Match" )->procedure = - bindrule( "MATCH" )->procedure = - parse_make( builtin_match, P0, P0, P0, C0, C0, 0); - - bindrule( "NoCare" )->procedure = - bindrule( "NOCARE" )->procedure = - parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE); - - bindrule( "NOTIME" )->procedure = - bindrule( "NotFile" )->procedure = - bindrule( "NOTFILE" )->procedure = - parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE); - - bindrule( "NoUpdate" )->procedure = - bindrule( "NOUPDATE" )->procedure = - parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE); - - bindrule( "Temporary" )->procedure = - bindrule( "TEMPORARY" )->procedure = - parse_make( builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP); - - bindrule( "HdrMacro" )->procedure = - bindrule( "HDRMACRO" )->procedure = - parse_make( builtin_hdrmacro, P0, P0, P0, C0, C0, 0); - - bindrule( "PWD" )->procedure = - bindrule( "Pwd" )->procedure = - parse_make( builtin_pwd, P0, P0, P0, C0, C0, 0); + +void load_builtins (void) { + bindrule("Always")->procedure = + bindrule("ALWAYS")->procedure = + parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TOUCHED); + + bindrule("Depends")->procedure = + bindrule("DEPENDS")->procedure = + parse_make(builtin_depends, P0, P0, P0, C0, C0, 0); + + bindrule("echo")->procedure = + bindrule("Echo")->procedure = + bindrule("ECHO")->procedure = + parse_make(builtin_echo, P0, P0, P0, C0, C0, 0); + + bindrule("exit")->procedure = + bindrule("Exit")->procedure = + bindrule("EXIT")->procedure = + parse_make(builtin_exit, P0, P0, P0, C0, C0, 0); + + bindrule("Glob")->procedure = + bindrule("GLOB")->procedure = + parse_make(builtin_glob, P0, P0, P0, C0, C0, 0); + + bindrule("Includes")->procedure = + bindrule("INCLUDES")->procedure = + parse_make(builtin_depends, P0, P0, P0, C0, C0, 1); + + bindrule("Leaves")->procedure = + bindrule("LEAVES")->procedure = + parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_LEAVES); + + bindrule("Match")->procedure = + bindrule("MATCH")->procedure = + parse_make(builtin_match, P0, P0, P0, C0, C0, 0); + + bindrule("NoCare")->procedure = + bindrule("NOCARE")->procedure = + parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOCARE); + + bindrule("NOTIME")->procedure = + bindrule("NotFile")->procedure = + bindrule("NOTFILE")->procedure = + parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOTFILE); + + bindrule("NoUpdate")->procedure = + bindrule("NOUPDATE")->procedure = + parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_NOUPDATE); + + bindrule("Temporary")->procedure = + bindrule("TEMPORARY")->procedure = + parse_make(builtin_flags, P0, P0, P0, C0, C0, T_FLAG_TEMP); + + bindrule("HdrMacro")->procedure = + bindrule("HDRMACRO")->procedure = + parse_make(builtin_hdrmacro, P0, P0, P0, C0, C0, 0); + + bindrule("PWD")->procedure = + bindrule("Pwd")->procedure = + parse_make(builtin_pwd, P0, P0, P0, C0, C0, 0); + + bindrule("SORT")->procedure = + bindrule("Sort")->procedure = + parse_make(builtin_sort, P0, P0, P0, C0, C0, 0); + + bindrule("COMMAND")->procedure = + bindrule("Command")->procedure = + parse_make(builtin_command, P0, P0, P0, C0, C0, 0); } @@ -133,7 +142,7 @@ LIST *builtin_depends (PARSE *parse, LOL *args, int *jmp) { LIST *l; for (l = targets; l; l = list_next(l)) { - TARGET *t = bindtarget( l->string); + TARGET *t = bindtarget(l->string); /* If doing INCLUDES, switch to the TARGET's include */ /* TARGET, creating it if needed. The internal include */ /* TARGET shares the name of its parent. */ @@ -141,9 +150,8 @@ LIST *builtin_depends (PARSE *parse, LOL *args, int *jmp) { if (!t->includes) t->includes = copytarget(t); t = t->includes; } - t->depends = targetlist( t->depends, sources); + t->depends = targetlist(t->depends, sources); } - return L0; } @@ -157,7 +165,6 @@ LIST *builtin_depends (PARSE *parse, LOL *args, int *jmp) { LIST * builtin_echo (PARSE *parse, LOL *args, int *jmp) { list_print(lol_get(args, 0)); printf("\n"); - return L0; } @@ -172,7 +179,6 @@ LIST *builtin_exit (PARSE *parse, LOL *args, int *jmp) { list_print(lol_get(args, 0)); printf("\n"); exit(EXITBAD); /* yeech */ - return L0; } @@ -187,7 +193,6 @@ LIST *builtin_flags (PARSE *parse, LOL *args, int *jmp) { LIST *l = lol_get(args, 0); for (; l; l = list_next(l)) bindtarget(l->string)->flags |= parse->num; - return L0; } @@ -271,7 +276,7 @@ LIST *builtin_match (PARSE *parse, LOL *args, int *jmp) { LIST *builtin_hdrmacro (PARSE *parse, LOL *args, int *jmp) { - LIST* l = lol_get(args, 0); + LIST *l = lol_get(args, 0); for (; l; l = list_next(l)) { TARGET *t = bindtarget(l->string); @@ -290,13 +295,199 @@ LIST *builtin_hdrmacro (PARSE *parse, LOL *args, int *jmp) { * * Usage: pwd = [ PWD ] ; */ -LIST* builtin_pwd (PARSE *parse, LOL *args, int *jmp) { +LIST *builtin_pwd (PARSE *parse, LOL *args, int *jmp) { char pwd_buffer[PATH_MAX]; if (!getcwd(pwd_buffer, sizeof(pwd_buffer))) { perror("can not get current directory"); return L0; } - return list_new(L0, pwd_buffer, 0); } + + + +/* backported from boost-jam */ +LIST *builtin_sort (PARSE *parse, LOL *args, int *jmp) { + LIST *arg = lol_get(args, 0); + /*printf("unsorted: "); list_print(arg); printf("\n");*/ + arg = list_sort(arg); + /*printf("sorted: "); list_print(arg); printf("\n");*/ + return arg; +} + + +#if 0 +/* backported from boost-jam */ +LIST *builtin_normalize_path (PARSE *parse, LOL *args, int *jmp) { + LIST *arg = lol_get(args, 0); + /* First, we iterate over all '/'-separated elements, starting from + the end of string. If we see '..', we remove previous path elements. + If we see '.', we remove it. + The removal is done by putting '\1' in the string. After all the string + is processed, we do a second pass, removing '\1' characters. + */ + string in[1], out[1], tmp[1]; + char *end; /* Last character of the part of string still to be processed. */ + char *current; /* Working pointer. */ + int dotdots = 0; /* Number of '..' elements seen and not processed yet. */ + int rooted = arg->string[0] == '/'; + char *result; + + /* Make a copy of input: we should not change it. */ + string_new(in); + if (!rooted) string_push_back(in, '/'); + while (arg) { + string_append(in, arg->string); + arg = list_next(arg); + if (arg) string_append(in, "/"); + } + + end = in->value+in->size-1; + current = end; + + for (; end >= in->value;) { + /* Set 'current' to the next occurence of '/', which always exists. */ + for (current = end; *current != '/'; --current) ; + if (current == end && current != in->value) { + /* Found a trailing slash. Remove it. */ + *current = '\1'; + } else if (current == end && *(current+1) == '/') { + /* Found duplicated slash. Remove it. */ + *current = '\1'; + } else if (end - current == 1 && strncmp(current, "/.", 2) == 0) { + /* Found '/.'. Drop them all. */ + *current = '\1'; + *(current+1) = '\1'; + } else if (end - current == 2 && strncmp(current, "/..", 3) == 0) { + /* Found '/..' */ + *current = '\1'; + *(current+1) = '\1'; + *(current+2) = '\1'; + ++dotdots; + } else if (dotdots) { + char* p = current; + memset(current, '\1', end-current+1); + --dotdots; + } + end = current-1; + } + + string_new(tmp); + while (dotdots--) string_append(tmp, "/.."); + string_append(tmp, in->value); + string_copy(in, tmp->value); + string_free(tmp); + + string_new(out); + /* The resulting path is either empty or has '/' as the first significant + element. If the original path was not rooted, we need to drop first '/'. + If the original path was rooted, and we've got empty path, need to add '/' + */ + if (!rooted) { + current = strchr(in->value, '/'); + if (current) *current = '\1'; + } + + for (current = in->value; *current; ++current) if (*current != '\1') string_push_back(out, *current); + + result = newstr(out->size ? out->value : (rooted ? "/" : ".")); + string_free(in); + string_free(out); + + return list_new(0, result); +} +#endif + + +/* backported from boost-jam */ +LIST *builtin_command (PARSE *parse, LOL *args, int *jmp) { + LIST *l; + LIST *command = 0; + LIST *result = 0; + int ret, f; + char buffer[1024]; + FILE *p = NULL; + int exit_status = -1; + int exit_status_opt = 0; + int no_output_opt = 0; + int trim_left = 1; + int trim_right = 1; + char *str, *spos; + int strSize = 1024; + + + /* for each arg */ + for (l = lol_get(args, 0); l; l = l->next) { + if (!command) { + command = l; + } else { + /* for each string in list: check for arg */ + if (!strcmp("exit-status", l->string)) { + exit_status_opt = 1; + } else if (!strcmp("exit-code", l->string)) { + exit_status_opt = 1; + } else if (!strcmp("no-output", l->string)) { + no_output_opt = 1; + } else if (!strcmp("no-trim", l->string)) { + trim_left = trim_right = 0; + } else if (!strcmp("no-trim-left", l->string)) { + trim_left = 0; + } else if (!strcmp("no-trim-right", l->string)) { + trim_right = 0; + } + } + } + + fflush(NULL); + if (!(str = (char *)malloc((strSize+1)*sizeof(char)))) { + perror("COMMAND: out of memory"); + } + if (!(p = popen(command->string, "r"))) { + free(str); + return L0; + } + *str = '\0'; spos = str; + while ((ret = fread(buffer, sizeof(char), sizeof(buffer)-1, p)) > 0) { + buffer[ret] = 0; + if (!no_output_opt) { + f = spos-str; + if ((spos-str)+ret > strSize) { + strSize = (spos-str)+ret+2048; + spos = str; + str = (char *)realloc(str, (strSize+1)*sizeof(char)); + if (!str) { + pclose(p); + free(spos); + perror("COMMAND: out of memory"); + } + spos = str+f; + } + strcpy(spos, buffer); + spos += ret; + } + } + exit_status = pclose(p); + + /* trim output if necessary */ + if (trim_right) { + spos = str+strlen(str)-1; + while (spos >= str && *((unsigned char *)spos) <= ' ') spos--; + *(++spos) = '\0'; + } + spos = str; + if (trim_left) { + while (*spos && *((unsigned char *)spos) <= ' ') spos++; + } + /* The command output is returned first. */ + result = list_new(L0, spos, 0); + free(str); + + /* The command exit result next. */ + if (exit_status_opt) { + sprintf(buffer, "%d", exit_status); + result = list_new(result, buffer, 0); + } + + return result; +} diff --git a/builtins.h b/builtins.h index d545457..61b11cd 100644 --- a/builtins.h +++ b/builtins.h @@ -10,7 +10,7 @@ * 01/10/01 (seiwald) - split from compile.h */ -void load_builtins (); +void load_builtins (void); LIST *builtin_depends (PARSE *parse, LOL *args, int *jmp); @@ -21,4 +21,9 @@ LIST *builtin_glob (PARSE *parse, LOL *args, int *jmp); LIST *builtin_match (PARSE *parse, LOL *args, int *jmp); LIST *builtin_hdrmacro (PARSE *parse, LOL *args, int *jmp); -LIST* builtin_pwd (PARSE *parse, LOL *args, int *jmp); +LIST *builtin_pwd (PARSE *parse, LOL *args, int *jmp); +LIST *builtin_sort (PARSE *parse, LOL *args, int *jmp); +#if 0 +LIST *builtin_normalize_path (PARSE *parse, LOL *args, int *jmp); +#endif +LIST *builtin_command (PARSE *parse, LOL *args, int *jmp); diff --git a/lists.c b/lists.c index ccf2b04..6114d8a 100644 --- a/lists.c +++ b/lists.c @@ -30,260 +30,229 @@ static LIST *freelist = 0; /* junkpile for list_free() */ + /* * list_append() - append a list onto another one, returning total */ - -LIST * -list_append( - LIST *l, - LIST *nl ) -{ - if( !nl ) - { - /* Just return l */ - } - else if( !l ) - { - l = nl; - } - else - { - /* Graft two non-empty lists. */ - l->tail->next = nl; - l->tail = nl->tail; +LIST *list_append (LIST *l, LIST *nl) { + if (!nl) { + /* Just return l */ + } else if (!l) { + l = nl; + } else { + /* Graft two non-empty lists. */ + l->tail->next = nl; + l->tail = nl->tail; } - return l; } + /* * list_new() - tack a string onto the end of a list of strings */ - -LIST * -list_new( - LIST *head, - const char *string, - int copy ) -{ +/* copy!=0: copystr; else newstr */ +LIST *list_new (LIST *head, const char *string, int copy) { LIST *l; - if( DEBUG_LISTS ) - printf( "list > %s <\n", string ); + if (DEBUG_LISTS) printf("list > %s <\n", string); /* Copy/newstr as needed */ - - string = copy ? copystr( string ) : newstr( string ); + string = copy?copystr(string):newstr(string); /* Get list struct from freelist, if one available. */ /* Otherwise allocate. */ /* If from freelist, must free string first */ - - if( freelist ) - { - l = freelist; - freestr( l->string ); - freelist = freelist->next; - } - else - { - l = (LIST *)malloc( sizeof( *l ) ); - } + if (freelist) { + l = freelist; + freestr(l->string); + freelist = freelist->next; + } else l = (LIST *)malloc(sizeof(*l)); /* If first on chain, head points here. */ /* If adding to chain, tack us on. */ /* Tail must point to this new, last element. */ - - if( !head ) head = l; - else head->tail->next = l; + if (!head) head = l; else head->tail->next = l; head->tail = l; l->next = 0; - l->string = string; - return head; } + /* * list_copy() - copy a whole list of strings (nl) onto end of another (l) */ - -LIST * -list_copy( - LIST *l, - LIST *nl ) -{ - for( ; nl; nl = list_next( nl ) ) - l = list_new( l, nl->string, 1 ); - +LIST *list_copy (LIST *l, LIST *nl) { + for (; nl; nl = list_next(nl)) l = list_new(l, nl->string, 1); return l; } + /* * list_sublist() - copy a subset of a list of strings */ +LIST *list_sublist (LIST *l, int start, int count) { + LIST *nl = 0; -LIST * -list_sublist( - LIST *l, - int start, - int count ) -{ - LIST *nl = 0; - - for( ; l && start--; l = list_next( l ) ) - ; + for (; l && start--; l = list_next(l)) ; + for (; l && count--; l = list_next(l)) nl = list_new(nl, l->string, 1); + return nl; +} - for( ; l && count--; l = list_next( l ) ) - nl = list_new( nl, l->string, 1 ); - return nl; +/* backported from boost-jam; TEST IT!!! */ +/* + * list_sort() - sort list + */ +LIST *list_sort (LIST *l) { + LIST *first = 0; + LIST *second = 0; + LIST *merged = l; + LIST *result; + + if (!l) return L0; + for (;;) { + /* Split the list in two */ + LIST **dst = &first; + LIST *src = merged; + for (;;) { + *dst = list_append(*dst, list_new(0, src->string, 1)); + if (!src->next) break; + if (strcmp(src->string, src->next->string) > 0) dst = (dst==&first)?&second:&first; + src = src->next; + } + if (merged != l) list_free(merged); + merged = 0; + if (second == 0) { result = first; break; } + /* Merge lists 'first' and 'second' into 'merged' and free 'first'/'second'. */ + { + LIST *f = first; + LIST *s = second; + while (f && s) { + if (strcmp(f->string, s->string) < 0) { + merged = list_append(merged, list_new(0, f->string, 1)); + f = f->next; + } else { + merged = list_append(merged, list_new(0, s->string, 1)); + s = s->next; + } + } + merged = list_copy(merged, f); + merged = list_copy(merged, s); + list_free(first); + list_free(second); + first = second = 0; + } + } + return result; } + /* * list_free() - free a list of strings */ - -void -list_free( LIST *head ) -{ +void list_free (LIST *head) { /* Just tack onto freelist. */ - - if( head ) - { - head->tail->next = freelist; - freelist = head; + if (head) { + head->tail->next = freelist; + freelist = head; } } + /* * list_print() - print a list of strings to stdout */ - -void -list_print( LIST *l ) -{ - for( ; l; l = list_next( l ) ) - printf( "%s ", l->string ); +void list_print (LIST *l) { + for (; l; l = list_next(l)) printf("%s ", l->string); } + +/* FIXME! */ /* * list_printq() - print a list of safely quoted strings to a file */ - -void -list_printq( FILE *out, LIST *l ) -{ +void list_printq (FILE *out, LIST *l) { /* Dump each word, enclosed in "s */ /* Suitable for Jambase use. */ - - for( ; l; l = list_next( l ) ) - { - const char *p = l->string; - const char *ep = p + strlen( p ); - const char *op = p; - - fputc( '\n', out ); - fputc( '\t', out ); - fputc( '"', out ); - - /* Any embedded "'s? Escape them */ - - while ((p = (char *)memchr(op, '"', ep-op))) - { - fwrite( op, p - op, 1, out ); - fputc( '\\', out ); - fputc( '"', out ); - op = p + 1; - } - - /* Write remainder */ - - fwrite( op, ep - op, 1, out ); - fputc( '"', out ); - fputc( ' ', out ); + for (; l; l = list_next(l)) { + const char *p = l->string; + const char *ep = p+strlen(p); + const char *op = p; + + fputc('\n', out); + fputc('\t', out); + fputc('"', out); + /* Any embedded "'s? Escape them */ + while ((p = (char *)memchr(op, '"', ep-op))) { + fwrite(op, p-op, 1, out); + fputc('\\', out); + fputc('"', out); + op = p+1; + } + /* Write remainder */ + fwrite(op, ep-op, 1, out); + fputc('"', out); + fputc(' ', out); } } + /* * list_length() - return the number of items in the list */ - -int -list_length( LIST *l ) -{ +int list_length (LIST *l) { int n = 0; - for( ; l; l = list_next( l ), ++n ) - ; - + for (; l; l = list_next(l), ++n) ; return n; } + /* * lol_init() - initialize a LOL (list of lists) */ - -void -lol_init( LOL *lol ) -{ +void lol_init (LOL *lol) { lol->count = 0; } + /* * lol_add() - append a LIST onto an LOL */ - -void -lol_add( - LOL *lol, - LIST *l ) -{ - if( lol->count < LOL_MAX ) - lol->list[ lol->count++ ] = l; +void lol_add (LOL *lol, LIST *l) { + if (lol->count < LOL_MAX) lol->list[lol->count++] = l; } + /* * lol_free() - free the LOL and its LISTs */ - -void -lol_free( LOL *lol ) -{ +void lol_free (LOL *lol) { int i; - for( i = 0; i < lol->count; i++ ) - list_free( lol->list[i] ); - + for (i = 0; i < lol->count; i++) list_free(lol->list[i]); lol->count = 0; } + /* * lol_get() - return one of the LISTs in the LOL */ - -LIST * -lol_get( - LOL *lol, - int i ) -{ - return i < lol->count ? lol->list[i] : 0; +LIST *lol_get (LOL *lol, int i) { + return icount?lol->list[i]:0; } + /* * lol_print() - debug print LISTS separated by ":" */ - -void -lol_print( LOL *lol ) -{ +void lol_print (LOL *lol) { int i; - for( i = 0; i < lol->count; i++ ) - { - if( i ) - printf( " : " ); - list_print( lol->list[i] ); + for (i = 0; i < lol->count; i++) { + if (i) printf(" : "); + list_print(lol->list[i]); } } diff --git a/lists.h b/lists.h index 4504d50..afe0803 100644 --- a/lists.h +++ b/lists.h @@ -47,11 +47,12 @@ typedef struct _list LIST; struct _list { - LIST *next; - LIST *tail; /* only valid in head node */ - const char *string; /* private copy */ + LIST *next; + LIST *tail; /* only valid in head node */ + const char *string; /* private copy */ } ; + /* * LOL - list of LISTs */ @@ -62,23 +63,25 @@ typedef struct _lol LOL; struct _lol { int count; - LIST *list[ LOL_MAX ]; -} ; + LIST *list[LOL_MAX]; +}; + -LIST * list_append( LIST *l, LIST *nl ); -LIST * list_copy( LIST *l, LIST *nl ); -void list_free( LIST *head ); -LIST * list_new( LIST *head, const char *string, int copy ); -void list_print( LIST *l ); -int list_length( LIST *l ); -LIST * list_sublist( LIST *l, int start, int count ); +LIST *list_append (LIST *l, LIST *nl); +LIST *list_copy (LIST *l, LIST *nl); +void list_free (LIST *head); +LIST *list_new (LIST *head, const char *string, int copy); +void list_print (LIST *l); +int list_length (LIST *l); +LIST *list_sublist (LIST *l, int start, int count); +LIST *list_sort (LIST *l); # define list_next( l ) ((l)->next) # define L0 ((LIST *)0) -void lol_add( LOL *lol, LIST *l ); -void lol_init( LOL *lol ); -void lol_free( LOL *lol ); -LIST * lol_get( LOL *lol, int i ); -void lol_print( LOL *lol ); +void lol_add (LOL *lol, LIST *l); +void lol_init (LOL *lol); +void lol_free (LOL *lol); +LIST *lol_get (LOL *lol, int i); +void lol_print (LOL *lol); -- 2.11.4.GIT