/* Return 1 if 'line' is an mbox From_ line---that is, a line * beginning with zero or more '>' characters followed by the * characters 'F', 'r', 'o', 'm', and space. * * Any characters at all may appear after that in the line. */ static int _is_from_line (const char *line) { const char *s = line; if (line == NULL) return 0; while (*s == '>') s++; if (STRNCMP_LITERAL (s, "From ") == 0) return 1; else return 0; }
static void receive_data_to_file (FILE *peer, FILE *output) { char *line = NULL; size_t line_size; ssize_t line_len; while ((line_len = getline (&line, &line_size, peer)) != -1) { if (STRNCMP_LITERAL (line, ".\r\n") == 0) break; if (line_len < 2) continue; if (line[line_len - 1] == '\n' && line[line_len - 2] == '\r') { line[line_len - 2] = '\n'; line[line_len - 1] = '\0'; } fprintf (output, "%s", line[0] == '.' ? line + 1 : line); } free (line); }
int notmuch_count_command (void *ctx, int argc, char *argv[]) { notmuch_config_t *config; notmuch_database_t *notmuch; notmuch_query_t *query; char *query_str; int i; notmuch_bool_t output_messages = TRUE; argc--; argv++; /* skip subcommand argument */ for (i = 0; i < argc && argv[i][0] == '-'; i++) { if (strcmp (argv[i], "--") == 0) { i++; break; } if (STRNCMP_LITERAL (argv[i], "--output=") == 0) { const char *opt = argv[i] + sizeof ("--output=") - 1; if (strcmp (opt, "threads") == 0) { output_messages = FALSE; } else if (strcmp (opt, "messages") == 0) { output_messages = TRUE; } else { fprintf (stderr, "Invalid value for --output: %s\n", opt); return 1; } } else { fprintf (stderr, "Unrecognized option: %s\n", argv[i]); return 1; } } argc -= i; argv += i; config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; notmuch = notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_ONLY); if (notmuch == NULL) return 1; query_str = query_string_from_args (ctx, argc, argv); if (query_str == NULL) { fprintf (stderr, "Out of memory.\n"); return 1; } if (*query_str == '\0') { query_str = talloc_strdup (ctx, ""); } query = notmuch_query_create (notmuch, query_str); if (query == NULL) { fprintf (stderr, "Out of memory\n"); return 1; } if (output_messages) printf ("%u\n", notmuch_query_count_messages (query)); else printf ("%u\n", notmuch_query_count_threads (query)); notmuch_query_destroy (query); notmuch_database_close (notmuch); return 0; }
int notmuch_search_command (void *ctx, int argc, char *argv[]) { notmuch_config_t *config; notmuch_database_t *notmuch; notmuch_query_t *query; char *query_str; char *opt; notmuch_sort_t sort = NOTMUCH_SORT_NEWEST_FIRST; const search_format_t *format = &format_text; int i, ret; output_t output = OUTPUT_SUMMARY; for (i = 0; i < argc && argv[i][0] == '-'; i++) { if (strcmp (argv[i], "--") == 0) { i++; break; } if (STRNCMP_LITERAL (argv[i], "--sort=") == 0) { opt = argv[i] + sizeof ("--sort=") - 1; if (strcmp (opt, "oldest-first") == 0) { sort = NOTMUCH_SORT_OLDEST_FIRST; } else if (strcmp (opt, "newest-first") == 0) { sort = NOTMUCH_SORT_NEWEST_FIRST; } else { fprintf (stderr, "Invalid value for --sort: %s\n", opt); return 1; } } else if (STRNCMP_LITERAL (argv[i], "--format=") == 0) { opt = argv[i] + sizeof ("--format=") - 1; if (strcmp (opt, "text") == 0) { format = &format_text; } else if (strcmp (opt, "json") == 0) { format = &format_json; } else { fprintf (stderr, "Invalid value for --format: %s\n", opt); return 1; } } else if (STRNCMP_LITERAL (argv[i], "--output=") == 0) { opt = argv[i] + sizeof ("--output=") - 1; if (strcmp (opt, "summary") == 0) { output = OUTPUT_SUMMARY; } else if (strcmp (opt, "threads") == 0) { output = OUTPUT_THREADS; } else if (strcmp (opt, "messages") == 0) { output = OUTPUT_MESSAGES; } else if (strcmp (opt, "files") == 0) { output = OUTPUT_FILES; } else if (strcmp (opt, "tags") == 0) { output = OUTPUT_TAGS; } else { fprintf (stderr, "Invalid value for --output: %s\n", opt); return 1; } } else { fprintf (stderr, "Unrecognized option: %s\n", argv[i]); return 1; } } argc -= i; argv += i; config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; notmuch = notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_ONLY); if (notmuch == NULL) return 1; query_str = query_string_from_args (notmuch, argc, argv); if (query_str == NULL) { fprintf (stderr, "Out of memory.\n"); return 1; } if (*query_str == '\0') { fprintf (stderr, "Error: notmuch search requires at least one search term.\n"); return 1; } query = notmuch_query_create (notmuch, query_str); if (query == NULL) { fprintf (stderr, "Out of memory\n"); return 1; } notmuch_query_set_sort (query, sort); switch (output) { default: case OUTPUT_SUMMARY: case OUTPUT_THREADS: ret = do_search_threads (format, query, sort, output); break; case OUTPUT_MESSAGES: case OUTPUT_FILES: ret = do_search_messages (format, query, output); break; case OUTPUT_TAGS: ret = do_search_tags (notmuch, format, query); break; } notmuch_query_destroy (query); notmuch_database_close (notmuch); return ret; }
int notmuch_new_command (void *ctx, int argc, char *argv[]) { notmuch_config_t *config; notmuch_database_t *notmuch; add_files_state_t add_files_state; double elapsed; struct timeval tv_now, tv_start; int ret = 0; struct stat st; const char *db_path; char *dot_notmuch_path; struct sigaction action; _filename_node_t *f; int renamed_files, removed_files; notmuch_status_t status; int i; notmuch_bool_t timer_is_active = FALSE; add_files_state.verbose = 0; add_files_state.output_is_a_tty = isatty (fileno (stdout)); for (i = 0; i < argc && argv[i][0] == '-'; i++) { if (STRNCMP_LITERAL (argv[i], "--verbose") == 0) { add_files_state.verbose = 1; } else { fprintf (stderr, "Unrecognized option: %s\n", argv[i]); return 1; } } config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; add_files_state.new_tags = notmuch_config_get_new_tags (config, &add_files_state.new_tags_length); add_files_state.synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config); add_files_state.message_ids_to_sync = _filename_list_create (ctx); db_path = notmuch_config_get_database_path (config); dot_notmuch_path = talloc_asprintf (ctx, "%s/%s", db_path, ".notmuch"); if (stat (dot_notmuch_path, &st)) { int count; count = 0; count_files (db_path, &count); if (interrupted) return 1; printf ("Found %d total files (that's not much mail).\n", count); notmuch = notmuch_database_create (db_path); add_files_state.total_files = count; } else { notmuch = notmuch_database_open (db_path, NOTMUCH_DATABASE_MODE_READ_WRITE); if (notmuch == NULL) return 1; if (notmuch_database_needs_upgrade (notmuch)) { printf ("Welcome to a new version of notmuch! Your database will now be upgraded.\n"); gettimeofday (&add_files_state.tv_start, NULL); notmuch_database_upgrade (notmuch, upgrade_print_progress, &add_files_state); printf ("Your notmuch database has now been upgraded to database format version %u.\n", notmuch_database_get_version (notmuch)); } add_files_state.total_files = 0; } if (notmuch == NULL) return 1; /* Setup our handler for SIGINT. We do this after having * potentially done a database upgrade we this interrupt handler * won't support. */ memset (&action, 0, sizeof (struct sigaction)); action.sa_handler = handle_sigint; sigemptyset (&action.sa_mask); action.sa_flags = SA_RESTART; sigaction (SIGINT, &action, NULL); talloc_free (dot_notmuch_path); dot_notmuch_path = NULL; add_files_state.processed_files = 0; add_files_state.added_messages = 0; gettimeofday (&add_files_state.tv_start, NULL); add_files_state.removed_files = _filename_list_create (ctx); add_files_state.removed_directories = _filename_list_create (ctx); if (! debugger_is_active () && add_files_state.output_is_a_tty && ! add_files_state.verbose) { setup_progress_printing_timer (); timer_is_active = TRUE; } ret = add_files (notmuch, db_path, &add_files_state); removed_files = 0; renamed_files = 0; gettimeofday (&tv_start, NULL); for (f = add_files_state.removed_files->head; f; f = f->next) { status = notmuch_database_remove_message (notmuch, f->filename); if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) renamed_files++; else removed_files++; if (do_print_progress) { do_print_progress = 0; generic_print_progress ("Cleaned up", "messages", tv_start, removed_files + renamed_files, add_files_state.removed_files->count); } } gettimeofday (&tv_start, NULL); for (f = add_files_state.removed_directories->head, i = 0; f; f = f->next, i++) { _remove_directory (ctx, notmuch, f->filename, &renamed_files, &removed_files); if (do_print_progress) { do_print_progress = 0; generic_print_progress ("Cleaned up", "directories", tv_start, i, add_files_state.removed_directories->count); } } talloc_free (add_files_state.removed_files); talloc_free (add_files_state.removed_directories); /* Now that removals are done (hence the database is aware of all * renames), we can synchronize maildir_flags to tags for all * messages that had new filenames appear on this run. */ gettimeofday (&tv_start, NULL); if (add_files_state.synchronize_flags) { _filename_node_t *node; notmuch_message_t *message; for (node = add_files_state.message_ids_to_sync->head, i = 0; node; node = node->next, i++) { message = notmuch_database_find_message (notmuch, node->filename); notmuch_message_maildir_flags_to_tags (message); notmuch_message_destroy (message); if (do_print_progress) { do_print_progress = 0; generic_print_progress ( "Synchronized tags for", "messages", tv_start, i, add_files_state.message_ids_to_sync->count); } } } talloc_free (add_files_state.message_ids_to_sync); add_files_state.message_ids_to_sync = NULL; if (timer_is_active) stop_progress_printing_timer (); gettimeofday (&tv_now, NULL); elapsed = notmuch_time_elapsed (add_files_state.tv_start, tv_now); if (add_files_state.processed_files) { printf ("Processed %d %s in ", add_files_state.processed_files, add_files_state.processed_files == 1 ? "file" : "total files"); notmuch_time_print_formatted_seconds (elapsed); if (elapsed > 1) { printf (" (%d files/sec.).\033[K\n", (int) (add_files_state.processed_files / elapsed)); } else { printf (".\033[K\n"); } } if (add_files_state.added_messages) { printf ("Added %d new %s to the database.", add_files_state.added_messages, add_files_state.added_messages == 1 ? "message" : "messages"); } else { printf ("No new mail."); } if (removed_files) { printf (" Removed %d %s.", removed_files, removed_files == 1 ? "message" : "messages"); } if (renamed_files) { printf (" Detected %d file %s.", renamed_files, renamed_files == 1 ? "rename" : "renames"); } printf ("\n"); if (ret) { printf ("\nNote: At least one error was encountered: %s\n", notmuch_status_to_string (ret)); } notmuch_database_close (notmuch); return ret || interrupted; }
int notmuch_count_command (void *ctx, int argc, char *argv[]) { notmuch_config_t *config; notmuch_database_t *notmuch; notmuch_query_t *query; char *query_str; int i; #if 0 char *opt, *end; int i, first = 0, max_threads = -1; notmuch_sort_t sort = NOTMUCH_SORT_NEWEST_FIRST; #endif for (i = 0; i < argc && argv[i][0] == '-'; i++) { if (strcmp (argv[i], "--") == 0) { i++; break; } #if 0 if (STRNCMP_LITERAL (argv[i], "--first=") == 0) { opt = argv[i] + sizeof ("--first=") - 1; first = strtoul (opt, &end, 10); if (*opt == '\0' || *end != '\0') { fprintf (stderr, "Invalid value for --first: %s\n", opt); return 1; } } else if (STRNCMP_LITERAL (argv[i], "--max-threads=") == 0) { opt = argv[i] + sizeof ("--max-threads=") - 1; max_threads = strtoul (opt, &end, 10); if (*opt == '\0' || *end != '\0') { fprintf (stderr, "Invalid value for --max-threads: %s\n", opt); return 1; } } else if (STRNCMP_LITERAL (argv[i], "--sort=") == 0) { opt = argv[i] + sizeof ("--sort=") - 1; if (strcmp (opt, "oldest-first") == 0) { sort = NOTMUCH_SORT_OLDEST_FIRST; } else if (strcmp (opt, "newest-first") == 0) { sort = NOTMUCH_SORT_NEWEST_FIRST; } else { fprintf (stderr, "Invalid value for --sort: %s\n", opt); return 1; } } else #endif { fprintf (stderr, "Unrecognized option: %s\n", argv[i]); return 1; } } argc -= i; argv += i; config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; notmuch = notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_ONLY); if (notmuch == NULL) return 1; query_str = query_string_from_args (ctx, argc, argv); if (query_str == NULL) { fprintf (stderr, "Out of memory.\n"); return 1; } if (*query_str == '\0') { fprintf (stderr, "Error: notmuch count requires at least one count term.\n"); return 1; } query = notmuch_query_create (notmuch, query_str); if (query == NULL) { fprintf (stderr, "Out of memory\n"); return 1; } printf ("%u\n", notmuch_query_count_messages(query)); notmuch_query_destroy (query); notmuch_database_close (notmuch); return 0; }
int notmuch_reply_command (void *ctx, int argc, char *argv[]) { notmuch_config_t *config; notmuch_database_t *notmuch; notmuch_query_t *query; char *opt, *query_string; int i, ret = 0; int (*reply_format_func)(void *ctx, notmuch_config_t *config, notmuch_query_t *query, notmuch_show_params_t *params); notmuch_show_params_t params; reply_format_func = notmuch_reply_format_default; params.part = -1; params.cryptoctx = NULL; argc--; argv++; /* skip subcommand argument */ for (i = 0; i < argc && argv[i][0] == '-'; i++) { if (strcmp (argv[i], "--") == 0) { i++; break; } if (STRNCMP_LITERAL (argv[i], "--format=") == 0) { opt = argv[i] + sizeof ("--format=") - 1; if (strcmp (opt, "default") == 0) { reply_format_func = notmuch_reply_format_default; } else if (strcmp (opt, "headers-only") == 0) { reply_format_func = notmuch_reply_format_headers_only; } else { fprintf (stderr, "Invalid value for --format: %s\n", opt); return 1; } } else if ((STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { if (params.cryptoctx == NULL) { GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) fprintf (stderr, "Failed to construct gpg context.\n"); else g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); g_object_unref (session); session = NULL; } } else { fprintf (stderr, "Unrecognized option: %s\n", argv[i]); return 1; } } argc -= i; argv += i; config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; query_string = query_string_from_args (ctx, argc, argv); if (query_string == NULL) { fprintf (stderr, "Out of memory\n"); return 1; } if (*query_string == '\0') { fprintf (stderr, "Error: notmuch reply requires at least one search term.\n"); return 1; } notmuch = notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_ONLY); if (notmuch == NULL) return 1; query = notmuch_query_create (notmuch, query_string); if (query == NULL) { fprintf (stderr, "Out of memory\n"); return 1; } if (reply_format_func (ctx, config, query, ¶ms) != 0) return 1; notmuch_query_destroy (query); notmuch_database_close (notmuch); if (params.cryptoctx) g_object_unref(params.cryptoctx); return ret; }
int notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[])) { notmuch_config_t *config; notmuch_database_t *notmuch; notmuch_query_t *query; char *query_string; char *opt; const notmuch_show_format_t *format = &format_text; notmuch_show_params_t params; int mbox = 0; int format_specified = 0; int i; params.entire_thread = 0; params.raw = 0; params.part = -1; params.cryptoctx = NULL; params.decrypt = 0; argc--; argv++; /* skip subcommand argument */ for (i = 0; i < argc && argv[i][0] == '-'; i++) { if (strcmp (argv[i], "--") == 0) { i++; break; } if (STRNCMP_LITERAL (argv[i], "--format=") == 0) { opt = argv[i] + sizeof ("--format=") - 1; if (strcmp (opt, "text") == 0) { format = &format_text; } else if (strcmp (opt, "json") == 0) { format = &format_json; params.entire_thread = 1; } else if (strcmp (opt, "mbox") == 0) { format = &format_mbox; mbox = 1; } else if (strcmp (opt, "raw") == 0) { format = &format_raw; params.raw = 1; } else { fprintf (stderr, "Invalid value for --format: %s\n", opt); return 1; } format_specified = 1; } else if (STRNCMP_LITERAL (argv[i], "--part=") == 0) { params.part = atoi(argv[i] + sizeof ("--part=") - 1); } else if (STRNCMP_LITERAL (argv[i], "--entire-thread") == 0) { params.entire_thread = 1; } else if ((STRNCMP_LITERAL (argv[i], "--verify") == 0) || (STRNCMP_LITERAL (argv[i], "--decrypt") == 0)) { if (params.cryptoctx == NULL) { GMimeSession* session = g_object_new(g_mime_session_get_type(), NULL); if (NULL == (params.cryptoctx = g_mime_gpg_context_new(session, "gpg"))) fprintf (stderr, "Failed to construct gpg context.\n"); else g_mime_gpg_context_set_always_trust((GMimeGpgContext*)params.cryptoctx, FALSE); g_object_unref (session); session = NULL; } if (STRNCMP_LITERAL (argv[i], "--decrypt") == 0) params.decrypt = 1; } else { fprintf (stderr, "Unrecognized option: %s\n", argv[i]); return 1; } } argc -= i; argv += i; config = notmuch_config_open (ctx, NULL, NULL); if (config == NULL) return 1; query_string = query_string_from_args (ctx, argc, argv); if (query_string == NULL) { fprintf (stderr, "Out of memory\n"); return 1; } if (mbox && params.part > 0) { fprintf (stderr, "Error: specifying parts is incompatible with mbox output format.\n"); return 1; } if (*query_string == '\0') { fprintf (stderr, "Error: notmuch show requires at least one search term.\n"); return 1; } notmuch = notmuch_database_open (notmuch_config_get_database_path (config), NOTMUCH_DATABASE_MODE_READ_ONLY); if (notmuch == NULL) return 1; query = notmuch_query_create (notmuch, query_string); if (query == NULL) { fprintf (stderr, "Out of memory\n"); return 1; } /* if part was requested and format was not specified, use format=raw */ if (params.part >= 0 && !format_specified) format = &format_raw; /* If --format=raw specified without specifying part, we can only * output single message, so set part=0 */ if (params.raw && params.part < 0) params.part = 0; if (params.part >= 0) return do_show_single (ctx, query, format, ¶ms); else return do_show (ctx, query, format, ¶ms); notmuch_query_destroy (query); notmuch_database_close (notmuch); if (params.cryptoctx) g_object_unref(params.cryptoctx); return 0; }
bool TraceAnalyzer::recordTextureSideEffects(trace::Call *call, const char *name) { if (strcmp(name, "glGenTextures") == 0) { const trace::Array *textures = call->arg(1).toArray(); size_t i; GLuint texture; if (textures) { for (i = 0; i < textures->size(); i++) { texture = textures->values[i]->toUInt(); providef("texture-", texture, call->no); } } return true; } /* FIXME: When we start tracking framebuffer objects as their own * resources, we will want to link the FBO to the given texture * resource, (and to this call). For now, just link render state * to the texture, and force this call to be required. */ if (strcmp(name, "glFramebufferTexture2D") == 0) { GLuint texture; texture = call->arg(3).toUInt(); linkf("render-state", "texture-", texture); provide("state", call->no); } if (strcmp(name, "glBindTexture") == 0) { GLenum target; GLuint texture; std::stringstream ss_target, ss_texture; target = static_cast<GLenum>(call->arg(0).toSInt()); texture = call->arg(1).toUInt(); ss_target << "texture-unit-" << activeTextureUnit << "-target-" << target; ss_texture << "texture-" << texture; resources.erase(ss_target.str()); provide(ss_target.str(), call->no); unlinkAll(ss_target.str()); link(ss_target.str(), ss_texture.str()); /* FIXME: This really shouldn't be necessary. The effect * this provide() has is that all glBindTexture calls will * be preserved in the output trace (never trimmed). Carl * has a trace ("btr") where a glBindTexture call should * not be necessary at all, (it's immediately followed * with a glBindTexture to a different texture and no * intervening texture-related calls), yet this 'provide' * makes the difference between a trim_stress test failing * and passing. * * More investigation is necessary, but for now, be * conservative and don't trim. */ provide("state", call->no); return true; } /* FIXME: Need to handle glMultiTexImage and friends. */ if (STRNCMP_LITERAL(name, "glTexImage") == 0 || STRNCMP_LITERAL(name, "glTexSubImage") == 0 || STRNCMP_LITERAL(name, "glCopyTexImage") == 0 || STRNCMP_LITERAL(name, "glCopyTexSubImage") == 0 || STRNCMP_LITERAL(name, "glCompressedTexImage") == 0 || STRNCMP_LITERAL(name, "glCompressedTexSubImage") == 0 || strcmp(name, "glInvalidateTexImage") == 0 || strcmp(name, "glInvalidateTexSubImage") == 0) { std::set<unsigned> *calls; std::set<unsigned>::iterator c; std::stringstream ss_target, ss_texture; GLenum target = static_cast<GLenum>(call->arg(0).toSInt()); ss_target << "texture-unit-" << activeTextureUnit << "-target-" << target; ss_texture << "texture-" << texture_map[target]; /* The texture resource depends on this call and any calls * providing the given texture target. */ provide(ss_texture.str(), call->no); if (resources.count(ss_target.str())) { calls = &resources[ss_target.str()]; for (c = calls->begin(); c != calls->end(); c++) { provide(ss_texture.str(), *c); } } return true; } if (strcmp(name, "glEnable") == 0) { GLenum cap; cap = static_cast<GLenum>(call->arg(0).toSInt()); if (cap == GL_TEXTURE_1D || cap == GL_TEXTURE_2D || cap == GL_TEXTURE_3D || cap == GL_TEXTURE_CUBE_MAP) { std::stringstream ss; ss << "texture-unit-" << activeTextureUnit << "-target-" << cap; link("render-state", ss.str()); } provide("state", call->no); return true; } if (strcmp(name, "glDisable") == 0) { GLenum cap; cap = static_cast<GLenum>(call->arg(0).toSInt()); if (cap == GL_TEXTURE_1D || cap == GL_TEXTURE_2D || cap == GL_TEXTURE_3D || cap == GL_TEXTURE_CUBE_MAP) { std::stringstream ss; ss << "texture-unit-" << activeTextureUnit << "-target-" << cap; unlink("render-state", ss.str()); } provide("state", call->no); return true; } /* No known texture-related side effects. Return false for more analysis. */ return false; }
void TraceAnalyzer::recordSideEffects(trace::Call *call) { const char *name = call->name(); /* Handle display lists before any other processing. */ /* FIXME: If we encode the list of commands that are executed * immediately (as opposed to those that are compiled into a * display list) then we could generate a "display-list-X" * resource just as we do for "texture-X" resources and only * emit it in the trace if a glCallList(X) is emitted. For * now, simply punt and include anything within glNewList and * glEndList in the trim output. This guarantees that display * lists will work, but does not trim out unused display * lists. */ if (insideNewEndList != 0) { provide("state", call->no); /* Also, any texture bound inside a display list is * conservatively considered required. */ if (strcmp(name, "glBindTexture") == 0) { GLuint texture = call->arg(1).toUInt(); linkf("state", "texture-", texture); } return; } /* If call is flagged as no side effects, then we are done here. */ if (call->flags & trace::CALL_FLAG_NO_SIDE_EFFECTS) { return; } /* Similarly, swap-buffers calls don't have interesting side effects. */ if (call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET && call->flags & trace::CALL_FLAG_END_FRAME) { return; } if (strcmp(name, "glGenTextures") == 0) { const trace::Array *textures = dynamic_cast<const trace::Array *>(&call->arg(1)); size_t i; GLuint texture; if (textures) { for (i = 0; i < textures->size(); i++) { texture = textures->values[i]->toUInt(); providef("texture-", texture, call->no); } } return; } /* FIXME: When we start tracking framebuffer objects as their own * resources, we will want to link the FBO to the given texture * resource, (and to this call). For now, just link render state * to the texture, and force this call to be required. */ if (strcmp(name, "glFramebufferTexture2D") == 0) { GLuint texture; texture = call->arg(3).toUInt(); linkf("render-state", "texture-", texture); required.insert(call->no); } if (strcmp(name, "glBindTexture") == 0) { GLenum target; GLuint texture; std::stringstream ss_target, ss_texture; target = static_cast<GLenum>(call->arg(0).toSInt()); texture = call->arg(1).toUInt(); ss_target << "texture-unit-" << activeTextureUnit << "-target-" << target; ss_texture << "texture-" << texture; resources.erase(ss_target.str()); provide(ss_target.str(), call->no); unlinkAll(ss_target.str()); link(ss_target.str(), ss_texture.str()); /* FIXME: This really shouldn't be necessary. The effect * this provide() has is that all glBindTexture calls will * be preserved in the output trace (never trimmed). Carl * has a trace ("btr") where a glBindTexture call should * not be necessary at all, (it's immediately followed * with a glBindTexture to a different texture and no * intervening texture-related calls), yet this 'provide' * makes the difference between a trim_stress test failing * and passing. * * More investigation is necessary, but for now, be * conservative and don't trim. */ provide("state", call->no); return; } /* FIXME: Need to handle glMultTetImage and friends. */ if (STRNCMP_LITERAL(name, "glTexImage") == 0 || STRNCMP_LITERAL(name, "glTexSubImage") == 0 || STRNCMP_LITERAL(name, "glCopyTexImage") == 0 || STRNCMP_LITERAL(name, "glCopyTexSubImage") == 0 || STRNCMP_LITERAL(name, "glCompressedTexImage") == 0 || STRNCMP_LITERAL(name, "glCompressedTexSubImage") == 0 || strcmp(name, "glInvalidateTexImage") == 0 || strcmp(name, "glInvalidateTexSubImage") == 0) { std::set<unsigned> *calls; std::set<unsigned>::iterator c; std::stringstream ss_target, ss_texture; GLenum target = static_cast<GLenum>(call->arg(0).toSInt()); ss_target << "texture-unit-" << activeTextureUnit << "-target-" << target; ss_texture << "texture-" << texture_map[target]; /* The texture resource depends on this call and any calls * providing the given texture target. */ provide(ss_texture.str(), call->no); if (resources.count(ss_target.str())) { calls = &resources[ss_target.str()]; for (c = calls->begin(); c != calls->end(); c++) { provide(ss_texture.str(), *c); } } return; } if (strcmp(name, "glEnable") == 0) { GLenum cap; cap = static_cast<GLenum>(call->arg(0).toSInt()); if (cap == GL_TEXTURE_1D || cap == GL_TEXTURE_2D || cap == GL_TEXTURE_3D || cap == GL_TEXTURE_CUBE_MAP) { std::stringstream ss; ss << "texture-unit-" << activeTextureUnit << "-target-" << cap; link("render-state", ss.str()); } provide("state", call->no); return; } if (strcmp(name, "glDisable") == 0) { GLenum cap; cap = static_cast<GLenum>(call->arg(0).toSInt()); if (cap == GL_TEXTURE_1D || cap == GL_TEXTURE_2D || cap == GL_TEXTURE_3D || cap == GL_TEXTURE_CUBE_MAP) { std::stringstream ss; ss << "texture-unit-" << activeTextureUnit << "-target-" << cap; unlink("render-state", ss.str()); } provide("state", call->no); return; } if (strcmp(name, "glCreateShader") == 0 || strcmp(name, "glCreateShaderObjectARB") == 0) { GLuint shader = call->ret->toUInt(); providef("shader-", shader, call->no); return; } if (strcmp(name, "glShaderSource") == 0 || strcmp(name, "glShaderSourceARB") == 0 || strcmp(name, "glCompileShader") == 0 || strcmp(name, "glCompileShaderARB") == 0 || strcmp(name, "glGetShaderiv") == 0 || strcmp(name, "glGetShaderInfoLog") == 0) { GLuint shader = call->arg(0).toUInt(); providef("shader-", shader, call->no); return; } if (strcmp(name, "glCreateProgram") == 0 || strcmp(name, "glCreateProgramObjectARB") == 0) { GLuint program = call->ret->toUInt(); providef("program-", program, call->no); return; } if (strcmp(name, "glAttachShader") == 0 || strcmp(name, "glAttachObjectARB") == 0) { GLuint program, shader; std::stringstream ss_program, ss_shader; program = call->arg(0).toUInt(); shader = call->arg(1).toUInt(); ss_program << "program-" << program; ss_shader << "shader-" << shader; link(ss_program.str(), ss_shader.str()); provide(ss_program.str(), call->no); return; } if (strcmp(name, "glDetachShader") == 0 || strcmp(name, "glDetachObjectARB") == 0) { GLuint program, shader; std::stringstream ss_program, ss_shader; program = call->arg(0).toUInt(); shader = call->arg(1).toUInt(); ss_program << "program-" << program; ss_shader << "shader-" << shader; unlink(ss_program.str(), ss_shader.str()); return; } if (strcmp(name, "glUseProgram") == 0 || strcmp(name, "glUseProgramObjectARB") == 0) { GLuint program; program = call->arg(0).toUInt(); unlinkAll("render-program-state"); if (program == 0) { unlink("render-state", "render-program-state"); provide("state", call->no); } else { std::stringstream ss; ss << "program-" << program; link("render-state", "render-program-state"); link("render-program-state", ss.str()); provide(ss.str(), call->no); } return; } if (strcmp(name, "glGetUniformLocation") == 0 || strcmp(name, "glGetUniformLocationARB") == 0 || strcmp(name, "glGetFragDataLocation") == 0 || strcmp(name, "glGetFragDataLocationEXT") == 0 || strcmp(name, "glGetSubroutineUniformLocation") == 0 || strcmp(name, "glGetProgramResourceLocation") == 0 || strcmp(name, "glGetProgramResourceLocationIndex") == 0 || strcmp(name, "glGetVaryingLocationNV") == 0) { GLuint program = call->arg(0).toUInt(); providef("program-", program, call->no); return; } /* For any call that accepts 'location' as its first argument, * perform a lookup in our location->program map and add a * dependence on the program we find there. */ if (call->sig->num_args > 0 && strcmp(call->sig->arg_names[0], "location") == 0) { providef("program-", activeProgram, call->no); /* We can't easily tell if this uniform is being used to * associate a sampler in the shader with a texture * unit. The conservative option is to assume that it is * and create a link from the active program to any bound * textures for the given unit number. * * FIXME: We should be doing the same thing for calls to * glUniform1iv. */ if (strcmp(name, "glUniform1i") == 0 || strcmp(name, "glUniform1iARB") == 0) { GLint max_unit = MAX(GL_MAX_TEXTURE_COORDS, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS); GLint unit = call->arg(1).toSInt(); std::stringstream ss_program; std::stringstream ss_texture; if (unit < max_unit) { ss_program << "program-" << activeProgram; ss_texture << "texture-unit-" << GL_TEXTURE0 + unit << "-target-"; /* We don't know what target(s) might get bound to * this texture unit, so conservatively link to * all. Only bound textures will actually get inserted * into the output call stream. */ linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_1D); linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_2D); linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_3D); linkf(ss_program.str(), ss_texture.str(), GL_TEXTURE_CUBE_MAP); } } return; } /* FIXME: We cut a huge swath by assuming that any unhandled * call that has a first argument named "program" should not * be included in the trimmed output unless the program of * that number is also included. * * This heuristic is correct for many cases, but we should * actually carefully verify if this includes some calls * inappropriately, or if it misses some. */ if (strcmp(name, "glLinkProgram") == 0 || strcmp(name, "glLinkProgramARB") == 0 || (call->sig->num_args > 0 && (strcmp(call->sig->arg_names[0], "program") == 0 || strcmp(call->sig->arg_names[0], "programObj") == 0))) { GLuint program = call->arg(0).toUInt(); providef("program-", program, call->no); return; } /* Handle all rendering operations, (even though only glEnd is * flagged as a rendering operation we treat everything from * glBegin through glEnd as a rendering operation). */ if (call->flags & trace::CALL_FLAG_RENDER || insideBeginEnd) { std::set<unsigned> calls; std::set<unsigned>::iterator c; provide("framebuffer", call->no); calls = resolve("render-state"); for (c = calls.begin(); c != calls.end(); c++) { provide("framebuffer", *c); } /* In some cases, rendering has side effects beyond the * framebuffer update. */ if (renderingHasSideEffect()) { provide("state", call->no); for (c = calls.begin(); c != calls.end(); c++) { provide("state", *c); } } return; } /* By default, assume this call affects the state somehow. */ resources["state"].insert(call->no); }
int main (int argc, char *argv[]) { void *local; command_t *command; alias_t *alias; unsigned int i, j; const char **argv_local; talloc_enable_null_tracking (); local = talloc_new (NULL); g_mime_init (0); g_type_init (); if (argc == 1) return notmuch (local); if (STRNCMP_LITERAL (argv[1], "--help") == 0) return notmuch_help_command (NULL, 0, NULL); if (STRNCMP_LITERAL (argv[1], "--version") == 0) { printf ("notmuch " STRINGIFY(NOTMUCH_VERSION) "\n"); return 0; } for (i = 0; i < ARRAY_SIZE (aliases); i++) { alias = &aliases[i]; if (strcmp (argv[1], alias->name) == 0) { int substitutions; argv_local = talloc_size (local, sizeof (char *) * (argc + MAX_ALIAS_SUBSTITUTIONS - 1)); if (argv_local == NULL) { fprintf (stderr, "Out of memory.\n"); return 1; } /* Copy all substution arguments from the alias. */ argv_local[0] = argv[0]; for (j = 0; j < MAX_ALIAS_SUBSTITUTIONS; j++) { if (alias->substitutions[j] == NULL) break; argv_local[j+1] = alias->substitutions[j]; } substitutions = j; /* And copy all original arguments (skipping the argument * that matched the alias of course. */ for (j = 2; j < (unsigned) argc; j++) { argv_local[substitutions+j-1] = argv[j]; } argc += substitutions - 1; argv = (char **) argv_local; } } for (i = 0; i < ARRAY_SIZE (commands); i++) { command = &commands[i]; if (strcmp (argv[1], command->name) == 0) return (command->function) (local, argc - 2, &argv[2]); } fprintf (stderr, "Error: Unknown command '%s' (see \"notmuch help\")\n", argv[1]); talloc_free (local); return 1; }