/* files.c */ /* songs management, mainly some iterators */ /* * $Author: Espie $ * $Date: 91/05/16 15:05:01 $ * $Revision: 1.8 $ * $Log: files.c,v $ * Revision 1.8 91/05/16 15:05:01 Espie * *** empty log message *** * * Revision 1.7 91/05/12 19:53:05 Espie * Corrected the non-freed icons problem. * * Revision 1.6 91/05/12 15:56:53 Espie * Bugs mostly corrected. * * Revision 1.5 91/05/11 14:58:02 Espie * Corrected an auto-cleanup problem. * * Revision 1.4 91/05/10 00:04:19 Espie * Added albums support. Directories missing, not sure * I will add it... * * Revision 1.3 91/05/09 17:35:17 Espie * Support for appwindow, mk_wbarg optimization. * * Revision 1.2 91/05/08 18:33:46 Espie * Added check_abort() for loooong runs. * Added support for arp pattern-matching... need checking that arp is around. * Does enter subdirectories too ! * * Revision 1.1 91/05/08 15:50:32 Espie * Initial revision * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "song.h" #include "public_play.h" void *malloc(); #include "proto.h" LOCAL struct WBStartup *msg; LOCAL char **tools; /*** * * Tooltypes management * ***/ /* get_arg(tools, tooltype, def): reads the tooltype in the tools array, * as a numerical argument, with def as a default value. */ int get_arg(char **tools, char *tooltype, int def) { int val; char *string = NULL; if (tools) string = FindToolType(tools, tooltype); if (string && sscanf(string, "%d", &val) == 1) return val; else return def; } /* same thing with default tools */ int do_get_arg(char *tooltype, int def) { return get_arg(tools, tooltype, def); } /* map_arg: gives a numerical argument from a limited set * of strings values */ int map_arg(char **tools, char *tooltype, char **names, int def) { int val; char *string = NULL; if (tools) string = FindToolType(tools, tooltype); if (string) { for (val = 0; names; val++, names++) if (MatchToolValue(string, *names)) return val; } return def; } int do_map_arg(char *tooltype, char **names, int def) { return map_arg(tools, tooltype, names, def); } /* exist_tooltype(tools, tooltype): checks if tooltype exists among tools */ BOOL exist_tooltype(char **tools, char *tooltype) { if (!tools) return NULL; return (FindToolType(tools, tooltype) != NULL); } /*** * * Iterators. * Every argument is managed as an iterator * you can resume later. This makes a nice * framework for multiple arguments, pattern-matching, * albums, directory traversal... * ***/ enum it_tag {CLI_list, Match_list, Dir_list, WB_list, Album, Empty}; struct CLI_it { int number; int index; char **names; }; struct WB_it { int number; int index; struct WBArg *args; }; #define MAX_LENGTH 400 struct Album_it { FILE *f; char *pbuf; char buffer[MAX_LENGTH]; }; struct Match_it { struct AnchorPath anchor; BYTE suppl_buffer[MAX_LENGTH]; }; /* struct Dir_List { struct FileInfoBlock examiner; }; */ /* be careful of the fields you use, the FileInfoBlock/AnchorPath * must be longword aligned ! */ struct iterator { enum it_tag tag; struct iterator *next; CLEAN auto_clean; BPTR save_lock; struct DiskObject *currenticon; BOOL cd; BOOL ownlock; union { struct CLI_it CLI; struct WB_it WB; struct Album_it Album; struct Match_it Match; } u; }; /*** * * Support functions * ***/ LOCAL void free_last_icon(struct iterator *it) { if (it->currenticon) FreeDiskObject(it->currenticon); it->currenticon = NULL; } /* where do we get the tooltypes from anyway */ char **grab_tooltypes(struct iterator *it, char *arg) { struct DiskObject *icon; if (icon_around()) { icon = GetDiskObject(arg); if (icon) { if (it) { free_last_icon(it); it->currenticon = icon; } else ToClean(FreeDiskObject, icon); return icon->do_ToolTypes; } } return NULL; } /* we need doFreeAnchorChain 'cause FreeAnchorChain exists only as a pragma */ LOCAL void doFreeAnchorChain(struct AnchorPath *ap) { FreeAnchorChain(ap); } /* getline(buffer, bufsize, f): read a line from file f into buffer, * upto bufsize characters. Puts a \0 at the end of line in place of * the \n. The last line may end without a \n. * If the line is too long, remaining characters are skipped. * returns the strlen of buffer, or -1 if the line is too long. * A NULL f is allowed, in that case, the call will never succeed. * * It is the responsibility of the client to close that file. */ LOCAL int getline(char *buffer, int bufsize, FILE *f) { int i, c; if (!f) return 0; for (i = 0; i < bufsize; i++) { c = fgetc(f); if (c == EOF) { buffer[i] = '\0'; return i; } if (c == '\n') { buffer[i] = '\0'; return i; } buffer[i] = c; } while((c=fgetc(f)) != '\n' && c != EOF); return (-1); } /* The iterators need to change dir in an effective way. */ LOCAL void un_dir(struct iterator *it) { if (it->cd) { if (it->ownlock) UnLock(CurrentDir(it->save_lock)); else CurrentDir(it->save_lock); } it->cd = FALSE; it->ownlock = FALSE; } LOCAL void change_dir(struct iterator *it, BPTR new_dir) { un_dir(it); it->save_lock = CurrentDir(new_dir); it->cd = TRUE; } /* iterator creation. An iterator is always the son of another, * except the root, of course. new_iterator() initializes all * relevant fields to reasonable values. */ LOCAL struct iterator *new_iterator(struct iterator *father) { struct iterator *new; /* this is an aligned structure */ new = AllocMem(sizeof(struct iterator), MEMF_ANY); if (!new) return NULL; new->auto_clean = AllocClean(NIL); ToClean2L(new->auto_clean, FreeMem, new, sizeof(struct iterator)); new->ownlock = FALSE; new->cd = FALSE; new->next = father; new->currenticon = NULL; ToCleanL(new->auto_clean, un_dir, new); ToCleanL(new->auto_clean, free_last_icon, new); return new; } /* insert_iterator(it): returns a new iterator inserted as a son * of it, so executed before we resume it. Note that the address of * it is necessary. */ LOCAL struct iterator *insert_iterator(struct iterator **head) { struct iterator *base; base = new_iterator(*head); *head = base; return base; } /* creation of common iterator types */ LOCAL struct iterator *mk_CLI_iterator(struct iterator **head, int argc, char **argv) { struct iterator *new; new = insert_iterator(head); new->tag = CLI_list; new->u.CLI.names = argv; new->u.CLI.number = argc; new->u.CLI.index = 0; return new; } LOCAL struct iterator *mk_WB_iterator(struct iterator **head, int argn, struct WBArg *argarray) { struct iterator *new; new = insert_iterator(head); new->tag = WB_list; new->u.WB.args = argarray; new->u.WB.number = argn; new->u.WB.index = 0; return new; } /* the arp pattern matcher... */ LOCAL char *match_next(struct AnchorPath *ap) { int res; forever { res = FindNext(ap); if (res) return NULL; if (ap->ap_Info.fib_DirEntryType < 0) return ap->ap_Buf; if (!ap->ap_Flags & APF_DidDir) ap->ap_Flags |= APF_DoDir; } } /* ...and the start of it */ LOCAL char *match_CLI(struct iterator **head) { struct iterator *first, *new; first = *head; forever { if (first->u.CLI.index >= first->u.CLI.number) return NULL; if (requester_type() != ARP) return first->u.CLI.names[first->u.CLI.index++]; else { struct AnchorPath *ap; int res; new = insert_iterator(head); new->tag = Match_list; ap = &new->u.Match.anchor; ap->ap_BreakBits = 0; ap->ap_Flags = APF_DoWild; ap->ap_StrLen = MAX_LENGTH; ToCleanL(new->auto_clean, doFreeAnchorChain, ap); res = FindFirst(first->u.CLI.names[first->u.CLI.index++], ap); if (!res) { if (ap->ap_Info.fib_DirEntryType < 0) return ap->ap_Buf; else { ap->ap_Flags |= APF_DoDir; return match_next(ap); } } } } } /* how to restart an existing iterator */ LOCAL char *resume_iterator(struct iterator **head) { struct iterator *new, *first; struct WBArg *temp; first = *head; switch(first->tag) { case CLI_list: return match_CLI(head); case WB_list: if (first->u.WB.index >= first->u.WB.number) return NULL; temp = first->u.WB.args + first->u.WB.index++; if (temp->wa_Lock) change_dir(first, temp->wa_Lock); return temp->wa_Name; case Match_list: return match_next(&first->u.Match.anchor); case Album: if (!getline(first->u.Album.pbuf, MAX_LENGTH, first->u.Album.f)) { /* temporary kludge */ if (first->u.Album.f) { fclose(first->u.Album.f); first->u.Album.f = NULL; } return NULL; } else { new = mk_CLI_iterator(head, 1, &(first->u.Album.pbuf)); return match_CLI(head); } } } LOCAL void delete_first(struct iterator **head) { struct iterator *next; next = (*head)->next; CleanUp((*head)->auto_clean); *head = next; } /* basic stuff about iterators. Still needs a special test * about albums. */ LOCAL char *do_next_song(struct iterator **head) { char *temp; forever { check_abort(); if (!*head) return NULL; if (temp = resume_iterator(head)) return temp; else delete_first(head); } } /* basic code to deal with albums. Once we've detected an album, * we can insert it. */ LOCAL void insert_album(struct iterator **head, char *arg) { struct iterator *base; base = insert_iterator(head); base->tag = Album; base->u.Album.pbuf = base->u.Album.buffer; base->u.Album.f = fopen(arg, "r"); /* I can't autoclean this stuff: conflict with Lattice ToCleanL(base->auto_clean, fclose, base->u.Album.f); */ } char *next_song(struct iterator **head, BOOL req) { char *test; struct iterator *new; static struct WBArg holder; forever { test = do_next_song(head); if (test) { if (exist_tooltype(grab_tooltypes(*head, test), "ALBUM")) { insert_album(head, test); } else return test; } else { if (req && query_name("Module to play", &holder)) { new = mk_WB_iterator(head, 1, &holder); new->ownlock = TRUE; } else return NULL; } } } /* init_iterator(): builds an initial iterator from the arguments * supplied by the user. */ LOCAL struct iterator *zero; LOCAL struct iterator *init_iterator(int argc, char **argv) { struct iterator *base; zero = NULL; if (argc) { msg = NULL; base = mk_CLI_iterator(&zero, argc, argv); } else { msg = (struct WBStartup *)argv; base = mk_WB_iterator(&zero, msg->sm_NumArgs, msg->sm_ArgList); } return base; } /* insert_args: adds new songs given to the appwindow. */ void insert_args(struct iterator **head, struct AppMessage *msg) { struct iterator *new; new = mk_WB_iterator(head, msg->am_NumArgs, msg->am_ArgList); ToCleanL(new->auto_clean, ReplyMsg, msg); } /* setup_arguments: initial setup for everything */ struct iterator *setup_arguments(int argc, char **argv) { struct iterator *result; result = init_iterator(argc, argv); if (argc) tools = argv; /* grab_tooltypes(NIL, next_song(&result, FALSE)); */ else tools = grab_tooltypes(NIL, next_song(&result, FALSE)); return result; }