- /* main.c - main program. 
-   
-    aspp - simple assembler source file preprocessor. 
-   
-    Author: Ivan Tatarinov, <ivan-tat@ya.ru>, 2019-2020. 
-   
-    This is free and unencumbered software released into the public domain. 
-    For more information, please refer to <http://unlicense.org>. 
-   
-    Home page: <https://gitlab.com/ivan-tat/aspp> */ 
-   
- #include "defs.h" 
-   
- #include <stdbool.h> 
- #include <stdarg.h> 
- #include <string.h> 
- #include <stdio.h> 
- #include <stdlib.h> 
- #include <errno.h> 
- #include <locale.h> 
- #include <ctype.h> 
- #include "asmfile.h" 
- #include "debug.h" 
- #include "l_err.h" 
- #include "l_ifile.h" 
- #include "l_inc.h" 
- #include "l_isrc.h" 
- #include "l_list.h" 
- #include "l_pre.h" 
- #include "l_src.h" 
- #include "l_tgt.h" 
- #include "parser.h" 
- #include "platform.h" 
-   
- #define PROGRAM_NAME "aspp" 
-   
- #define PROGRAM_VERSION "0.1" PROGRAM_VERSION_SUFFIX 
-   
- #define PROGRAM_DESCRIPTION "Simple assembler source file preprocessor." 
-   
- #define PROGRAM_LICENSE \ 
- "License: public domain, <http://unlicense.org>" NL \ 
- "This is free software; you are free to change and redistribute it." NL \ 
- "There is NO WARRANTY, to the extent permitted by law." 
-   
- #define PROGRAM_AUTHORS \ 
- "Author: Ivan Tatarinov, <ivan-tat@ya.ru>, 2019-2020." 
-   
- #define PROGRAM_CONTACTS \ 
- "Home page: <https://gitlab.com/ivan-tat/aspp>" 
-   
- #define HELP_HINT \ 
- "Use '-h' or '--help' to get help." 
-   
- // Acts 
-   
- #define ACT_NONE       0 
- #define ACT_SHOW_HELP  1 
- #define ACT_PREPROCESS 2 
- #define ACT_MAKE_RULE  3 
-   
- // Variables 
-   
- struct errors_t 
-           errors           = { { .first = NULL, .last = NULL, .count = 0 } }; 
- unsigned  v_syntax         = SYNTAX_TASM; 
- unsigned  v_act            = ACT_NONE; 
- char      v_act_show_help  = 0; 
- char      v_act_preprocess = 0; 
- char      v_act_make_rule  = 0; 
- char     *v_base_path_real = NULL; 
- struct include_paths_t 
-           v_include_paths  = { { .first = NULL, .last = NULL, .count = 0 } }; 
- struct input_sources_t 
-           v_input_sources  = { { .first = NULL, .last = NULL, .count = 0 } }; 
- struct sources_t 
-           v_sources        = { { .first = NULL, .last = NULL, .count = 0 } }; 
- struct target_names_t 
-           v_target_names   = { { .first = NULL, .last = NULL, .count = 0 } }; 
- char     *v_output_name; 
- struct prerequisites_t 
-           v_prerequisites  = { { .first = NULL, .last = NULL, .count = 0 } }; 
-   
- #if DEBUG == 1 
- void _DBG_dump_vars (void) 
- { 
-     const char *s; 
-     _DBG_ ("Input files syntax = '%s'", _syntax_to_str (v_syntax, &s) ? s : "unknown"); 
-     _DBG_include_paths_dump (&v_include_paths); 
-     _DBG_input_sources_dump (&v_input_sources); 
-     _DBG_target_names_dump (&v_target_names); 
-     _DBG_ ("Output file name = '%s'", v_output_name); 
-   
- } 
- #else   // DEBUG != 1 
- #define _DBG_dump_vars(x) 
- #endif  // DEBUG != 1 
-   
- // Returns "false" on success. 
- bool add_error (const char *format, ...) 
- { 
-     va_list ap; 
-     bool status; 
-   
-     status = errors_add_vfmt (&errors, NULL, 1024, format, ap); 
-     return status; 
- } 
-   
- // Returns "false" on success. 
- bool add_missing_arg_error (const char *name, unsigned index) 
- { 
-     return add_error ("Missing parameter for %s (argument #%u).", name, index); 
- } 
-   
- void show_errors (void) 
- { 
-     const struct error_entry_t *p; 
-   
-     for (p = (struct error_entry_t *) errors.list.first; p; 
-          p = (struct error_entry_t *) p->list_entry.next) 
- } 
-   
- void exit_on_errors (void) 
- { 
-     if (errors.list.count) 
-     { 
-         fprintf (- stderr , "Errors: %u. Stopped."-  NL ,-  errors. list- . count);
 
-     } 
- } 
-   
- void error_exit (const char *format, ...) 
- { 
-     va_list ap; 
-   
- } 
-   
- void show_title (void) 
- { 
-         "%s (version %s) - %s" NL 
-         "%s" NL 
-         "%s" NL 
-         "%s" NL, 
-         PROGRAM_NAME, 
-         PROGRAM_VERSION, 
-         PROGRAM_DESCRIPTION, 
-         PROGRAM_LICENSE, 
-         PROGRAM_AUTHORS, 
-         PROGRAM_CONTACTS 
-     ); 
- } 
-   
- void show_help (void) 
- { 
- NL 
- "Usage:" NL 
- "    %s [options] [filename ...] [options]" NL 
- NL 
- "Options (GCC-compatible):" NL 
- "-h, --help      show this help and exit" NL 
- "-E              preprocess" NL 
- "-I <path>       include directory" NL 
- "-M[M]           output autodepend make rule" NL 
- "-MF <file>      autodepend output name" NL 
- "-MT <target>    autodepend target name (can be specified multiple times)" NL 
- NL 
- "Other options:" NL 
- "--syntax <syntax>   select source file syntax (tasm, sjasm)" NL, 
-         PROGRAM_NAME 
-     ); 
- } 
-   
- // Result must be freed by caller. 
- char *_make_path (const char *a, const char *b) 
- { 
-     char *result; 
-   
-     if (result) 
-         sprintf (- result , "%s"-  PATHSEPSTR  "%s",-  a ,-  b );
 
-     else 
-     { 
-         _perror ("malloc"); 
-     } 
-     return result; 
- } 
-   
- // Returns "true" on success. 
- bool process_included_file (struct source_entry_t *src, char *f_loc, unsigned inc_flags) 
- { 
-     bool ok; 
-     char *tmp; 
-     char *src_base; 
-     char *src_base_tmp; 
-     char *inc_real, *inc_base, *inc_user; 
-     char *inc_real_tmp, *inc_base_tmp, *inc_user_tmp; 
-     char *inc_real_res; 
-     struct include_path_entry_t *resolved; 
-   
-     _DBG_ ("Source user file = '%s'", src->user); 
-     _DBG_ ("Source base path = '%s'", src->base); 
-     _DBG_ ("Source real file = '%s'", src->real); 
-     _DBG_ ("Include file = '%s'", f_loc); 
-     _DBG_ ("Include flags = 0x%X", inc_flags); 
-   
-     ok = false; 
-     src_base_tmp = (char *) NULL; 
-     inc_real_tmp = (char *) NULL; 
-     inc_base_tmp = (char *) NULL; 
-     inc_user_tmp = (char *) NULL; 
-     inc_real_res = (char *) NULL; 
-   
-     inc_user = f_loc; 
-     if (check_path_abs (f_loc)) 
-     { 
-         // absolute source's path - use it as is 
-         inc_real = f_loc; 
-         inc_base_tmp = get_dir_name (f_loc); 
-         if (!inc_base_tmp) 
-         { 
-             // Fail 
-             _perror ("get_dir_name"); 
-             goto _local_exit; 
-         } 
-         inc_base = inc_base_tmp; 
-         if (!check_file_exists (f_loc)) 
-             inc_flags &= ~SRCFL_PARSE; 
-         if (sources_add (&v_sources, inc_real, inc_base, inc_user, inc_flags, NULL)) 
-         { 
-             // Fail 
-             _perror ("sources_add"); 
-             goto _local_exit; 
-         } 
-         // Success 
-         ok = true; 
-     } 
-     else 
-     { 
-         // relative source's path - try to resolve real name 
-         src_base_tmp = get_dir_name (src->user); 
-         if (!src_base_tmp) 
-         { 
-             // Fail 
-             _perror ("get_dir_name"); 
-             goto _local_exit; 
-         } 
-         src_base = src_base_tmp; 
-         if (check_path_abs (src->user)) 
-         { 
-             // Absolute path of primary source file 
-             tmp = _make_path (src_base, src->user); 
-             if (!tmp) 
-             { 
-                 // Fail 
-                 _perror ("_make_path"); 
-                 goto _local_exit; 
-             } 
-             inc_real_tmp = resolve_full_path (tmp); 
-             if (!inc_real_tmp) 
-             { 
-                 // Fail 
-                 _perror ("resolve_full_path"); 
-                 goto _local_exit; 
-             } 
-             inc_real = inc_real_tmp; 
-             inc_base = src_base; 
-         } 
-         else 
-         { 
-             // Relative path of primary source file 
-             tmp = get_dir_name (src->real); 
-             if (!tmp) 
-             { 
-                 // Fail 
-                 _perror ("get_dir_name"); 
-                 goto _local_exit; 
-             } 
-             inc_real_tmp = _make_path (tmp, f_loc); 
-             if (!inc_real_tmp) 
-             { 
-                 // Fail 
-                 _perror ("_make_path"); 
-                 goto _local_exit; 
-             } 
-             tmp = inc_real_tmp; 
-             inc_real_tmp = resolve_full_path (tmp); 
-             if (!inc_real_tmp) 
-             { 
-                 // Fail 
-                 _perror ("resolve_full_path"); 
-                 goto _local_exit; 
-             } 
-             inc_real = inc_real_tmp; 
-             inc_base = src->base; 
-             if (strcmp (- src_base , ".") != 0)
 
-             { 
-                 inc_user_tmp = _make_path (src_base, inc_user); 
-                 if (!inc_user_tmp) 
-                 { 
-                     // Fail 
-                     _perror ("_make_path"); 
-                     goto _local_exit; 
-                 } 
-                 inc_user = inc_user_tmp; 
-             } 
-         } 
-         if (check_file_exists (inc_real)) 
-         { 
-             if (sources_add (&v_sources, inc_real, inc_base, inc_user, inc_flags, NULL)) 
-             { 
-                 // Fail 
-                 _perror ("sources_add"); 
-                 goto _local_exit; 
-             } 
-             // Success 
-         } 
-         else 
-         { 
-             _DBG_ ("'%s' not found, resolving...", f_loc); 
-             if (!include_paths_resolve_file (&v_include_paths, f_loc, &resolved)) 
-             { 
-                 tmp = _make_path (resolved->real, f_loc); 
-                 if (!tmp) 
-                 { 
-                     // Fail 
-                     _perror ("_make_path"); 
-                     goto _local_exit; 
-                 } 
-                 inc_real_res = resolve_full_path (tmp); 
-                 if (!inc_real_res) 
-                 { 
-                     // Fail 
-                     _perror ("resolve_full_path"); 
-                     goto _local_exit; 
-                 } 
-                 inc_real = inc_real_res; 
-                 inc_base = resolved->real; 
-                 inc_user = f_loc; 
-             } 
-             else 
-             { 
-                 inc_flags = 0; 
-             } 
-             if (sources_add (&v_sources, inc_real, inc_base, inc_user, 0, NULL)) 
-             { 
-                 // Fail 
-                 _perror ("sources_add"); 
-                 goto _local_exit; 
-             } 
-             // Success 
-         } 
-         // Success 
-         ok = true; 
-     } 
- _local_exit: 
-     if (src_base_tmp) 
-     if (inc_real_tmp) 
-     if (inc_base_tmp) 
-     if (inc_user_tmp) 
-     if (inc_real_res) 
-   
-     _DBG_ ("Done checking '%s' (%s).", f_loc, ok ? "success" : "failed"); 
-     return ok; 
- } 
-   
- // Returns "false" on success. 
- bool collect_included_files (struct source_entry_t *src) 
- { 
-     bool ok; 
-     struct asm_file_t file; 
-     char *t; 
-     const char *s; 
-     unsigned tl, len; 
-     unsigned inc_flags; 
-     char *inc_name; 
-     get_include_proc_t *getincl; 
-     char st; 
-     struct included_file_entry_t *incl; 
-   
-     _DBG_ ("Source user file = '%s'", src->user); 
-     _DBG_ ("Source base path = '%s'", src->base); 
-     _DBG_ ("Source real file = '%s'", src->real); 
-   
-     ok = false; 
-   
-     // Free on exit (_local_exit): 
-     asm_file_clear (&file); 
-     t = (char *) NULL; 
-   
-     if (!_find_get_include_proc (v_syntax, &getincl)) 
-     { 
-         // Fail 
-         _DBG ("Unknown syntax specified."); 
-         goto _local_exit; 
-     } 
-   
-     if (!asm_file_load (&file, src->user)) 
-     { 
-         // Fail 
-         goto _local_exit; 
-     } 
-   
-     tl = 0; 
-     while (asm_file_next_line (&file, &s, &len)) 
-     { 
-         // Free on exit (_loop_exit): 
-         inc_name = (char *) NULL; 
-   
-         if (tl < len + 1) 
-         { 
-             tl = len + 1;       // + terminating zero 
-             if (t) 
-             if (!t) 
-             { 
-                 // Fail 
-                 _perror ("malloc"); 
-                 goto _loop_exit; 
-             } 
-         } 
-         t[len] = '\0'; 
-   
-         st = getincl (t, &inc_flags, &inc_name); 
-   
-         switch (st) 
-         { 
-         case PARST_OK: 
-             if (included_files_find (&src->included, inc_name, &incl)) 
-             { 
-                 if (included_files_add (&src->included, file.line, inc_flags, inc_name, NULL)) 
-                 { 
-                     // Fail 
-                     goto _loop_exit; 
-                 } 
-             } 
-             else 
-             { 
-                 // HINT: This is weird if we included this file as binary but now we want to parse it 
-                 if ((inc_flags & SRCFL_PARSE) && !(incl->flags & SRCFL_PARSE)) 
-                     incl->flags |= SRCFL_PARSE; 
-             } 
-   
-             if (inc_name) 
-             inc_name = (char *) NULL; 
-             break; 
-         case PARST_SKIP: 
-             goto _skip_line; 
-         default: 
-             // Error 
-             goto _loop_exit; 
-         } 
-     _skip_line:; 
-     } 
-   
-     ok = true; 
-     goto _local_exit; 
-   
- _loop_exit: 
-     if (inc_name) 
- _local_exit: 
-     asm_file_free (&file); 
-     if (t) 
-     _DBG_ ("Done collecting included files of '%s' (%s).", src->user, ok ? "success" : "failed"); 
-     return !ok; 
- } 
-   
- // Returns "false" on success. 
- bool process_included_files_list (struct source_entry_t *src) 
- { 
-     bool ok; 
-     struct included_file_entry_t *p; 
-   
-     ok = false; 
-   
-     p = (struct included_file_entry_t *) src->included.list.first; 
-     while (p) 
-     { 
-         if (!process_included_file (src, p->name, p->flags)) 
-         { 
-             // Fail 
-             goto _local_exit; 
-         } 
-         p = (struct included_file_entry_t *) p->list_entry.next; 
-     } 
-   
-     ok = true; 
-   
- _local_exit: 
-     _DBG_ ("Done parsing included files of '%s' (%s).", src->user, ok ? "success" : "failed"); 
-     return !ok; 
- } 
-   
- // Returns "false" on success. 
- bool parse_source (struct source_entry_t *src) 
- { 
-     bool ok; 
-   
-     ok = false; 
-   
-     if (collect_included_files (src)) 
-     { 
-         // Fail 
-         goto _local_exit; 
-     } 
-   
-     _DBG_ ("Found %u included files.", src->included.list.count); 
-   
-     if (src->included.list.count && process_included_files_list (src)) 
-     { 
-         // Fail 
-         goto _local_exit; 
-     } 
-   
-     ok = true; 
-   
- _local_exit: 
-     _DBG_ ("Done parsing '%s' (%s).", src->user, ok ? "success" : "failed"); 
-     return !ok; 
- } 
-   
- // Returns "false" on success. 
- bool make_rule (void) 
- { 
-     struct input_source_entry_t *isrc; 
-     struct source_entry_t *src, *last; 
-   
-     for (isrc = (struct input_source_entry_t *) v_input_sources.list.first; isrc; 
-          isrc = (struct input_source_entry_t *) isrc->list_entry.next) 
-     { 
-         if (sources_add (&v_sources, isrc->real, isrc->base, isrc->user, SRCFL_PARSE, NULL)) 
-             return true;        // Fail 
-     } 
-   
-     if (v_sources.list.count) 
-     { 
-         src  = (struct source_entry_t *) v_sources.list.first; 
-         last = (struct source_entry_t *) v_sources.list.last; 
-         while (src != (struct source_entry_t *) last->list_entry.next) 
-         { 
-             while (src != (struct source_entry_t *) last->list_entry.next) 
-             { 
-                 if (src->flags & SRCFL_PARSE) 
-                 { 
-                     if (!parse_source (src)) 
-                     { 
-                         if (prerequisites_add (&v_prerequisites, src->user, NULL)) 
-                             return true;        // Fail 
-                     } 
-                     else 
-                     { 
-                         show_errors (); 
-                         exit_on_errors (); 
-                     } 
-                 } 
-                 else 
-                 { 
-                     if (prerequisites_add (&v_prerequisites, src->user, NULL)) 
-                         return true;    // Fail 
-                 } 
-                 src = (struct source_entry_t *) src->list_entry.next; 
-             } 
-             src = (struct source_entry_t *) last->list_entry.next; 
-             last = (struct source_entry_t *) v_sources.list.last; 
-         } 
-     } 
-   
-     return false;       // Success 
- } 
-   
- // Returns "false" on success. 
- bool write_rule (const char *name) 
- { 
-     FILE *f; 
-   
-     if (!f) 
-     { 
-         // Fail 
-         _perror ("fopen"); 
-         return true; 
-     } 
-   
-     if (target_names_print (&v_target_names, f)) 
-         return true;    // Fail 
-   
-         return true;    // Fail 
-   
-     if (prerequisites_print (&v_prerequisites, f)) 
-         return true;    // Fail 
-   
-         return true;    // Fail 
-   
-   
-     return false;       // Success 
- } 
-   
- int main (int argc, char **argv) 
- { 
-     unsigned i; 
-   
-   
-     if (argc == 1) 
-         error_exit ("No parameters. %s" NL, HELP_HINT); 
-   
-     v_base_path_real = get_current_dir (); 
-     if (!v_base_path_real) 
-         error_exit ("Failed to get current directory." NL); 
-     _DBG_ ("Base path = '%s'", v_base_path_real); 
-   
-     i = 1; 
-     while (i < argc) 
-     { 
-         if (strcmp (- argv [- i ], "-h") == 0
 
-         ||  strcmp (- argv [- i ], "--help") == 0)
 
-         { 
-             v_act_show_help = 1; 
-             i++; 
-         } 
-         else if (strcmp (- argv [- i ], "-E") == 0)
 
-         { 
-             v_act_preprocess = 1; 
-             i++; 
-         } 
-         else if (strcmp (- argv [- i ], "-I") == 0)
 
-         { 
-             i++; 
-             if (i == argc) 
-             { 
-                 if (add_missing_arg_error ("-I", i)) 
-                 break; 
-             } 
-             if (include_paths_add_with_check (&v_include_paths, argv[i], v_base_path_real, NULL)) 
-             i++; 
-         } 
-         else if (strcmp (- argv [- i ], "-M") == 0
 
-              ||  strcmp (- argv [- i ], "-MM") == 0)
 
-         { 
-             v_act_make_rule = 1; 
-             i++; 
-         } 
-         else if (strcmp (- argv [- i ], "-MF") == 0)
 
-         { 
-             i++; 
-             if (i == argc) 
-             { 
-                 if (add_missing_arg_error ("-MF", i)) 
-                 break; 
-             } 
-             v_output_name = argv[i]; 
-             i++; 
-         } 
-         else if (strcmp (- argv [- i ], "-MT") == 0)
 
-         { 
-             i++; 
-             if (i == argc) 
-             { 
-                 if (add_missing_arg_error ("-MT", i)) 
-                 break; 
-             } 
-             if (target_names_add (&v_target_names, argv[i], NULL)) 
-             i++; 
-         } 
-         else if (strcmp (- argv [- i ], "--syntax") == 0)
 
-         { 
-             i++; 
-             if (i == argc) 
-             { 
-                 if (add_missing_arg_error ("--syntax", i)) 
-                 break; 
-             } 
-             if (!_str_to_syntax (argv[i], &v_syntax)) 
-                 if (add_error ("Unknown syntax '%s' (#%u).", argv[i], i)) 
-             i++; 
-         } 
-         else if (argv[i][0] == '-') 
-         { 
-             if (add_error ("Unknown option '%s' (#%u).", argv[i], i)) 
-             i++; 
-         } 
-         else 
-         { 
-             if (v_input_sources.list.count >= 1) 
-             { 
-                 if (add_error ("Don't know what to do with input file '%s' (#%u).", argv[i], i)) 
-             } 
-             else 
-             { 
-                 if (input_sources_add_with_check (&v_input_sources, argv[i], v_base_path_real, NULL)) 
-                     error_exit ("Input source file '%s' was not found." NL, argv[i]); 
-             } 
-             i++; 
-         } 
-     } 
-   
-     if (v_act_show_help) 
-     { 
-         if (v_act_preprocess + v_act_make_rule + v_include_paths.list.count + v_sources.list.count) 
-         { 
-             if (add_error ("Other arguments were ignored.")) 
-         } 
-         v_act = ACT_SHOW_HELP; 
-     } 
-     else 
-     { 
-         if (v_act_preprocess + v_act_make_rule != 2) 
-         { 
-             if (add_error ("The only supported mode is when both options -E and -M are specified.")) 
-         } 
-         v_act = ACT_MAKE_RULE; 
-     } 
-   
-     if (errors.list.count) 
-     { 
-         if (v_act_show_help) 
-             show_title (); 
-         show_errors (); 
-         if (v_act_show_help) 
-         { 
-             show_help (); 
-         } 
-         else 
-             exit_on_errors (); 
-     } 
-   
-     switch (v_act) 
-     { 
-     case ACT_SHOW_HELP: 
-         show_title (); 
-         show_help (); 
-         break; 
-     case ACT_MAKE_RULE: 
-         if (!v_target_names.list.count) 
-         { 
-             if (add_error ("No target name was specified.")) 
-         } 
-         if (!- v_output_name  || !strcmp (- v_output_name , ""))
 
-         { 
-             if (add_error ("No output name was specified.")) 
-         } 
-         if (!v_input_sources.list.count) 
-         { 
-             if (add_error ("No source files were specified.")) 
-         } 
-         if (errors.list.count) 
-         { 
-             show_errors (); 
-             exit_on_errors (); 
-         } 
-         if (!v_include_paths.list.count) 
-         { 
-             if (include_paths_add_with_check (&v_include_paths, ".", v_base_path_real, NULL)) 
-         } 
-         _DBG_dump_vars (); 
-         if (make_rule ()) 
-             error_exit ("Failed to parse sources."); 
-         if (write_rule (v_output_name)) 
-             error_exit ("Failed to write to output file."); 
-         break; 
-     default: 
-         error_exit ("Action %u is not implemented yet.", v_act); 
-         break; 
-     } 
-   
-     return EXIT_SUCCESS; 
- } 
-