commit 424faeca4e0e1455975e90fc6f003d527a93a8fd Author: mhiretskiy Date: Wed May 20 06:27:16 2009 +0000 Модуль calcmenu.c32 для syslinux. git-svn-id: http://svn.calculate.ru/calcboot/trunk@1433 c91db197-33c1-4113-bf15-f8a5c547ca64 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/LICENSE @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..31dae54 --- /dev/null +++ b/Makefile @@ -0,0 +1,56 @@ +## ----------------------------------------------------------------------- +## +## Copyright 2001-2008 H. Peter Anvin - All Rights Reserved +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +## Boston MA 02110-1301, USA; either version 2 of the License, or +## (at your option) any later version; incorporated herein by reference. +## +## ----------------------------------------------------------------------- + +## +## Simple menu system +## + +topdir = ../.. +include ../MCONFIG + +LIBS = ../libutil/libutil_com.a ../lib/libcom32.a $(LIBGCC) +LNXLIBS = ../libutil/libutil_lnx.a + +MODULES = menu.c32 vesamenu.c32 calcmenu.c32 +TESTFILES = + +COMMONOBJS = passwd.o drain.o printmsg.o colors.o \ + background.o refstr.o execute.o + +CALCMENU = calcmenumain.o calcreadconfig.o + +COMMONMENU = menumain.o readconfig.o + +all: $(MODULES) $(TESTFILES) + +menu.elf : menu.o $(COMMONOBJS) $(COMMONMENU) $(LIBS) $(C_LIBS) + $(LD) $(LDFLAGS) -o $@ $^ + +calcmenu.elf : calcmenu.o $(COMMONOBJS) $(CALCMENU) $(LIBS) $(C_LIBS) + $(LD) $(LDFLAGS) -o $@ $^ + +vesamenu.elf : vesamenu.o $(COMMONOBJS) $(COMMONMENU) $(LIBS) $(C_LIBS) + $(LD) $(LDFLAGS) -o $@ $^ + +tidy dist: + rm -f *.o *.lo *.a *.lst *.elf .*.d *.tmp + +clean: tidy + rm -f *.lnx + +spotless: clean + rm -f *.lss *.c32 *.com + rm -f *~ \#* + +install: + +-include .*.d diff --git a/calcmenu.c b/calcmenu.c new file mode 100644 index 0000000..41de7c8 --- /dev/null +++ b/calcmenu.c @@ -0,0 +1,53 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * vesamenu.c + * + * Simple menu system which displays a list and allows the user to select + * a command line and/or edit it. + * + * VESA graphics version. + */ + +#include +#include +#include + +#include "calcmenu.h" + +void console_prepare(void) +{ + fputs("\033[0m\033[25l", stdout); +} + +void console_cleanup(void) +{ + /* For the serial console, be nice and clean up */ + fputs("\033[0m", stdout); +} + +int draw_background(const char *what) +{ + if (!what) + return vesacon_default_background(); + else if (what[0] == '#') + return vesacon_set_background(parse_argb((char **)&what)); + else + return vesacon_load_background(what); +} + +int main(int argc, char *argv[]) +{ + openconsole(&dev_rawcon_r, &dev_vesaserial_w); + return menu_main(argc, argv); +} diff --git a/calcmenu.h b/calcmenu.h new file mode 100644 index 0000000..970f6b0 --- /dev/null +++ b/calcmenu.h @@ -0,0 +1,240 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * menu.h + * + * Header file for the simple menu system + */ + +#ifndef CALCMENU_H +#define CALCMENU_H + +#include +#include +#include +#include +#include +#include +#include +#include "refstr.h" + +#ifndef CLK_TCK +# define CLK_TCK sysconf(_SC_CLK_TCK) +#endif + +struct menu; + +/* Note: the _UNRES variants must always be immediately after their + "normal" versions. */ +enum menu_action { + MA_NONE, /* Undefined value */ + MA_CMD, /* Execute a command */ + MA_DISABLED, /* Disabled menu entry */ + MA_SUBMENU, /* This is a submenu entry */ + MA_GOTO, /* Go to another menu */ + MA_GOTO_UNRES, /* Unresolved go to */ + MA_QUIT, /* Quit to CLI */ + MA_EXIT, /* Exit to higher-level menu */ + MA_EXIT_UNRES, /* Unresolved exit */ +}; + +struct menu_entry { + struct menu *menu; /* Parent menu */ + const char *displayname; + const char *label; + const char *passwd; + char *helptext; + const char *cmdline; + struct menu *submenu; + struct menu_entry *next; /* Linked list of all labels across menus */ + int entry; /* Entry number inside menu */ + enum menu_action action; + unsigned char hotkey; + bool save; /* Save this entry if selected */ +}; + +static inline bool is_disabled(struct menu_entry *me) +{ + return me->action == MA_DISABLED; +} + +enum kernel_type { + /* Meta-types for internal use */ + KT_NONE, + KT_LOCALBOOT, + + /* The ones we can pass off to SYSLINUX, in order */ + KT_KERNEL, /* Undefined type */ + KT_LINUX, /* Linux kernel */ + KT_BOOT, /* Bootstrap program */ + KT_BSS, /* Boot sector with patch */ + KT_PXE, /* PXE NBP */ + KT_FDIMAGE, /* Floppy disk image */ + KT_COMBOOT, /* COMBOOT image */ + KT_COM32, /* COM32 image */ + KT_CONFIG, /* Configuration file */ +}; + +extern const char * const kernel_types[]; + +/* Configurable integer parameters */ +enum parameter_number { + P_WIDTH, + P_MARGIN, + P_PASSWD_MARGIN, + P_MENU_ROWS, + P_TABMSG_ROW, + P_CMDLINE_ROW, + P_END_ROW, + P_PASSWD_ROW, + P_TIMEOUT_ROW, + P_HELPMSG_ROW, + P_HELPMSGEND_ROW, + P_HSHIFT, + P_VSHIFT, + P_HIDDEN_ROW, + + NPARAMS +}; + +/* Configurable messages */ +enum message_number { + MSG_TITLE, + MSG_AUTOBOOT, + MSG_TAB, + MSG_NOTAB, + MSG_PASSPROMPT, + + MSG_COUNT +}; + +struct messages { + const char *name; /* Message configuration name */ + const char *defmsg; /* Default message text */ +}; + +struct menu_parameter { + const char *name; + int value; +}; + +extern const struct menu_parameter mparm[NPARAMS]; + +struct fkey_help { + const char *textname; + const char *background; +}; + +/* 2048 is the current definition inside syslinux */ +#define MAX_CMDLINE_LEN 2048 + +struct menu { + struct menu *next; /* Linked list of all menus */ + const char *label; /* Goto label for this menu */ + struct menu *parent; + struct menu_entry *parent_entry; /* Entry for self in parent */ + + struct menu_entry **menu_entries; + struct menu_entry *menu_hotkeys[256]; + + const char *messages[MSG_COUNT]; + int mparm[NPARAMS]; + + int nentries; + int nentries_space; + int defentry; + int timeout; + + bool allowedit; + bool save; /* MENU SAVE default for this menu */ + + int curentry; + int curtop; + + const char *title; + const char *ontimeout; + const char *onerror; + const char *menu_master_passwd; + const char *menu_background; + + struct color_table *color_table; + + struct fkey_help fkeyhelp[12]; +}; + +/* calcmenumain.c */ +extern char tail_cmdline[MAX_CMDLINE_LEN]; +extern char defaultparam[50]; +extern int default_menu; +extern char startconfig[256]; +/* calcreadconfig.c */ +extern int splitstr(char *s,char ch, char **params,int maxparam); + + +extern struct menu *root_menu, *start_menu, *hide_menu, *menu_list; + +/* These are global parameters regardless of which menu we're displaying */ +extern int shiftkey; +extern int hiddenmenu; +extern long long totaltimeout; + +void parse_configs(char **argv); +int draw_background(const char *filename); + +static inline int my_isspace(char c) +{ + return (unsigned char)c <= ' '; +} + +int my_isxdigit(char c); +unsigned int hexval(char c); +unsigned int hexval2(const char *p); +uint32_t parse_argb(char **p); + +int menu_main(int argc, char *argv[]); +void console_prepare(void); +void console_cleanup(void); + +extern const int message_base_color, menu_color_table_size; +int mygetkey(clock_t timeout); +int show_message_file(const char *filename, const char *background); + +/* passwd.c */ +int passwd_compare(const char *passwd, const char *entry); + +/* colors.c */ +#define MSG_COLORS_DEF_FG 0x90ffffff +#define MSG_COLORS_DEF_BG 0x80ffffff +#define MSG_COLORS_DEF_SHADOW SHADOW_NORMAL +void set_msg_colors_global(struct color_table *tbl, + unsigned int fg, unsigned int bg, + enum color_table_shadow shadow); +struct color_table *default_color_table(void); +struct color_table *copy_color_table(const struct color_table *master); +extern const int message_base_color; + +/* background.c */ +extern const char *current_background; +void set_background(const char *new_background); + +/* execute.c */ +void execute(const char *cmdline, enum kernel_type type); + +/* drain.c */ +void drain_keyboard(void); + +/* calcmenumain.c must del in release */ +extern const char * +edit_cmdline(const char *input, int top); + +#endif /* CALCMENU_H */ diff --git a/calcmenumain.c b/calcmenumain.c new file mode 100644 index 0000000..8362810 --- /dev/null +++ b/calcmenumain.c @@ -0,0 +1,1186 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * menumain.c + * + * Simple menu system which displays a list and allows the user to select + * a command line and/or edit it. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "calcmenu.h" + +/* The symbol "cm" always refers to the current menu across this file... */ +static struct menu *cm; + +const struct menu_parameter mparm[NPARAMS] = { + [P_WIDTH] = { "width", 0 }, + [P_MARGIN] = { "margin", 10 }, + [P_PASSWD_MARGIN] = { "passwordmargin", 3 }, + [P_MENU_ROWS] = { "rows", 12 }, + [P_TABMSG_ROW] = { "tabmsgrow", 18 }, + [P_CMDLINE_ROW] = { "cmdlinerow", 18 }, + [P_END_ROW] = { "endrow", -1 }, + [P_PASSWD_ROW] = { "passwordrow", 11 }, + [P_TIMEOUT_ROW] = { "timeoutrow", 20 }, + [P_HELPMSG_ROW] = { "helpmsgrow", 22 }, + [P_HELPMSGEND_ROW] = { "helpmsgendrow", -1 }, + [P_HSHIFT] = { "hshift", 0 }, + [P_VSHIFT] = { "vshift", 0 }, + [P_HIDDEN_ROW] = { "hiddenrow", -2 }, +}; + +/* These macros assume "cm" is a pointer to the current menu */ +#define WIDTH (cm->mparm[P_WIDTH]) +#define MARGIN (cm->mparm[P_MARGIN]) +#define PASSWD_MARGIN (cm->mparm[P_PASSWD_MARGIN]) +#define MENU_ROWS (cm->mparm[P_MENU_ROWS]) +#define TABMSG_ROW (cm->mparm[P_TABMSG_ROW]+VSHIFT) +#define CMDLINE_ROW (cm->mparm[P_CMDLINE_ROW]+VSHIFT) +#define END_ROW (cm->mparm[P_END_ROW]) +#define PASSWD_ROW (cm->mparm[P_PASSWD_ROW]+VSHIFT) +#define TIMEOUT_ROW (cm->mparm[P_TIMEOUT_ROW]+VSHIFT) +#define HELPMSG_ROW (cm->mparm[P_HELPMSG_ROW]+VSHIFT) +#define HELPMSGEND_ROW (cm->mparm[P_HELPMSGEND_ROW]) +#define HSHIFT (cm->mparm[P_HSHIFT]) +#define VSHIFT (cm->mparm[P_VSHIFT]) +#define HIDDEN_ROW (cm->mparm[P_HIDDEN_ROW]) + +/* transfering buffer */ +char tail_cmdline[MAX_CMDLINE_LEN]; +/* number of parameter in defaultparam */ +int default_menu; +/* parameter for default label menu */ +char defaultparam[50]; +/* buffer for concatinating cmdline and tail_cmdline */ +char newcmdline[MAX_CMDLINE_LEN]; +/* primary config name */ +char startconfig[256]; + +static char * +pad_line(const char *text, int align, int width) +{ + static char buffer[MAX_CMDLINE_LEN]; + int n, p; + + if ( width >= (int) sizeof buffer ) + return NULL; /* Can't do it */ + + n = strlen(text); + if ( n >= width ) + n = width; + + memset(buffer, ' ', width); + buffer[width] = 0; + p = ((width-n)*align)>>1; + memcpy(buffer+p, text, n); + + return buffer; +} + +/* Display an entry, with possible hotkey highlight. Assumes + that the current attribute is the non-hotkey one, and will + guarantee that as an exit condition as well. */ +static void +display_entry(const struct menu_entry *entry, const char *attrib, + const char *hotattrib, int width) +{ + const char *p = entry->displayname; + char marker; + + if (!p) + p = ""; + + switch (entry->action) { + case MA_SUBMENU: + marker = '>'; + break; + case MA_EXIT: + marker = '<'; + break; + default: + marker = 0; + break; + } + + if (marker) + width -= 2; + + while ( width ) { + if ( *p ) { + if ( *p == '^' ) { + p++; + if ( *p && ((unsigned char)*p & ~0x20) == entry->hotkey ) { + fputs(hotattrib, stdout); + putchar(*p++); + fputs(attrib, stdout); + width--; + } + } else { + putchar(*p++); + width--; + } + } else { + putchar(' '); + width--; + } + } + + if (marker) { + putchar(' '); + putchar(marker); + } +} + +static void +draw_row(int y, int sel, int top, int sbtop, int sbbot) +{ + int i = (y-4-VSHIFT)+top; + int dis = (i < cm->nentries) && is_disabled(cm->menu_entries[i]); + + printf("\033[%d;%dH\1#1\016x\017%s ", + y, MARGIN+1+HSHIFT, + (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3"); + + if ( i >= cm->nentries ) { + fputs(pad_line("", 0, WIDTH-2*MARGIN-4), stdout); + } else { + display_entry(cm->menu_entries[i], + (i == sel) ? "\1#5" : dis ? "\2#17" : "\1#3", + (i == sel) ? "\1#6" : dis ? "\2#17" : "\1#4", + WIDTH-2*MARGIN-4); + } + + if ( cm->nentries <= MENU_ROWS ) { + printf(" \1#1\016x\017"); + } else if ( sbtop > 0 ) { + if ( y >= sbtop && y <= sbbot ) + printf(" \1#7\016a\017"); + else + printf(" \1#1\016x\017"); + } else { + putchar(' '); /* Don't modify the scrollbar */ + } +} + +static jmp_buf timeout_jump; + +int mygetkey(clock_t timeout) +{ + clock_t t0, t; + clock_t tto, to; + int key; + + if ( !totaltimeout ) + return get_key(stdin, timeout); + + for (;;) { + tto = min(totaltimeout, INT_MAX); + to = timeout ? min(tto, timeout) : tto; + + t0 = times(NULL); + key = get_key(stdin, to); + t = times(NULL) - t0; + + if ( totaltimeout <= t ) + longjmp(timeout_jump, 1); + + totaltimeout -= t; + + if ( key != KEY_NONE ) + return key; + + if ( timeout ) { + if ( timeout <= t ) + return KEY_NONE; + + timeout -= t; + } + } +} + +static int +ask_passwd(const char *menu_entry) +{ + char user_passwd[WIDTH], *p; + int done; + int key; + int x; + int rv; + + printf("\033[%d;%dH\2#11\016l", PASSWD_ROW, PASSWD_MARGIN+1); + for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ ) + putchar('q'); + + printf("k\033[%d;%dHx", PASSWD_ROW+1, PASSWD_MARGIN+1); + for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ ) + putchar(' '); + + printf("x\033[%d;%dHm", PASSWD_ROW+2, PASSWD_MARGIN+1); + for ( x = 2 ; x <= WIDTH-2*PASSWD_MARGIN-1 ; x++ ) + putchar('q'); + + printf("j\017\033[%d;%dH\2#12 %s \033[%d;%dH\2#13", + PASSWD_ROW, (WIDTH-(strlen(cm->messages[MSG_PASSPROMPT])+2))/2, + cm->messages[MSG_PASSPROMPT], PASSWD_ROW+1, PASSWD_MARGIN+3); + + drain_keyboard(); + + /* Actually allow user to type a password, then compare to the SHA1 */ + done = 0; + p = user_passwd; + + while ( !done ) { + key = mygetkey(0); + + switch ( key ) { + case KEY_ENTER: + case KEY_CTRL('J'): + done = 1; + break; + + case KEY_ESC: + case KEY_CTRL('C'): + p = user_passwd; /* No password entered */ + done = 1; + break; + + case KEY_BACKSPACE: + case KEY_DEL: + case KEY_DELETE: + if ( p > user_passwd ) { + printf("\b \b"); + p--; + } + break; + + case KEY_CTRL('U'): + while ( p > user_passwd ) { + printf("\b \b"); + p--; + } + break; + + default: + if ( key >= ' ' && key <= 0xFF && + (p-user_passwd) < WIDTH-2*PASSWD_MARGIN-5 ) { + *p++ = key; + putchar('*'); + } + break; + } + } + + if ( p == user_passwd ) + return 0; /* No password entered */ + + *p = '\0'; + + rv = (cm->menu_master_passwd && + passwd_compare(cm->menu_master_passwd, user_passwd)) + || (menu_entry && passwd_compare(menu_entry, user_passwd)); + + /* Clean up */ + memset(user_passwd, 0, WIDTH); + drain_keyboard(); + + return rv; +} + + +static void +draw_menu(int sel, int top, int edit_line) +{ + int x, y; + int sbtop = 0, sbbot = 0; + const char *tabmsg; + int tabmsg_len; + + if ( cm->nentries > MENU_ROWS ) { + int sblen = max(MENU_ROWS*MENU_ROWS/cm->nentries, 1); + sbtop = (MENU_ROWS-sblen+1)*top/(cm->nentries-MENU_ROWS+1); + sbbot = sbtop+sblen-1; + sbtop += 4; sbbot += 4; /* Starting row of scrollbar */ + } + + printf("\033[%d;%dH\1#1\016l", VSHIFT+1, HSHIFT+MARGIN+1); + for ( x = 2+HSHIFT ; x <= (WIDTH-2*MARGIN-1)+HSHIFT ; x++ ) + putchar('q'); + + printf("k\033[%d;%dH\1#1x\017\1#2 %s \1#1\016x", + VSHIFT+2, + HSHIFT+MARGIN+1, + pad_line(cm->title, 1, WIDTH-2*MARGIN-4)); + + printf("\033[%d;%dH\1#1t", VSHIFT+3, HSHIFT+MARGIN+1); + for ( x = 2+HSHIFT ; x <= (WIDTH-2*MARGIN-1)+HSHIFT ; x++ ) + putchar('q'); + fputs("u\017", stdout); + + for ( y = 4+VSHIFT ; y < 4+VSHIFT+MENU_ROWS ; y++ ) + draw_row(y, sel, top, sbtop, sbbot); + + printf("\033[%d;%dH\1#1\016m", y, HSHIFT+MARGIN+1); + for ( x = 2+HSHIFT ; x <= (WIDTH-2*MARGIN-1)+HSHIFT ; x++ ) + putchar('q'); + fputs("j\017", stdout); + + if ( edit_line && cm->allowedit && !cm->menu_master_passwd ) + tabmsg = cm->messages[MSG_TAB]; + else + tabmsg = cm->messages[MSG_NOTAB]; + + tabmsg_len = strlen(tabmsg); + + printf("\1#8\033[%d;%dH%s", + TABMSG_ROW, 1+HSHIFT+((WIDTH-tabmsg_len)>>1), tabmsg); + printf("\1#0\033[%d;1H", END_ROW); +} + +static void +clear_screen(void) +{ + fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout); +} + +static void +display_help(const char *text) +{ + int row; + const char *p; + + if (!text) { + text = ""; + printf("\1#0\033[%d;1H", HELPMSG_ROW); + } else { + printf("\2#16\033[%d;1H", HELPMSG_ROW); + } + + for (p = text, row = HELPMSG_ROW; *p && row <= HELPMSGEND_ROW; p++) { + switch (*p) { + case '\r': + case '\f': + case '\v': + case '\033': + break; + case '\n': + printf("\033[K\033[%d;1H", ++row); + break; + default: + putchar(*p); + } + } + + fputs("\033[K", stdout); + + while (row <= HELPMSGEND_ROW) { + printf("\033[K\033[%d;1H", ++row); + } +} + +static int show_fkey(int key) +{ + int fkey; + + while (1) { + switch (key) { + case KEY_F1: fkey = 0; break; + case KEY_F2: fkey = 1; break; + case KEY_F3: fkey = 2; break; + case KEY_F4: fkey = 3; break; + case KEY_F5: fkey = 4; break; + case KEY_F6: fkey = 5; break; + case KEY_F7: fkey = 6; break; + case KEY_F8: fkey = 7; break; + case KEY_F9: fkey = 8; break; + case KEY_F10: fkey = 9; break; + case KEY_F11: fkey = 10; break; + case KEY_F12: fkey = 11; break; + default: fkey = -1; break; + } + + if (fkey == -1) + return -2; + + if( cm->fkeyhelp[fkey].textname && cm->fkeyhelp[fkey].background ) { + int slent = strlen(cm->fkeyhelp[fkey].textname); + if (slent>4 && + !strcmp(cm->fkeyhelp[fkey].textname+slent-4,".c32")) { + return fkey; + } + } + + if (cm->fkeyhelp[fkey].textname) { + key = show_message_file(cm->fkeyhelp[fkey].textname, + cm->fkeyhelp[fkey].background); + return -1; + } + else + return -2; + } +} + +const char * +edit_cmdline(const char *input, int top) +{ + static char cmdline[MAX_CMDLINE_LEN]; + int key, len, prev_len, cursor; + int redraw = 1; /* We enter with the menu already drawn */ + + strncpy(cmdline, input, MAX_CMDLINE_LEN); + cmdline[MAX_CMDLINE_LEN-1] = '\0'; + + len = cursor = strlen(cmdline); + prev_len = 0; + + for (;;) { + if ( redraw > 1 ) { + /* Clear and redraw whole screen */ + /* Enable ASCII on G0 and DEC VT on G1; do it in this order + to avoid confusing the Linux console */ + clear_screen(); + draw_menu(-1, top, 1); + prev_len = 0; + } + + if ( redraw > 0 ) { + /* Redraw the command line */ + printf("\033[?25l\033[%d;1H\1#9> \2#10%s", + CMDLINE_ROW, pad_line(cmdline, 0, max(len, prev_len))); + printf("\2#10\033[%d;3H%s\033[?25h", + CMDLINE_ROW, pad_line(cmdline, 0, cursor)); + prev_len = len; + redraw = 0; + } + + key = mygetkey(0); + + switch( key ) { + case KEY_CTRL('L'): + redraw = 2; + break; + + case KEY_ENTER: + case KEY_CTRL('J'): + return cmdline; + + case KEY_ESC: + case KEY_CTRL('C'): + return NULL; + + case KEY_BACKSPACE: + case KEY_DEL: + if ( cursor ) { + memmove(cmdline+cursor-1, cmdline+cursor, len-cursor+1); + len--; + cursor--; + redraw = 1; + } + break; + + case KEY_CTRL('D'): + case KEY_DELETE: + if ( cursor < len ) { + memmove(cmdline+cursor, cmdline+cursor+1, len-cursor); + len--; + redraw = 1; + } + break; + + case KEY_CTRL('U'): + if ( len ) { + len = cursor = 0; + cmdline[len] = '\0'; + redraw = 1; + } + break; + + case KEY_CTRL('W'): + if ( cursor ) { + int prevcursor = cursor; + + while ( cursor && my_isspace(cmdline[cursor-1]) ) + cursor--; + + while ( cursor && !my_isspace(cmdline[cursor-1]) ) + cursor--; + + memmove(cmdline+cursor, cmdline+prevcursor, len-prevcursor+1); + len -= (cursor-prevcursor); + redraw = 1; + } + break; + + case KEY_LEFT: + case KEY_CTRL('B'): + if ( cursor ) { + cursor--; + redraw = 1; + } + break; + + case KEY_RIGHT: + case KEY_CTRL('F'): + if ( cursor < len ) { + putchar(cmdline[cursor++]); + } + break; + + case KEY_CTRL('K'): + if ( cursor < len ) { + cmdline[len = cursor] = '\0'; + redraw = 1; + } + break; + + case KEY_HOME: + case KEY_CTRL('A'): + if ( cursor ) { + cursor = 0; + redraw = 1; + } + break; + + case KEY_END: + case KEY_CTRL('E'): + if ( cursor != len ) { + cursor = len; + redraw = 1; + } + break; + + case KEY_F1: + case KEY_F2: + case KEY_F3: + case KEY_F4: + case KEY_F5: + case KEY_F6: + case KEY_F7: + case KEY_F8: + case KEY_F9: + case KEY_F10: + case KEY_F11: + case KEY_F12: + show_fkey(key); + redraw = 1; + break; + + default: + if ( key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN-1 ) { + if ( cursor == len ) { + cmdline[len] = key; + cmdline[++len] = '\0'; + cursor++; + putchar(key); + prev_len++; + } else { + memmove(cmdline+cursor+1, cmdline+cursor, len-cursor+1); + cmdline[cursor++] = key; + len++; + redraw = 1; + } + } + break; + } + } +} + +static inline int +shift_is_held(void) +{ + uint8_t shift_bits = *(uint8_t *)0x417; + + return !!(shift_bits & 0x5d); /* Caps/Scroll/Alt/Shift */ +} + +static void +print_timeout_message(int tol, int row, const char *msg) +{ + char buf[256]; + int nc = 0, nnc; + const char *tp = msg; + char tc; + char *tq = buf; + + while ((size_t)(tq-buf) < (sizeof buf-16) && (tc = *tp)) { + tp++; + if (tc == '#') { + nnc = sprintf(tq, "\2#15%d\2#14", tol); + tq += nnc; + nc += nnc-8; /* 8 formatting characters */ + } else if (tc == '{') { + /* Deal with {singular[,dual],plural} constructs */ + struct { + const char *s, *e; + } tx[3]; + const char *tpp; + int n = 0; + + memset(tx, 0, sizeof tx); + + tx[0].s = tp; + + while (*tp && *tp != '}') { + if (*tp == ',' && n < 2) { + tx[n].e = tp; + n++; + tx[n].s = tp+1; + } + tp++; + } + tx[n].e = tp; + + if (*tp) + tp++; /* Skip final bracket */ + + if (!tx[1].s) + tx[1] = tx[0]; + if (!tx[2].s) + tx[2] = tx[1]; + + /* Now [0] is singular, [1] is dual, and [2] is plural, + even if the user only specified some of them. */ + + switch (tol) { + case 1: n = 0; break; + case 2: n = 1; break; + default: n = 2; break; + } + + for (tpp = tx[n].s; tpp < tx[n].e; tpp++) { + if ((size_t)(tq-buf) < (sizeof buf)) { + *tq++ = *tpp; + nc++; + } + } + } else { + *tq++ = tc; + nc++; + } + } + *tq = '\0'; + + /* Let's hope 4 spaces on each side is enough... */ + printf("\033[%d;%dH\2#14 %s ", row, HSHIFT+1+((WIDTH-nc-8)>>1), buf); +} + +/* Set the background screen, etc. */ +static void +prepare_screen_for_menu(void) +{ + console_color_table = cm->color_table; + console_color_table_size = menu_color_table_size; + set_background(cm->menu_background); +} + +static const char * +do_hidden_menu(void) +{ + int key; + int timeout_left, this_timeout; + + clear_screen(); + + if ( !setjmp(timeout_jump) ) { + timeout_left = cm->timeout; + + while (!cm->timeout || timeout_left) { + int tol = timeout_left/CLK_TCK; + + print_timeout_message(tol, HIDDEN_ROW, cm->messages[MSG_AUTOBOOT]); + + this_timeout = min(timeout_left, CLK_TCK); + key = mygetkey(this_timeout); + + if (key != KEY_NONE) + return NULL; /* Key pressed */ + + timeout_left -= this_timeout; + } + } + + return cm->menu_entries[cm->defentry]->cmdline; /* Default entry */ +} + +static const char * +run_menu(void) +{ + int key; + int done = 0; + volatile int entry = cm->curentry; + int prev_entry = -1; + volatile int top = cm->curtop; + int prev_top = -1; + int clear = 1, to_clear; + const char *cmdline = NULL; + volatile clock_t key_timeout, timeout_left, this_timeout; + const struct menu_entry *me; + + /* Note: for both key_timeout and timeout == 0 means no limit */ + timeout_left = key_timeout = cm->timeout; + + /* If we're in shiftkey mode, exit immediately unless a shift key + is pressed */ + + if ( shiftkey && !shift_is_held() ) { + return cm->menu_entries[cm->defentry]->cmdline; + } else { + shiftkey = 0; + } + + /* Do this before hiddenmenu handling, so we show the background */ + prepare_screen_for_menu(); + + /* Handle hiddenmenu */ + if ( hiddenmenu ) { + cmdline = do_hidden_menu(); + if (cmdline) + return cmdline; + + /* Otherwise display the menu now; the timeout has already been + cancelled, since the user pressed a key. */ + hiddenmenu = 0; + key_timeout = 0; + } + + /* Handle both local and global timeout */ + if ( setjmp(timeout_jump) ) { + entry = cm->defentry; + + if ( top < 0 || top < entry-MENU_ROWS+1 ) + top = max(0, entry-MENU_ROWS+1); + else if ( top > entry || top > max(0, cm->nentries-MENU_ROWS) ) + top = min(entry, max(0, cm->nentries-MENU_ROWS)); + + draw_menu(cm->ontimeout ? -1 : entry, top, 1); + cmdline = cm->ontimeout ? cm->ontimeout : cm->menu_entries[entry]->cmdline; + done = 1; + } + + char *p = strstr(tail_cmdline,defaultparam); + if(p) { + /* if it is param - not inner part str */ + if( p == tail_cmdline || *(p-1) == ' ' ) { + p += strlen(defaultparam)+1; + char *params1[256]; char str1[512]; + strcpy(str1,p); + /* Truncates the end of the string containing the rest + of the parameters */ + if(strchr(str1,' ')) + *strchr(str1,' ')=0; + /* spliting str1 to params */ + int n = splitstr(str1,',',params1,255); + if( default_menu < n ) { + for(int i=0;i< cm->nentries;i++) + { + if(!strcmp(cm->menu_entries[i]->label,params1[default_menu])) + { + entry = i; + break; + } + } + } + } + } + + while ( !done ) { + if (entry <= 0) { + entry = 0; + while (entry < cm->nentries && is_disabled(cm->menu_entries[entry])) + entry++; + } + if (entry >= cm->nentries) { + entry = cm->nentries-1; + while (entry > 0 && is_disabled(cm->menu_entries[entry])) + entry--; + } + + me = cm->menu_entries[entry]; + + if ( top < 0 || top < entry-MENU_ROWS+1 ) + top = max(0, entry-MENU_ROWS+1); + else if ( top > entry || top > max(0, cm->nentries-MENU_ROWS) ) + top = min(entry, max(0, cm->nentries-MENU_ROWS)); + + /* Start with a clear screen */ + if ( clear ) { + /* Clear and redraw whole screen */ + /* Enable ASCII on G0 and DEC VT on G1; do it in this order + to avoid confusing the Linux console */ + if (clear >= 2) + prepare_screen_for_menu(); + printf("\033[?25l"); /* Hide cursor */ + clear_screen(); /* Disable flashing screen */ + clear = 0; + prev_entry = prev_top = -1; + } + + if ( top != prev_top ) { + draw_menu(entry, top, 1); + display_help(me->helptext); + } else if ( entry != prev_entry ) { + draw_row(prev_entry-top+4+VSHIFT, entry, top, 0, 0); + draw_row(entry-top+4+VSHIFT, entry, top, 0, 0); + display_help(me->helptext); + } + + prev_entry = entry; prev_top = top; + cm->curentry = entry; + cm->curtop = top; + + /* Cursor movement cancels timeout */ + if ( entry != cm->defentry ) + key_timeout = 0; + + if ( key_timeout ) { + int tol = timeout_left/CLK_TCK; + print_timeout_message(tol, TIMEOUT_ROW, cm->messages[MSG_AUTOBOOT]); + to_clear = 1; + } else { + to_clear = 0; + } + + this_timeout = min(min(key_timeout, timeout_left), (clock_t)CLK_TCK); + key = mygetkey(this_timeout); + + if ( key != KEY_NONE ) { + timeout_left = key_timeout; + if ( to_clear ) + printf("\033[%d;1H\1#0\033[K", TIMEOUT_ROW); + } + + switch ( key ) { + case KEY_NONE: /* Timeout */ + /* This is somewhat hacky, but this at least lets the user + know what's going on, and still deals with "phantom inputs" + e.g. on serial ports. + + Warning: a timeout will boot the default entry without any + password! */ + if ( key_timeout ) { + if ( timeout_left <= this_timeout ) + longjmp(timeout_jump, 1); + + timeout_left -= this_timeout; + } + break; + + case KEY_CTRL('L'): + clear = 1; + break; + + case KEY_ENTER: + case KEY_CTRL('J'): + key_timeout = 0; /* Cancels timeout */ + if ( me->passwd ) { + clear = 1; + done = ask_passwd(me->passwd); + } else { + done = 1; + } + cmdline = NULL; + if (done) { + switch (me->action) { + case MA_CMD: + cmdline = me->cmdline; + break; + case MA_SUBMENU: + case MA_GOTO: + case MA_EXIT: + done = 0; + clear = 2; + cm = me->submenu; + entry = cm->curentry; + top = cm->curtop; + break; + case MA_QUIT: + /* Quit menu system */ + done = 1; + clear = 1; + draw_row(entry-top+4+VSHIFT, -1, top, 0, 0); + break; + default: + done = 0; + break; + } + } + if (done && !me->passwd) { + /* Only save a new default if we don't have a password... */ + if (me->save && me->label) { + syslinux_setadv(ADV_MENUSAVE, strlen(me->label), me->label); + syslinux_adv_write(); + } + } + break; + + case KEY_UP: + case KEY_CTRL('P'): + while (entry > 0) { + entry--; + if (entry < top) + top -= MENU_ROWS; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case KEY_DOWN: + case KEY_CTRL('N'): + while (entry < cm->nentries-1) { + entry++; + if (entry >= top+MENU_ROWS) + top += MENU_ROWS; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case KEY_PGUP: + case KEY_LEFT: + case KEY_CTRL('B'): + case '<': + entry -= MENU_ROWS; + top -= MENU_ROWS; + while (entry > 0 && is_disabled(cm->menu_entries[entry])) { + entry--; + if (entry < top) + top -= MENU_ROWS; + } + break; + + case KEY_PGDN: + case KEY_RIGHT: + case KEY_CTRL('F'): + case '>': + case ' ': + entry += MENU_ROWS; + top += MENU_ROWS; + while (entry < cm->nentries-1 && is_disabled(cm->menu_entries[entry])) { + entry++; + if (entry >= top+MENU_ROWS) + top += MENU_ROWS; + } + break; + + case '-': + while (entry > 0) { + entry--; + top--; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case '+': + while (entry < cm->nentries-1) { + entry++; + top++; + if (!is_disabled(cm->menu_entries[entry])) + break; + } + break; + + case KEY_CTRL('A'): + case KEY_HOME: + top = entry = 0; + break; + + case KEY_CTRL('E'): + case KEY_END: + entry = cm->nentries - 1; + top = max(0, cm->nentries-MENU_ROWS); + break; + + case KEY_F1: + case KEY_F2: + case KEY_F3: + case KEY_F4: + case KEY_F5: + case KEY_F6: + case KEY_F7: + case KEY_F8: + case KEY_F9: + case KEY_F10: + case KEY_F11: + case KEY_F12: { + int res = show_fkey(key); + /* Adds ability to initizalize other menu on func keys */ + if( res >= 0 ) { + strncpy(newcmdline,cm->fkeyhelp[res].textname,512); + strncat(newcmdline," ",512); + strncat(newcmdline,cm->fkeyhelp[res].background,512); + return newcmdline; + } + if(res == -1 ) { + clear = 1; + } + } + break; + case KEY_TAB: + if ( cm->allowedit && me->action == MA_CMD ) { + int ok = 1; + + key_timeout = 0; /* Cancels timeout */ + draw_row(entry-top+4+VSHIFT, -1, top, 0, 0); + + if ( cm->menu_master_passwd ) { + ok = ask_passwd(NULL); + clear_screen(); + draw_menu(-1, top, 0); + } else { + /* Erase [Tab] message and help text*/ + printf("\033[%d;1H\1#0\033[K", TABMSG_ROW); + display_help(NULL); + } + + if ( ok ) { + strncpy(newcmdline, me->cmdline, MAX_CMDLINE_LEN-1); + strncat(newcmdline, tail_cmdline, MAX_CMDLINE_LEN-1); + cmdline = edit_cmdline(newcmdline, top); + done = !!cmdline; + clear = 1; /* In case we hit [Esc] and done is null */ + } else { + draw_row(entry-top+4+VSHIFT, entry, top, 0, 0); + } + } + break; + case KEY_CTRL('C'): /* Ctrl-C */ + case KEY_ESC: /* Esc */ + if ( cm->parent ) { + cm = cm->parent; + clear = 2; + entry = cm->curentry; + top = cm->curtop; + } else { + strcpy(newcmdline,"calcmenu.c32 "); + strncat(newcmdline,startconfig,255); + return newcmdline; + } + break; + default: + if ( key > 0 && key < 0xFF ) { + key &= ~0x20; /* Upper case */ + if ( cm->menu_hotkeys[key] ) { + key_timeout = 0; + entry = cm->menu_hotkeys[key]->entry; + /* Should we commit at this point? */ + } + } + break; + } + } + + printf("\033[?25h"); /* Show cursor */ + + /* Return the label name so localboot and ipappend work */ + return cmdline; +} + +int menu_main(int argc, char *argv[]) +{ + const char *cmdline; + struct menu *m; + int rows, cols; + int i; + *defaultparam = '\0'; + *startconfig = '\0'; + + (void)argc; + + console_prepare(); + + if (getscreensize(1, &rows, &cols)) { + /* Unknown screen size? */ + rows = 24; + cols = 80; + } + + default_menu = -1; + parse_configs(argv+1); + + for(;;) { + /* Some postprocessing for all menus */ + for (m = menu_list; m; m = m->next) { + if (!m->mparm[P_WIDTH]) + m->mparm[P_WIDTH] = cols; + + /* If anyone has specified negative parameters, consider them + relative to the bottom row of the screen. */ + for (i = 0; i < NPARAMS; i++) + if (m->mparm[i] < 0) + m->mparm[i] = max(m->mparm[i]+rows, 0); + } + + if ( !cm->nentries ) { + fputs("Initial menu has no LABEL entries!\n", stdout); + return 1; /* Error! */ + } + + cm = start_menu; + for(;;) { + cmdline = run_menu(); + strncpy(newcmdline,cmdline,MAX_CMDLINE_LEN-1); + if( strlen(newcmdline) < MAX_CMDLINE_LEN-1) + strncat(newcmdline,tail_cmdline,MAX_CMDLINE_LEN-1-strlen(newcmdline)); + cmdline = newcmdline; + + printf("\033[?25h\033[%d;1H\033[0m", END_ROW); + console_cleanup(); + + if ( strstr(cmdline,"calcmenu.c32") ) + { + char *params[100]; + char *p = cmdline; + *p = ' '; + for( int i=0;*p && i<99;p++) { + if( *p == ' ') { + *p = '\0'; + } else if( !*(p-1)) { + params[i] = p; + params[++i] = NULL; + } + } + /* Erase previous content of tail */ + *tail_cmdline = '\0'; + /* Erase previous of default param for menu */ + *defaultparam = '\0'; + default_menu = 0; + parse_configs(params+1); + // обновить меню + break; + } + + if ( cmdline ) { + execute(cmdline, KT_NONE); + if ( cm->onerror ) + execute(cm->onerror, KT_NONE); + } else { + return 0; /* Exit */ + } + + console_prepare(); /* If we're looping... */ + } + } +} diff --git a/calcreadconfig.c b/calcreadconfig.c new file mode 100644 index 0000000..41c72e8 --- /dev/null +++ b/calcreadconfig.c @@ -0,0 +1,1185 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved + * Copyright 2009 Intel Corporation; author: H. Peter Anvin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston MA 02110-1301, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "calcmenu.h" + +/* Empty refstring */ +const char *empty_string; + +/* Root menu, starting menu, hidden menu, and list of all menus */ +struct menu *root_menu, *start_menu, *hide_menu, *menu_list; + +/* These are global parameters regardless of which menu we're displaying */ +int shiftkey = 0; /* Only display menu if shift key pressed */ +int hiddenmenu = 0; +long long totaltimeout = 0; + +/* Keep track of global default */ +static int has_ui = 0; /* DEFAULT only counts if UI is found */ +static const char *globaldefault = NULL; +static bool menusave = false; /* True if there is any "menu save" */ + +/* Linked list of all entires, hidden or not; used by unlabel() */ +static struct menu_entry *all_entries; +static struct menu_entry **all_entries_end = &all_entries; + +static const struct messages messages[MSG_COUNT] = { + [MSG_AUTOBOOT] = { "autoboot", "Automatic boot in # second{,s}..." }, + [MSG_TAB] = { "tabmsg", "Press [Tab] to edit options" }, + [MSG_NOTAB] = { "notabmsg", "" }, + [MSG_PASSPROMPT] = { "passprompt", "Password required" }, +}; + +#define astrdup(x) ({ char *__x = (x); \ + size_t __n = strlen(__x) + 1; \ + char *__p = alloca(__n); \ + if ( __p ) memcpy(__p, __x, __n); \ + __p; }) + +/* Must match enum kernel_type */ +const char * const kernel_types[] = { + "none", + "localboot", + "kernel", + "linux", + "boot", + "bss", + "pxe", + "fdimage", + "comboot", + "com32", + "config", + NULL +}; + +/* + * Search the list of all menus for a specific label + */ +static struct menu * +find_menu(const char *label) +{ + struct menu *m; + + for (m = menu_list; m; m = m->next) { + if (!strcmp(label, m->label)) + return m; + } + + return NULL; +} + +#define MAX_LINE 4096 + +static char * +skipspace(char *p) +{ + while (*p && my_isspace(*p)) + p++; + + return p; +} + +/* Strip ^ from a string, returning a new reference to the same refstring + if none present */ +static const char *strip_caret(const char *str) +{ + const char *p, *r; + char *q; + int carets = 0; + + p = str; + for (;;) { + p = strchr(p, '^'); + if (!p) + break; + carets++; + p++; + } + + if (!carets) + return refstr_get(str); + + r = q = refstr_alloc(strlen(str)-carets); + for (p = str; *p; p++) + if (*p != '^') + *q++ = *p; + + *q = '\0'; /* refstr_alloc() already did this... */ + + return r; +} + +/* Check to see if we are at a certain keyword (case insensitive) */ +/* Returns a pointer to the first character past the keyword */ +static char * +looking_at(char *line, const char *kwd) +{ + char *p = line; + const char *q = kwd; + + while ( *p && *q && ((*p^*q) & ~0x20) == 0 ) { + p++; + q++; + } + + if ( *q ) + return NULL; /* Didn't see the keyword */ + + return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */ +} + +static struct menu * new_menu(struct menu *parent, + struct menu_entry *parent_entry, + const char *label) +{ + struct menu *m = calloc(1, sizeof(struct menu)); + int i; + + m->label = label; + m->title = refstr_get(empty_string); + + if (parent) { + /* Submenu */ + m->parent = parent; + m->parent_entry = parent_entry; + parent_entry->action = MA_SUBMENU; + parent_entry->submenu = m; + + for (i = 0; i < MSG_COUNT; i++) + m->messages[i] = refstr_get(parent->messages[i]); + + memcpy(m->mparm, parent->mparm, sizeof m->mparm); + + m->allowedit = parent->allowedit; + m->timeout = parent->timeout; + m->save = parent->save; + + m->ontimeout = refstr_get(parent->ontimeout); + m->onerror = refstr_get(parent->onerror); + m->menu_master_passwd = refstr_get(parent->menu_master_passwd); + m->menu_background = refstr_get(parent->menu_background); + + m->color_table = copy_color_table(parent->color_table); + + for (i = 0; i < 12; i++) { + m->fkeyhelp[i].textname = refstr_get(parent->fkeyhelp[i].textname); + m->fkeyhelp[i].background = refstr_get(parent->fkeyhelp[i].background); + } + } else { + /* Root menu */ + for (i = 0; i < MSG_COUNT; i++) + m->messages[i] = refstrdup(messages[i].defmsg); + for (i = 0; i < NPARAMS; i++) + m->mparm[i] = mparm[i].value; + + m->allowedit = true; /* Allow edits of the command line */ + m->color_table = default_color_table(); + } + + m->next = menu_list; + menu_list = m; + + return m; +} + +struct labeldata { + const char *label; + const char *kernel; + enum kernel_type type; + const char *append; + const char *initrd; + const char *menulabel; + const char *passwd; + char *helptext; + unsigned int ipappend; + unsigned int menuhide; + unsigned int menudefault; + unsigned int menuseparator; + unsigned int menudisabled; + unsigned int menuindent; + enum menu_action action; + int save; + struct menu *submenu; +}; + +/* Menu currently being parsed */ +static struct menu *current_menu; + +static void +clear_label_data(struct labeldata *ld) +{ + refstr_put(ld->label); + refstr_put(ld->kernel); + refstr_put(ld->append); + refstr_put(ld->initrd); + refstr_put(ld->menulabel); + refstr_put(ld->passwd); + + memset(ld, 0, sizeof *ld); +} + +static struct menu_entry *new_entry(struct menu *m) +{ + struct menu_entry *me; + + if (m->nentries >= m->nentries_space) { + if (!m->nentries_space) + m->nentries_space = 1; + else + m->nentries_space <<= 1; + + m->menu_entries = realloc(m->menu_entries, m->nentries_space* + sizeof(struct menu_entry *)); + } + + me = calloc(1, sizeof(struct menu_entry)); + me->menu = m; + me->entry = m->nentries; + m->menu_entries[m->nentries++] = me; + *all_entries_end = me; + all_entries_end = &me->next; + + return me; +} + +static void consider_for_hotkey(struct menu *m, struct menu_entry *me) +{ + const char *p = strchr(me->displayname, '^'); + + if (me->action != MA_DISABLED) { + if ( p && p[1] ) { + unsigned char hotkey = p[1] & ~0x20; + if ( !m->menu_hotkeys[hotkey] ) { + me->hotkey = hotkey; + m->menu_hotkeys[hotkey] = me; + } + } + } +} + +static void +record(struct menu *m, struct labeldata *ld, const char *append) +{ + int i; + struct menu_entry *me; + const struct syslinux_ipappend_strings *ipappend; + + if (!ld->label) + return; /* Nothing defined */ + + /* Hidden entries are recorded on a special "hidden menu" */ + if (ld->menuhide) + m = hide_menu; + + if ( ld->label ) { + char ipoptions[4096], *ipp; + const char *a; + char *s; + + me = new_entry(m); + + me->displayname = ld->menulabel + ? refstr_get(ld->menulabel) : refstr_get(ld->label); + me->label = refstr_get(ld->label); + me->passwd = refstr_get(ld->passwd); + me->helptext = ld->helptext; + me->hotkey = 0; + me->action = ld->action ? ld->action : MA_CMD; + me->save = ld->save ? (ld->save > 0) : m->save; + + if ( ld->menuindent ) { + const char *dn; + + rsprintf(&dn, "%*s%s", ld->menuindent, "", me->displayname); + refstr_put(me->displayname); + me->displayname = dn; + } + + if ( ld->menuseparator ) { + refstr_put(me->displayname); + me->displayname = refstr_get(empty_string); + } + + if ( ld->menuseparator || ld->menudisabled ) { + me->action = MA_DISABLED; + refstr_put(me->label); + me->label = NULL; + refstr_put(me->passwd); + me->passwd = NULL; + } + + if (ld->menulabel) + consider_for_hotkey(m, me); + + switch (me->action) { + case MA_CMD: + ipp = ipoptions; + *ipp = '\0'; + + if (ld->initrd) + ipp += sprintf(ipp, " initrd=%s", ld->initrd); + + if (ld->ipappend) { + ipappend = syslinux_ipappend_strings(); + for (i = 0; i < ipappend->count; i++) { + if ( (ld->ipappend & (1U << i)) && ipappend->ptr[i] ) + ipp += sprintf(ipp, " %s", ipappend->ptr[i]); + } + } + + a = ld->append; + if ( !a ) + a = append; + if ( !a || (a[0] == '-' && !a[1]) ) + a = ""; + s = a[0] ? " " : ""; + if (ld->type == KT_KERNEL) { + rsprintf(&me->cmdline, "%s%s%s%s", + ld->kernel, s, a, ipoptions); + } else { + rsprintf(&me->cmdline, ".%s %s%s%s%s", + kernel_types[ld->type], ld->kernel, s, a, ipoptions); + } + break; + + case MA_GOTO_UNRES: + case MA_EXIT_UNRES: + me->cmdline = refstr_get(ld->kernel); + break; + + case MA_GOTO: + case MA_EXIT: + me->submenu = ld->submenu; + break; + + default: + break; + } + + if ( ld->menudefault && me->action == MA_CMD ) + m->defentry = m->nentries-1; + } + + clear_label_data(ld); +} + +static struct menu *begin_submenu(const char *tag) +{ + struct menu_entry *me; + + if (!tag[0]) + tag = NULL; + + me = new_entry(current_menu); + me->displayname = refstrdup(tag); + return new_menu(current_menu, me, refstr_get(me->displayname)); +} + +static struct menu *end_submenu(void) +{ + return current_menu->parent ? current_menu->parent : current_menu; +} + +static struct menu_entry *find_label(const char *str) +{ + const char *p; + struct menu_entry *me; + int pos; + + p = str; + while ( *p && !my_isspace(*p) ) + p++; + + /* p now points to the first byte beyond the kernel name */ + pos = p-str; + + for (me = all_entries; me; me = me->next) { + if (!strncmp(str, me->label, pos) && !me->label[pos]) + return me; + } + + return NULL; +} + +static const char *unlabel(const char *str) +{ + /* Convert a CLI-style command line to an executable command line */ + const char *p; + const char *q; + struct menu_entry *me; + int pos; + + p = str; + while ( *p && !my_isspace(*p) ) + p++; + + /* p now points to the first byte beyond the kernel name */ + pos = p-str; + + for (me = all_entries; me; me = me->next) { + if (!strncmp(str, me->label, pos) && !me->label[pos]) { + /* Found matching label */ + rsprintf(&q, "%s%s", me->cmdline, p); + refstr_put(str); + return q; + } + } + + return str; +} + +static const char * +refdup_word(char **p) +{ + char *sp = *p; + char *ep = sp; + + while (*ep && !my_isspace(*ep)) + ep++; + + *p = ep; + return refstrndup(sp, ep-sp); +} + +int my_isxdigit(char c) +{ + unsigned int uc = c; + + return (uc-'0') < 10 || + ((uc|0x20)-'a') < 6; +} + +unsigned int hexval(char c) +{ + unsigned char uc = c | 0x20; + unsigned int v; + + v = uc-'0'; + if (v < 10) + return v; + + return uc-'a'+10; +} + +unsigned int hexval2(const char *p) +{ + return (hexval(p[0]) << 4)+hexval(p[1]); +} + +uint32_t parse_argb(char **p) +{ + char *sp = *p; + char *ep; + uint32_t argb; + size_t len, dl; + + if (*sp == '#') + sp++; + + ep = sp; + + while (my_isxdigit(*ep)) + ep++; + + *p = ep; + len = ep-sp; + + switch(len) { + case 3: /* #rgb */ + argb = + 0xff000000 + + (hexval(sp[0])*0x11 << 16) + + (hexval(sp[1])*0x11 << 8) + + (hexval(sp[2])*0x11); + break; + case 4: /* #argb */ + argb = + (hexval(sp[0])*0x11 << 24) + + (hexval(sp[1])*0x11 << 16) + + (hexval(sp[2])*0x11 << 8) + + (hexval(sp[3])*0x11); + break; + case 6: /* #rrggbb */ + case 9: /* #rrrgggbbb */ + case 12: /* #rrrrggggbbbb */ + dl = len/3; + argb = + 0xff000000 + + (hexval2(sp+0) << 16) + + (hexval2(sp+dl) << 8) + + hexval2(sp+dl*2); + break; + case 8: /* #aarrggbb */ + /* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb, + assume the latter is a more common format */ + case 16: /* #aaaarrrrggggbbbb */ + dl = len/4; + argb = + (hexval2(sp+0) << 24) + + (hexval2(sp+dl) << 16) + + (hexval2(sp+dl*2) << 8) + + hexval2(sp+dl*3); + break; + default: + argb = 0xffff0000; /* Bright red (error indication) */ + break; + } + + return argb; +} + +/* + * Parser state. This is global so that including multiple + * files work as expected, which is that everything works the + * same way as if the files had been concatenated together. + */ +static const char *append = NULL; +static unsigned int ipappend = 0; +static struct labeldata ld; + +static int parse_one_config(const char *filename); + +static char *is_kernel_type(char *cmdstr, enum kernel_type *type) +{ + const char * const *p; + char *q; + enum kernel_type t = KT_NONE; + + for (p = kernel_types; *p; p++, t++) { + if ((q = looking_at(cmdstr, *p))) { + *type = t; + return q; + } + } + + return NULL; +} + +static char *is_message_name(char *cmdstr, enum message_number *msgnr) +{ + char *q; + enum message_number i; + + for (i = 0; i < MSG_COUNT; i++) { + if ((q = looking_at(cmdstr, messages[i].name))) { + *msgnr = i; + return q; + } + } + + return NULL; +} + +static char *is_fkey(char *cmdstr, int *fkeyno) +{ + char *q; + int no; + + if ((cmdstr[0]|0x20) != 'f') + return NULL; + + no = strtoul(cmdstr+1, &q, 10); + if (!my_isspace(*q)) + return NULL; + + if (no < 0 || no > 12) + return NULL; + + *fkeyno = (no == 0) ? 10 : no-1; + return q; +} + +static void parse_config_file(FILE *f) +{ + char line[MAX_LINE], *p, *ep, ch; + enum kernel_type type; + enum message_number msgnr; + int fkeyno; + struct menu *m = current_menu; + + while ( fgets(line, sizeof line, f) ) { + p = strchr(line, '\r'); + if ( p ) + *p = '\0'; + p = strchr(line, '\n'); + if ( p ) + *p = '\0'; + + p = skipspace(line); + + if ( looking_at(p, "menu") ) { + p = skipspace(p+4); + + if ( looking_at(p, "label") ) { + if ( ld.label ) { + refstr_put(ld.menulabel); + ld.menulabel = refstrdup(skipspace(p+5)); + } else if ( m->parent_entry ) { + refstr_put(m->parent_entry->displayname); + m->parent_entry->displayname = refstrdup(skipspace(p+5)); + consider_for_hotkey(m->parent, m->parent_entry); + if (!m->title[0]) { + /* MENU LABEL -> MENU TITLE on submenu */ + refstr_put(m->title); + m->title = strip_caret(m->parent_entry->displayname); + } + } + } else if ( looking_at(p, "title") ) { + refstr_put(m->title); + m->title = refstrdup(skipspace(p+5)); + if (m->parent_entry) { + /* MENU TITLE -> MENU LABEL on submenu */ + if (m->parent_entry->displayname == m->label) { + refstr_put(m->parent_entry->displayname); + m->parent_entry->displayname = refstr_get(m->title); + } + } + } else if ( looking_at(p, "default") ) { + if (ld.label) { + ld.menudefault = 1; + } else if (m->parent_entry) { + m->parent->defentry = m->parent_entry->entry; + } + } else if ( looking_at(p, "hide") ) { + ld.menuhide = 1; + } else if ( looking_at(p, "passwd") ) { + if ( ld.label ) { + refstr_put(ld.passwd); + ld.passwd = refstrdup(skipspace(p+6)); + } else if ( m->parent_entry ) { + refstr_put(m->parent_entry->passwd); + m->parent_entry->passwd = refstrdup(skipspace(p+6)); + } + } else if ( looking_at(p, "shiftkey") ) { + shiftkey = 1; + } else if ( looking_at(p, "save") ) { + menusave = true; + if (ld.label) + ld.save = 1; + else + m->save = true; + } else if ( looking_at(p, "nosave") ) { + if (ld.label) + ld.save = -1; + else + m->save = false; + } else if ( looking_at(p, "onerror") ) { + refstr_put(m->onerror); + m->onerror = refstrdup(skipspace(p+7)); + } else if ( looking_at(p, "master") ) { + p = skipspace(p+6); + if ( looking_at(p, "passwd") ) { + refstr_put(m->menu_master_passwd); + m->menu_master_passwd = refstrdup(skipspace(p+6)); + } + } else if ( (ep = looking_at(p, "include")) ) { + goto do_include; + } else if ( (ep = looking_at(p, "background")) ) { + p = skipspace(ep); + refstr_put(m->menu_background); + m->menu_background = refdup_word(&p); + } else if ( (ep = looking_at(p, "hidden")) ) { + hiddenmenu = 1; + } else if ( (ep = is_message_name(p, &msgnr)) ) { + refstr_put(m->messages[msgnr]); + m->messages[msgnr] = refstrdup(skipspace(ep)); + } else if ((ep = looking_at(p, "color")) || + (ep = looking_at(p, "colour"))) { + int i; + struct color_table *cptr; + p = skipspace(ep); + cptr = m->color_table; + for ( i = 0; i < menu_color_table_size; i++ ) { + if ( (ep = looking_at(p, cptr->name)) ) { + p = skipspace(ep); + if (*p) { + if (looking_at(p, "*")) { + p++; + } else { + refstr_put(cptr->ansi); + cptr->ansi = refdup_word(&p); + } + + p = skipspace(p); + if (*p) { + if (looking_at(p, "*")) + p++; + else + cptr->argb_fg = parse_argb(&p); + + p = skipspace(p); + if (*p) { + if (looking_at(p, "*")) + p++; + else + cptr->argb_bg = parse_argb(&p); + + /* Parse a shadow mode */ + p = skipspace(p); + ch = *p | 0x20; + if (ch == 'n') /* none */ + cptr->shadow = SHADOW_NONE; + else if (ch == 's') /* std, standard */ + cptr->shadow = SHADOW_NORMAL; + else if (ch == 'a') /* all */ + cptr->shadow = SHADOW_ALL; + else if (ch == 'r') /* rev, reverse */ + cptr->shadow = SHADOW_REVERSE; + } + } + } + break; + } + cptr++; + } + } else if ((ep = looking_at(p, "msgcolor")) || + (ep = looking_at(p, "msgcolour"))) { + unsigned int fg_mask = MSG_COLORS_DEF_FG; + unsigned int bg_mask = MSG_COLORS_DEF_BG; + enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW; + + p = skipspace(ep); + if (*p) { + if (!looking_at(p, "*")) + fg_mask = parse_argb(&p); + + p = skipspace(p); + if (*p) { + if (!looking_at(p, "*")) + bg_mask = parse_argb(&p); + + p = skipspace(p); + switch (*p | 0x20) { + case 'n': + shadow = SHADOW_NONE; + break; + case 's': + shadow = SHADOW_NORMAL; + break; + case 'a': + shadow = SHADOW_ALL; + break; + case 'r': + shadow = SHADOW_REVERSE; + break; + default: + /* go with default */ + break; + } + } + } + set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow); + } else if ( looking_at(p, "separator") ) { + record(m, &ld, append); + ld.label = refstr_get(empty_string); + ld.menuseparator = 1; + record(m, &ld, append); + } else if ( looking_at(p, "disable") || + looking_at(p, "disabled")) { + ld.menudisabled = 1; + } else if ( looking_at(p, "indent") ) { + ld.menuindent = atoi(skipspace(p+6)); + } else if ( looking_at(p, "begin") ) { + record(m, &ld, append); + m = current_menu = begin_submenu(skipspace(p+5)); + } else if ( looking_at(p, "end") ) { + record(m, &ld, append); + m = current_menu = end_submenu(); + } else if ( looking_at(p, "quit") ) { + if (ld.label) + ld.action = MA_QUIT; + } else if ( looking_at(p, "goto") ) { + if (ld.label) { + ld.action = MA_GOTO_UNRES; + refstr_put(ld.kernel); + ld.kernel = refstrdup(skipspace(p+4)); + } + } else if ( looking_at(p, "exit") ) { + p = skipspace(p+4); + if (ld.label && m->parent) { + if (*p) { + /* This is really just a goto, except for the marker */ + ld.action = MA_EXIT_UNRES; + refstr_put(ld.kernel); + ld.kernel = refstrdup(p); + } else { + ld.action = MA_EXIT; + ld.submenu = m->parent; + } + } + } else if ( looking_at(p, "start") ) { + start_menu = m; + } else { + /* Unknown, check for layout parameters */ + enum parameter_number mp; + for (mp = 0; mp < NPARAMS; mp++) { + if ( (ep = looking_at(p, mparm[mp].name)) ) { + m->mparm[mp] = atoi(skipspace(ep)); + break; + } + } + } + } else if ( looking_at(p, "text") ) { + enum text_cmd { + TEXT_UNKNOWN, + TEXT_HELP + } cmd = TEXT_UNKNOWN; + int len = ld.helptext ? strlen(ld.helptext) : 0; + int xlen; + + p = skipspace(p+4); + + if (looking_at(p, "help")) + cmd = TEXT_HELP; + + while ( fgets(line, sizeof line, f) ) { + p = skipspace(line); + if (looking_at(p, "endtext")) + break; + + xlen = strlen(line); + + switch (cmd) { + case TEXT_UNKNOWN: + break; + case TEXT_HELP: + ld.helptext = realloc(ld.helptext, len+xlen+1); + memcpy(ld.helptext+len, line, xlen+1); + len += xlen; + break; + } + } + } else if ( (ep = is_fkey(p, &fkeyno)) ) { + p = skipspace(ep); + if (m->fkeyhelp[fkeyno].textname) { + refstr_put(m->fkeyhelp[fkeyno].textname); + m->fkeyhelp[fkeyno].textname = NULL; + } + if (m->fkeyhelp[fkeyno].background) { + refstr_put(m->fkeyhelp[fkeyno].background); + m->fkeyhelp[fkeyno].background = NULL; + } + + refstr_put(m->fkeyhelp[fkeyno].textname); + m->fkeyhelp[fkeyno].textname = refdup_word(&p); + if (*p) { + p = skipspace(p); + m->fkeyhelp[fkeyno].background = refstrdup(p); + } + } else if ( (ep = looking_at(p, "include")) ) { + do_include: + { + const char *file; + p = skipspace(ep); + file = refdup_word(&p); + p = skipspace(p); + if (*p) { + record(m, &ld, append); + m = current_menu = begin_submenu(p); + parse_one_config(file); + record(m, &ld, append); + m = current_menu = end_submenu(); + } else { + parse_one_config(file); + } + refstr_put(file); + } + } else if ( looking_at(p, "menuparam") ) { + strncpy( defaultparam, skipspace(p+9), 50); + char *pp = strchr(defaultparam,' '); + /* Iportant only first work */ + *pp = 0; + /* Other is number */ + if(pp) + default_menu = atoi(pp+1); + else + default_menu = 0; + } else if ( looking_at(p, "append") ) { + const char *a = refstrdup(skipspace(p+6)); + if ( ld.label ) { + refstr_put(ld.append); + ld.append = a; + } else { + refstr_put(append); + append = a; + } + } else if ( looking_at(p, "initrd") ) { + const char *a = refstrdup(skipspace(p+6)); + if ( ld.label ) { + refstr_put(ld.initrd); + ld.initrd = a; + } else { + /* Ignore */ + } + } else if ( looking_at(p, "label") ) { + p = skipspace(p+5); + record(m, &ld, append); + ld.label = refstrdup(p); + ld.kernel = refstrdup(p); + ld.type = KT_KERNEL; + ld.passwd = NULL; + ld.append = NULL; + ld.initrd = NULL; + ld.menulabel = NULL; + ld.helptext = NULL; + ld.ipappend = ipappend; + ld.menudefault = ld.menuhide = ld.menuseparator = + ld.menudisabled = ld.menuindent = 0; + } else if ( (ep = is_kernel_type(p, &type)) ) { + if ( ld.label ) { + refstr_put(ld.kernel); + ld.kernel = refstrdup(skipspace(ep)); + ld.type = type; + } + } else if ( looking_at(p, "timeout") ) { + m->timeout = (atoi(skipspace(p+7))*CLK_TCK+9)/10; + } else if ( looking_at(p, "totaltimeout") ) { + totaltimeout = (atoll(skipspace(p+13))*CLK_TCK+9)/10; + } else if ( looking_at(p, "ontimeout") ) { + m->ontimeout = refstrdup(skipspace(p+9)); + } else if ( looking_at(p, "allowoptions") ) { + m->allowedit = !!atoi(skipspace(p+12)); + } else if ( looking_at(p, "ipappend") ) { + if (ld.label) + ld.ipappend = atoi(skipspace(p+8)); + else + ipappend = atoi(skipspace(p+8)); + } else if ( looking_at(p, "default") ) { + refstr_put(globaldefault); + globaldefault = refstrdup(skipspace(p+7)); + } else if ( looking_at(p, "ui") ) { + has_ui = 1; + } + } +} + +int splitstr(char *s,char ch, char **params,int maxparam) { + char **eparam = params + maxparam; + char **bparam = params; + while(params != eparam) { + char *es = strchr(s,ch); + (*params++) = s; + if(!es) + break; + *es = 0; + s = es + 1; + } + *params = 0; + return params - bparam; +} + +void overlay_params(char **pars1,char **pars2) { + int force; + for( force=0;*pars2;pars1++,pars2++ ) { + if( !*pars1 ) + force = 1; + if( force || strlen(*pars1) == 0 ) + *pars1 = *pars2; + } + if( force ) + *pars1 = 0; +} + +char *join_params(char **params,char ch,char *s,int maxsize) { + *s = 0; + char splitch[2]; + splitch[0]=ch; + splitch[1]=0; + for(;*params;params++) { + strncat(s,*params,maxsize-strlen(s)); + strncat(s,splitch,maxsize-strlen(s)); + } + *(s+strlen(s)-1)=0; + return s; +} + +static int parse_one_config(const char *filename) +{ + FILE *f; + + if (!strcmp(filename, "~")) + filename = syslinux_config_file(); + + /* if param is config file (.cfg file) */ + + if(strlen(filename)>4 && !strcmp(filename+strlen(filename)-4,".cfg")) { + f = fopen(filename, "r"); + if( !f ) + if( startconfig[0] ) { + f = fopen(startconfig, "r"); + if( !f ) + return 1; + } + else + return 1; + else if( !startconfig[0] ) { + strncpy(startconfig,filename,255); + } + } + /* else it is additional param */ + else { + char *newparam = filename; + char bufParam[512]= "\0"; + char *i_eq = 0; + /* finding '=' in newparam */ + if( (i_eq = strchr(newparam,'='))) { + /* copy from newparam part before '=' */ + memcpy(bufParam, newparam, i_eq-newparam ); + bufParam[i_eq-newparam] = '\0'; + char *p; + /* find newparam in tail_cmdline + if param is found in tail_cmdline */ + while((p = strstr(tail_cmdline,bufParam))) { + /* if it is param - not inner part str */ + if( p == tail_cmdline || *(p-1) == ' ' ) { + /* p pointer to value of parameter */ + p += strlen(bufParam)+1; + char *np = newparam + strlen(bufParam) + 1; + char *params1[256]; char str1[512]; + char *params2[256]; char str2[512]; + char out_str[256]; + /* copy value of tail_cmdline in str1 */ + strcpy(str1,p); + /* Truncates the end of the string containing the rest + of the parameters */ + if(strchr(str1,' ')) + *strchr(str1,' ')=0; + int lstr1 = strlen(str1); + /* spliting str1 to params */ + splitstr(str1,',',params1,255); + /* copy value of newparam and split it */ + strcpy(str2,np); splitstr(str2,',',params2,255); + /* overlaying params */ + overlay_params(params1,params2); + /* join result of overlaying */ + join_params(params1,',',out_str,255); + /* insert result in tail_cmdline */ + memmove(p+strlen(out_str), p+lstr1, strlen(p)+1); + memmove(p,out_str,strlen(out_str)); + return 0; + } + } + } + /* param is new - append it to tail_cmdline */ + strcat(tail_cmdline," "); + strcat(tail_cmdline,filename); + return 0; + } + + parse_config_file(f); + fclose(f); + + return 0; +} + +static void resolve_gotos(void) +{ + struct menu_entry *me; + struct menu *m; + + for (me = all_entries; me; me = me->next) { + if (me->action == MA_GOTO_UNRES || + me->action == MA_EXIT_UNRES) { + m = find_menu(me->cmdline); + refstr_put(me->cmdline); + me->cmdline = NULL; + if (m) { + me->submenu = m; + me->action--; /* Drop the _UNRES */ + } else { + me->action = MA_DISABLED; + } + } + } +} + + +void parse_configs(char **argv) +{ + const char *filename; + struct menu *m; + struct menu_entry *me; + + empty_string = refstrdup(""); + + /* Initialize defaults for the root and hidden menus */ + hide_menu = new_menu(NULL, NULL, refstrdup(".hidden")); + root_menu = new_menu(NULL, NULL, refstrdup(".top")); + start_menu = root_menu; + + /* Other initialization */ + memset(&ld, 0, sizeof(struct labeldata)); + + /* Actually process the files */ + current_menu = root_menu; + if ( !*argv ) { + parse_one_config("~"); + } else { + while ( (filename = *argv++) ) + parse_one_config(filename); + } + + /* On final EOF process the last label statement */ + record(current_menu, &ld, append); + + /* Common postprocessing */ + resolve_gotos(); + + /* Handle global default */ + if (has_ui && globaldefault) { + me = find_label(globaldefault); + if (me && me->menu != hide_menu) { + me->menu->defentry = me->entry; + start_menu = me->menu; + } + } + + /* If "menu save" is active, let the ADV override the global default */ + if (menusave) { + size_t len; + const char *lbl = syslinux_getadv(ADV_MENUSAVE, &len); + char *lstr; + if (lbl && len) { + lstr = refstr_alloc(len); + memcpy(lstr, lbl, len); /* refstr_alloc() adds the final null */ + me = find_label(lstr); + if (me && me->menu != hide_menu) { + me->menu->defentry = me->entry; + start_menu = me->menu; + } + refstr_put(lstr); + } + } + + /* Final per-menu initialization, with all labels known */ + for (m = menu_list; m; m = m->next) { + m->curentry = m->defentry; /* All menus start at their defaults */ + + if ( m->ontimeout ) + m->ontimeout = unlabel(m->ontimeout); + if ( m->onerror ) + m->onerror = unlabel(m->onerror); + } +}