commit 7fb0248a33dc721ae570c294b04f6da94cd96b10 (HEAD, refs/remotes/origin/master) Author: Po Lu Date: Mon Aug 7 13:01:56 2023 +0800 Port to the Android NDK r10b * src/androidvfs.c (android_saf_stat, android_saf_file_open) (android_fstat): Eschew accessing POSIX timespec fields in struct stat, employing accessors supplied in Gnulib stat-time.h in their place. diff --git a/src/androidvfs.c b/src/androidvfs.c index 5afa752163d..4234e337acb 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -31,6 +31,8 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include +#include + #include #include "android.h" @@ -4011,8 +4013,15 @@ android_saf_stat (const char *uri_name, const char *id_name, memset (statb, 0, sizeof *statb); statb->st_size = MAX (0, MIN (TYPE_MAXIMUM (off_t), size)); statb->st_mode = mode; - statb->st_mtim.tv_sec = mtim / 1000; - statb->st_mtim.tv_nsec = (mtim % 1000) * 1000000; +#ifdef STAT_TIMESPEC + STAT_TIMESPEC (statb, st_mtim).tv_sec = mtim / 1000; + STAT_TIMESPEC (statb, st_mtim).tv_nsec = (mtim % 1000) * 1000000; +#else /* !STAT_TIMESPEC */ + /* Headers supplied by the NDK r10b contain a `struct stat' without + POSIX fields for nano-second timestamps. */ + statb->st_mtime = mtim / 1000; + statb->st_mtime_nsec = (mtim % 1000) * 1000000; +#endif /* STAT_TIMESPEC */ statb->st_uid = getuid (); statb->st_gid = getgid (); return 0; @@ -5674,7 +5683,7 @@ android_saf_file_open (struct android_vnode *vnode, int flags, if (!android_saf_stat (vp->tree_uri, vp->document_id, &statb)) - info->mtime = statb.st_mtim; + info->mtime = get_stat_mtime (&statb); else info->mtime = invalid_timespec (); @@ -6678,7 +6687,12 @@ android_fstat (int fd, struct stat *statb) if (parcel_fd->fd == fd && timespec_valid_p (parcel_fd->mtime)) { - statb->st_mtim = parcel_fd->mtime; +#ifdef STAT_TIMESPEC + STAT_TIMESPEC (statb, st_mtim) = parcel_fd->mtime; +#else /* !STAT_TIMESPEC */ + statb->st_mtime = parcel_fd->mtime.tv_sec; + statb->st_mtime_nsec = parcel_fd->mtime.tv_nsec; +#endif /* STAT_TIMESPEC */ break; } } commit fb997c8a8c1cde191986336d3ea83e911b32700b Author: Po Lu Date: Mon Aug 7 10:18:49 2023 +0800 Repair hang in android_fdopen * src/androidvfs.c (android_fdopen): Do not neglect to update next when iterating through open_parcel_fds. diff --git a/src/androidvfs.c b/src/androidvfs.c index d6daff481b0..5afa752163d 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -5606,7 +5606,7 @@ android_saf_file_open (struct android_vnode *vnode, int flags, method = service_class.open_document; trunc = (flags & O_TRUNC); - write = ((flags & O_RDWR) == O_RDWR || (flags & O_WRONLY)); + write = (((flags & O_RDWR) == O_RDWR) || (flags & O_WRONLY)); inside_saf_critical_section = true; descriptor = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, @@ -6917,11 +6917,12 @@ android_fdopen (int fd, const char *mode) if (fd != new_fd) emacs_abort (); - goto open_file; + break; } + else + next = &(*next)->next; } - open_file: return fdopen (fd, mode); } commit c71a520d1da636a722cf87b46534ca3b5aafbc7b Merge: 18e7bc87521 9a9f73041d0 Author: Po Lu Date: Mon Aug 7 08:51:11 2023 +0800 Introduce an Android window system port for GNU Emacs * src/xterm.h: New fields `quit_keysym' and `quit_keysym_time'. * src/xterm.c (handle_one_xevent): Check for the quit keysym, and set Vquit_flag upon witnessing two clicks in rapid succession. (x_term_init): Set `quit_keysym'. (init_xterm): Fix typo in name of `register_textconv_interface'. (syms_of_xterm) : Describe its default value on android. * src/xfns.c (xic_string_conversion_callback): Pass `0' as the last argument to textconv_query. (Fx_server_vendor, Fx_server_version): Document return values on Android. * src/xfaces.c (Fx_family_fonts, set_lface_from_font): Use FRAME_RES instead of FRAME_RES_Y, respecting user preferences on window systems that have distinct display and font scaling factors. (Fx_load_color_file): Call `emacs_fclose', not fclose. * src/xdisp.c (tab_bar_item_info): Allow `close_p' to be NULL. (get_tab_bar_item): Update commentary to reflect that change. (get_tab_bar_item_kbd): New function, resembling get_tab_bar_item. (build_desired_tool_bar_string): Clear `f->tool_bar_wraps_p'; insert new line characters if a QCwrap item is encountered, and set f->tool_bar_wrap_p. Replace characters beyond the end of the tool bar with spaces. (display_tool_bar_line): Move iterator to the next line if in contact with an explicit line-wrap item. (redisplay_internal): If there are newline characters in the tool bar, refrain from coercing each row into being identically tall. Don't call `set_tty_color_mode' on Android. (mark_window_display_accurate_1): Report changes to the point and mark to input methods. (display_menu_bar): Adjust ifdefs to allow non-X window systems to use the built-in menu bar. (draw_row_with_mouse_face): Don't call TTY functions on Android. (note_mouse_highlight): Call `popup_activated' on Android. (expose_frame): Correctly work on the menu bar window. (gui_union_rectangles): New function. * src/window.h (struct window): New fields for recording the last window and point positions, along with an ephemeral position used during IM text conversion. (WINDOW_MENU_BAR_P): Correct definition for non-X window systems without external menu bars. * src/window.c (replace_buffer_in_windows): Call Qreplace_buffer_in_windows only when bound. * src/w32proc.c (sys_spawnve): Pass extra argument to openp. * src/w32font.c (fill_in_logfont): Use font scaling factor, not the display scaling factor. * src/w32.c (check_windows_init_file): Pass extra argument to openp. * src/verbose.mk.in (AM_V_JAVAC, AM_V_DX, AM_V_AAPT) (AM_V_ZIPALIGN, AM_V_SILENT): New variables. * src/textconv.h (struct textconv_interface): New `point_changed', `compose_region_changed' and `notify_conversion'. Add declarations for new functions. * src/textconv.c (TEXTCONV_DEBUG): New macro. (suppress_conversion_count): New variable. (enum textconv_batch_edit_flags): New flag. (copy_buffer): Don't overwrite text before the gap with the text after. (get_mark, select_window): New functions. (textconv_query): New argument FLAGS. Contingent upon its value, use the previous point or mark or skip the conversion region. (sync_overlay, record_buffer_change, reset_frame_state) (detect_conversion_events, restore_selected_window) (really_commit_text, really_finish_composing_text) (really_set_composing_region, really_delete_composing_text) (really_request_point_update, really_set_point_and_mark) (complete_edit): New functions. (struct complete_edit_check_context): New structure; store in it the result of editing operations. (complete_edit_check, handle_pending_conversion_events_1) (decrement_inside, handle_pending_conversion_events) (start_batch_edit, end_batch_edit, commit_text) (set_composing_text, textconv_set_point_and_mark) (request_point_update, textconv_barrier, get_extracted_text) (get_surrounding_text, conversion_disabled_p) (report_selected_window_change, report_point_change) (disable_text_conversion, resume_text_conversion) (register_textconv_interface, check_postponed_buffers) (postponed_buffers, Fset_text_conversion_style) (syms_of_textconv) : New functions, symbols and variables. * src/terminal.c (Fterminal_live_p): Return Qandroid if type is output_android. * src/termhooks.h (enum output_method): Add `output_android'. (struct terminal) : Add union constituent field for `android'. : Define on Android as well. (TERMINAL_FONT_CACHE) [HAVE_ANDROID]: Return the inappropriately named font cache field on Android. * src/term.c (string_cost, string_cost_one_line, per_line_cost) (calculate_costs, produce_glyphs, produce_glyphs, tty_capable_p) (tty_capable_p, device, init_tty, maybe_fatal) (delete_tty) [HAVE_ANDROID]: Exclude or turn these functions into vestiges. (Fsuspend_tty, Fresume_tty): Call `emacs_fclose' and always signal on Android. (Fresume_tty): Call `emacs_fdopen'. (Ftty__set_output_buffer_size) [HAVE_ANDROID]: Remove this function. (encode_terminal_code): Replace with a stub. (init_tty, delete_tty, maybe_fatal): Call `emacs_fclose'. (syms_of_term): Remove most unnecessary code on Android. : Always set this option on Android. * src/sysdep.c (init_standard_fds): Call emacs_fopen. (reset_sigio, widen_foreground_group): Define out on Android. (reset_sys_modes): Don't call either function on Android. (init_sigbus, handle_sigbus): New functions. (init_signals): Don't add user signals on Android. Register signal handlers for SIGBUS, and refrain from handling SIGSEGV. (emacs_fstatat): Wrap android_fstatat on Android. (sys_fstat, sys_faccessat): New function. (emacs_openat): Exclude this function when building libemacs.so. (emacs_open, emacs_open_noquit, emacs_fopen, emacs_close): Wrap functions defined in the Android filesystem emulation code. (emacs_fdopen, emacs_fclose, emacs_unlink, emacs_symlink) (emacs_rmdir, emacs_mkdir, emacs_renameat_noreplace, emacs_rename) (emacs_fchmodat): New wrappers for more of those functions. (close_output_streams): Placate the file descriptor sanitizer that's included with android. * src/sound.c (Fplay_sound_internal): Pass extra argument to openp. * src/sfntfont.h: * src/sfntfont.c: * src/sfntfont-android.c: * src/sfnt.h: * src/sfnt.c: New files. * src/scroll.c: Exclude the entire file on Android. * src/process.c: (allocate_pty): Call sys_faccessat, not faccessat. (Fmake_process): Call openp with an extra argument. (wait_reading_process_output): Call android_select. (Fprocess_send_eof): Don't call tcdrain if not present. (handle_child_signal): Write a comment describing a small, seldom encountered issue. * src/print.c (print_vectorlike): Don't print FONT_EXTRA_INDEX for font entities. * src/pdumper.c (Fdump_emacs_portable): Allow dumping in interactive Emacs's on Android, as this is performed within loadup.el. (dump_discard_mem): Use madvise if posix_advise is not present. (pdumper_load): Call sys_fstat, not fstat. (syms_of_pdumper) : Calculate the fingerprint for this Emacs executable and store it there. * src/menu.c (have_boxes): Android has boxes. (push_submenu_start, push_submenu_end): Define on Android. (single_menu_item): Produce submenus on Android as well. (x_popup_menu_1): Call EVENT_START, in contrast to duplicating its old functionality with calls to Fcar and XCDR. (Fx_popup_menu): Update documentation to reflect that touch screen events are now accepted as POSITION. * src/marker.c (set_marker_internal): Redisplay buffers when their mark changes, enabling changes to be reported to the IME. * src/lread.c (lread_fd, lread_fd_cmp, lread_fd_p, lread_close) (lread_fstat, lread_read_quit, lread_lseek, file_stream) (file_seek, file_stream_valid_p, file_stream_close) (file_stream_invalid, getc): New macros. Define to an implementation with file descriptors and file streams on systems other than Android 2.3+, and one using Android file descriptors on those systems. (USE_ANDROID_ASSETS): Define on Android 2.3+; (file_get_char): New function. (infile, skip_dyn_bytes, skip_dyn_eof, readbyte_from_stdio) (read_filtered_event, safe_to_load_version, close_infile_unwind): Implement in terms of those macros. (close_file_unwind_android_fd): New function. (Fload): Pass extra argument to `openp' and use Android file descriptors where possible. (Flocate_file_internal): Pass extra argument to `openp'. (maybe_swap_for_eln1): Call sys_fstat, not fstat. (openp): New arg PLATFORM; if supplied and opening a platform-specific file descriptor replacement is possible, place one there. (build_load_history): Fix typos in comments. (skip_lazy_string): Implement in terms of the aformentioned macros. * src/lisp.h: Add declarations for new functions. * src/keyboard.h (reading_key_sequence): Declare here. (EVENT_START): Treat touch screen events specially by returning the posn of their touch point. * src/keyboard.c (reading_key_sequence, menu_bar_touch_id): New variables. (command_loop_1): (read_menu_command): Pass false to read_key_sequence. (read_char): Update commentary. (readable_events): If text conversion events (edits from an input method) are queued, return 1. (kbd_buffer_get_event): If text conversion events exist, carry out the edits contained within. Then, generate a Qtext_conversion event. (lispy_function_keys, FUNCTION_KEY_OFFSET): Define function key array on Android. (coords_in_tab_bar_window): New function. (make_lispy_event) : Keep track of touches that fall into the confines of the tab bar, and include the tab bar item in their position lists. Moreover, retain and track the touch in C code if it's taking place within the menu bar. : Likewise for the tab bar; generate menu bar events if the touch ends on a menu item and was previously singled out for tracking. : Don't deliver this event if the frame is dead, or if it was identified for tracking since the only touch sequence that changed begun inside the menu bar. (handle_async_input): Call android_check_query_urgent. (handle_input_available_signal): Add memory fence. (parse_tool_bar_item): Handle `wrap' properties within tool bar items moving subsequent items onto a new row. (access_keymap_keyremap): New arguments START, END, KEYBUF. Set Qcurrent_key_remap_sequence around calls to the remap function. (keyremap_step): Pass the necessary information to access_keymap_keyremap. (restore_reading_key_sequence): New function. (read_key_sequence): Set `reading_key_sequence'. New arg DISABLE_TEXT_CONVERSION_P, which causes text conversion to be disabled as long as the key sequence is being read. Disable text conversion as well if a menu or function key prefix is read, insert imaginary prefix keys before touchscreen events within special areas of a frame. Don't insert prefix keys if input is being mocked, which transpires if the input is in actuality originating from a key translation map. (read_key_sequence_vs): New argument DISABLE_TEXT_CONVERSION. (Fread_key_sequence): New argument DISABLE_TEXT_CONVERSION. (Fopen_dribble_file): Use emacs_fclose. (head_table): Make touchscreen-begin and touchscreen-end events touchscreen events. (syms_of_keyboard) : New symbols. : New variables. * src/inotify.c (Finotify_add_watch): Detect and avoid watching special files that don't exist from the POV of inotify. * src/image.c (image_create_bitmap_from_data) (image_create_bitmap_from_file, free_bitmap_record) (prepare_image_for_display, image_clear_image_1) (image_clear_image_1, image_size_in_bytes, image_set_transform): (Create_Pixmap_From_Bitmap_Data, lookup_rgb_color) (image_to_emacs_colors, image_from_emacs_colors) (image_pixmap_draw_cross, image_disable_image): Implement on Android, reusing much of the X11 code. (matrix_identity, matrix_rotate, matrix_mirror_horizontal) (matrix_translate): New functions. (x_check_image_size, x_create_x_image_and_pixmap) (x_destroy_x_image, image_check_image_size) (image_create_x_image_and_pixmap_1, image_destroy_x_image) (gui_put_x_image, image_get_x_image, image_unget_x_image): Implement on Android. (image_find_image_fd): Return an Android file descriptor if possible. (close_android_fd): New function. (slurp_file): Accept `image_fds', defined to Android file descriptors. (xpm_load): Enable built-in XPM support on Android. (xbm_load, pbm_load, png_load_body, jpeg_load_body, gif_load) (webp_load, imagemagick_load_image, svg_load): Use image file descriptors on Android; these file descriptors may in fact represent compressed asset streams, and obviate the necessity of creating a new file descriptor for each asset image opened. (Fimage_transforms_p): Report rotate90 on Android. (image_types, syms_of_image): Enable built-in XPM support on Android. * src/fringe.c (init_fringe_bitmap): Bit swap bitmaps on Android, as on X. * src/frame.h (enum text_conversion_operation): New enumerator. (struct text_conversion_action, struct text_conversion_state): New variable. (struct frame): New fields `tool_bar_wraps_p' and `conversion'. Increase the width of `output_method'. : Add `android' field. : Define on Android as well. (fset_menu_bar_window): Define correctly, so that it's declared on non-X builds without external menu bars. (FRAME_ANDROID_P): Define macro. (FRAME_WINDOW_P) [HAVE_ANDROID]: Define to FRAME_ANDROID_P. (FRAME_RES): New macro. (MOUSE_HL_INFO): Define without referencing tty output data on Android, which doesn't have them. * src/frame.c (Fframep): Return `android' on Android systems. (Fwindow_system): Likewise. (make_frame): Clear text conversion state and `tool_bar_wraps_p'. (Fmake_terminal_frame): Signal that Android doesn't support text terminals. (delete_frame): Reset text conversion state prior to deleting the frame. (gui_display_get_resource): Don't call the resource hook on Android. (Fx_parse_geometry): Pacify compiler warning. (make_monitor_attribute_list): Don't always use SOURCE if nil. (syms_of_frame) : New symbol. : Don't default scroll bars to an enabled state on Android. * src/fontset.c (fontset_find_font): Tackle an unusual problem. * src/font.h (struct font_entity): New field `is_android'. (PT_PER_INCH): Define to 160.00 on Android. * src/font.c (font_make_entity): New function. (font_make_entity_android): New variant that sets `is_android' to true. (font_pixel_size, font_find_for_lface, font_open_for_lface) (Ffont_face_attributes, Fopen_font): Respect the distinction between frame text and display scales. * src/fns.c (Flocale_info): Silence compiler warning. * src/filelock.c (BOOT_TIME): Undefine BOOT_TIME when building libemacs.so (get_boot_time, rename_lock_file, create_lock_file) (current_lock_owner, make_lock_file_name, unlock_file): Employ wrappers for Android filesystem operations. * src/fileio.c (emacs_fd, emacs_fd_open, emacs_fd_close) (emacs_fd_read, emacs_fd_lseek, emacs_fd_fstat, emacs_fd_valid_p): New type and macros; define them to suitable values, akin to those in lread.c (check_vfs_filename): New function. (file_access_p): Call `sys_faccessat'. (close_file_unwind_emacs_fd): New function. (fclose_unwind): Call `emacs_fclose', not fclose. (file_name_directory): Export this function. (user_homedir): If PW->pw_dir is not set and its uid is the current user, call `android_get_home_directory'. (get_homedir): Call `android_get_home_directory' if PW->pw_dir is not set. (Fcopy_file, Fmake_directory_internal, Fdelete_directory_internal) (Fdelete_file, Frename_file, Fmake_symbolic_link, Faccess_file) (file_directory_p, file_accessible_directory_p, Fset_file_modes) (Fset_file_times, Ffile_newer_than_file_p, read_non_regular) (Finsert_file_contents, write_region) (Fverify_visited_file_modtime, Fset_visited_file_modtime) (do_auto_save_unwind): Make use of Android filesystem wrappers and file descriptors where possible. (Fadd_name_to_file): Prohibit creating links to and from files residing on Android special directories. (Ffile_system_info): Avoid compilation failure on Android, where Gnulib can't find out how to implement statfs. * src/epaths.in [HAVE_ANDROID && !ANDROID_STUBIFY]: Deface this file, so Makefile cannot change the hard-coded values within. * src/emacs.c (using_utf8): Correctly initialize mbstate_t on Android. (init_cmdargs): Pass extra argument to openp. (load_pdump): When building libemacs.so, use solely the file provided on the command line or as an argument to `android_emacs_init'. (load_seccomp): Call sys_fstat, not fstat. (main, android_emacs_init): Name `main' `android_emacs_init' when building libemacs.so, and accept an argument designating the dump file. (main): Initialize text conversion and Android. Don't presume that argv is NULL terminated. (Fkill_emacs, shut_down_emacs): Properly implement RESTART on Android. (syms_of_emacs) : Describe the possible value `android'. * src/emacs-module.c (MODULE_HANDLE_NONLOCAL_EXIT): Cease relying on GCC clean-up attribute extension. (MODULE_INTERNAL_CLEANUP): New macro. (module_make_global_ref, module_free_global_ref) (module_make_function, module_get_function_finalizer) (module_make_interactive, module_funcall, module_extract_integer) (module_extract_float, module_copy_string_contents) (module_get_user_ptr, module_set_user_ptr) (module_get_user_finalizer, module_set_user_finalizer) (module_vec_set, module_vec_size, module_process_input) (module_extract_big_integer, module_make_big_integer): Carry out necessary clean-up tasks using MODULE_HANDLE_NONLOCAL_EXIT. * src/editfns.c (Fuser_full_name): Call `android_user_full_name', as USER_FULL_NAME doesn't always work. * src/doc.c (doc_fd, doc_fd_p, doc_open, doc_read_quit) (doc_lseek): New types and macros, resembling those in lread.c. (get_doc_string, Fsnarf_documentation): Implement in terms of those macros, so as to use Android asset streams directly. * src/dispnew.c (clear_current_matrices, clear_desired_matrices) (allocate_matrices_for_window_redisplay, free_glyphs) (redraw_frame, update_frame, scrolling, update_frame_line): Disable support for text terminals when building for Android. (Fopen_termscript): Use emacs_fclose. (init_display_interactive): Set Vinitial_window_system to Qandroid, and lose if Emacs needs to create a text terminal. * src/dispextern.h (No_Cursor, Emacs_Rectangle, struct gui_box): New definitions. (struct glyph_string) : Define to the Android GC type. (HAVE_NATIVE_TRANSPHORMS): Define on Android. (struct image): New fields `ximg', `mask_img', as on X. (enum tool_bar_item_idx): New tool bar item property TOOL_BAR_ITEM_WRAP. * src/dired.c (emacs_dir, emacs_closedir, emacs_readdir): New typedef and definitions. (open_directory): Return emacs_dir; use android_opendir on Android, instead of at-funcs. (directory_files_internal_unwind): Call emacs_closedir. (read_dirent): Call emacs_readdir. (directory_files_internal, file_name_completion) (file_name_completion_dirp): Use Android wrappers for directories and files. (file_attributes): Abstain from openat on Android. * src/conf_post.h (MB_CUR_MAX): Define to REPLACEMENT_MB_CUR_MAX if necessary to counteract inept LLVM headers. * src/coding.h (from_unicode_buffer): Define if HAVE_ANDROID as well. * src/coding.c (from_unicode_buffer): Define on Android, creating a variant that understands UCS-16 extended into wchar_t. (syms_of_coding) : Define on Android. * src/charset.c (load_charset_map_from_file): Supply extra argument to openp, and call Emacs wrappers for fdopen and fclose. * src/callproc.c (get_current_directory): Return the home directory if ENCODED is a special directory. (delete_temp_file): Call emacs_unlink in lieu of unlink. (call_process): Use openp. (emacs_spawn): Use Android executable loader binary if needed and enabled. (init_callproc): Set Vshell_file_name to /system/bin/sh if libemacs.so. (syms_of_callproc) : New variables. Define to the names of the programs they respectively stand for. * src/callint.c (Fcall_interactively): Supply new argument in calls to Fread_key_sequence and Fread_key_sequence_vector. * src/buffer.h (struct buffer) : New bvar. (bset_text_conversion_style): New bvar setter. * src/buffer.c (init_buffer_once): Set the text conversion style. (syms_of_buffer) : Define new BLV. * src/androidvfs.c: * src/androidterm.h: * src/androidterm.c: * src/androidselect.c: * src/androidmenu.c: * src/androidgui.h: * src/androidfont.c: * src/androidfns.c: * src/android.h: * src/android.c: * src/android-emacs.c: * src/android-asset.h: New function. * src/alloc.c (cleanup_vector): Finalize Android font entities. (find_string_data_in_pure) [__i386__ && !__clang__]: On Android, compensate for a bug in the latest NDK GCC. (mark_pinned_symbols, android_make_lisp_symbol): Elude another bug in debuginfo generation with an almost nonsensical fix. (garbage_collect): Mark androidterm and sfntfont. (mark_frame): Mark text conversion actions and info. * src/Makefile.in (XCONFIGURE): New variable. If set, add srcdir to vpath. (hostlib): New variable, always defined to libgnu.a on the build machine. (GIF_CFLAGS, JPEG_CFLAGS, TIFF_CFLAGS, SQLITE3_CFLAGS) (LIBSELINUX_CFLAGS, ANDROID_OBJ, ANDROID_LIBS, ANDROID_LDFLAGS) (ANDROID_BUILD_CFLAGS, LIBGMP_CFLAGS): New variables. (CM_OBJ): Update commentary. (EMACS_CFLAGS): Add new compiler flags variables. (base_obj): Add ANDROID_OBJ. (SOME_MACHINE_OBJECTS): Add Android-related objects. (lisp.mk): Generate from its absolute file name. ($(lispsource)/international/charprop.el): Don't generate when building libemacs.so. ($(libsrc)/make-docfile$(EXEEXT) $(libsrc)/make-fingerprint$(EXEEXT)): Depend on libgnu.a on the build machine. (mostlyclean): Remove libemacs.so. (build-counter.c, libemacs.so, android-emacs): New targets. These targets are made from this Makefile copied to a subdirectory of `cross', and provide the Emacs library and an ancillary binary used by the Android port. * nt/mingw-cfg.site: * nt/gnulib-cfg.mk: Impede building Gnulib's vasnprintf* code. * msdos/sedlibmk.inp: * msdos/sedlibcf.inp: * msdos/sed3v2.inp: * msdos/sed1v2.inp: Fix the DJGPP build. * make-dist (possibly_non_vc_files): Add exec/configure and exec/config.h.in. * m4/ndk-build.m4: New file. * m4/getline.m4: * m4/getdelim.m4: * m4/asm-underscore.m4: Update from Gnulib. * lisp/wid-edit.el (widget-event-point): Treat touch screen events correctly. (widget-keymap): Map touchscreen-begin to widget-button-click. (widget-event-start): New function. (widget-button--check-and-call-button): (widget-button-click): Behave correctly when confronted by touch screen events. * lisp/version.el (android-read-build-system) (android_read_build_time): New functions. (emacs-build-system, emacs-repository-version-android) (emacs-repository-get-version): (emacs-repository-get-branch): Implement properly on Android, by reading a file generated during the packaging process. * lisp/touch-screen.el: New file, supplying support for translating raw touch screen events into gestures. * lisp/tool-bar.el (secondary-tool-bar-map): New defvar. (tool-bar--cache-key, tool-bar--secondary-cache-key): Make defsubsts. (tool-bar--flush-key): Flush caches for the secondary tool bar as well. (tool-bar-make-keymap, tool-bar-make-keymap-1): Append the secondary tool bar map below the primary tool bar map. (modifier-bar-modifier-list): New variable. (tool-bar-apply-modifiers, modifier-bar-button) (tool-bar-event-apply-alt-modifier) (tool-bar-event-apply-super-modifier) (tool-bar-event-apply-hyper-modifier) (tool-bar-event-apply-shift-modifier) (tool-bar-event-apply-control-modifier) (tool-bar-event-apply-meta-modifier, modifier-bar-available-p) (modifier-bar-mode): New functions. * lisp/textmodes/text-mode.el (text-mode): Set text-conversion-style to t. * lisp/textmodes/reftex-global.el (reftex-create-tags-file): Use etags-program-name to provide the name of the etags program. * lisp/textmodes/conf-mode.el (conf-mode-initialize): Enable text conversion. * lisp/textmodes/artist.el (artist-figlet-get-font-list): Use /system/bin/sh on Android. * lisp/term/android-win.el: New file. * lisp/term.el (term-mode): Always display the on screen keyboard. (term-exec-1): Use /system/bin/sh on Android. * lisp/tab-line.el (tab-line-tab-map) (tab-line-new-tab) (tab-line-select-tab) (tab-line-close-tab) (tab-line-track-tap) (tab-line-event-start): Improve support for touch screen events. * lisp/tab-bar.el (tab-bar-mouse-context-menu): (tab-bar-map): Likewise. (tab-bar-handle-timeout, tab-bar-touchscreen-begin): New functions. * lisp/subr.el (event-start): Don't return nonsense if EVENT is a touchscreen event. (event-end): Likewise. (read-key): Disable text conversion within read-key-sequence-vector. (read-char-choice-with-read-key): Display the on screen keyboard. (read-char-from-minibuffer): Disable text conversion. (use-dialog-box-p): Prefer dialog boxes on Android. (y-or-n-p): Disable text conversion properly under all three modes of operation. * lisp/startup.el (android-fonts-enumerated): New variable. (normal-top-level): Load system fonts on Android. * lisp/speedbar.el (speedbar-fetch-etags-command): Use etags-program-name instead of hard-coding `etags'. * lisp/simple.el (normal-erase-is-backspace-setup-frame): Return true on Android. (event-apply-modifier): Correctly apply Shift and Control modifiers to keys with other modifiers. (undo-auto-amalgamate): Mention analyze-text-conversion wrt being an amalgamating command. * lisp/shell.el (shell--command-completion-data): Don't lose if PATH contains an inaccessible directory. * lisp/progmodes/prog-mode.el (prog-mode): Enable text conversion. * lisp/progmodes/cperl-mode.el (cperl-etags): Don't hard-code etags, employ etags-program-name instead. * lisp/progmodes/cc-mode.el (c-initialize-cc-mode): Initialize text conversion hook. * lisp/progmodes/cc-cmds.el (c-post-text-conversion): New function. Do electric characters. * lisp/play/gamegrid.el (gamegrid-setup-default-font): Don't crash if the display resolution is too high. * lisp/play/dunnet.el (text-conversion-style): * lisp/play/doctor.el (doctor-mode): Enable text conversion. * lisp/pixel-scroll.el (pixel-scroll-precision-scroll-down-page) (pixel-scroll-precision-scroll-Up-page): Make autoloads. * lisp/org/org-ctags.el (org-ctags-path-to-ctags): Use ctags-program-name, not ctags. * lisp/obsolete/terminal.el (terminal-emulator): Start /system/bin/sh, not /bin/sh. * lisp/net/tramp.el (tramp-encoding-shell): Use /system/bin/sh on Android. * lisp/net/eww.el (eww-form-submit, eww-form-file) (eww-form-checkbox, eww-form-select): Define these faces on Android as well. * lisp/net/browse-url.el (browse-url-default-browser) (browse-url--browser-defcustom-type): Specify on Android. (browse-url-android-share, browse-url-default-android-browser): New option and function. * lisp/mwheel.el (mouse-wheel-down-event, mouse-wheel-up-event) (mouse-wheel-left-event, mouse-wheel-right-event): Define suitably on Android. * lisp/mouse.el (minor-mode-menu-from-indicator): New argument EVENT. Use it for positioning the menu. (mouse-minor-mode-menu): Pass EVENT to that function. * lisp/minibuffer.el (clear-minibuffer-message): Don't clear the message if `touch-screen-preview-select' may be underway. (minibuffer-mode): Enable text conversion. (minibuffer-setup-on-screen-keyboard) (minibuffer-exit-on-screen-keyboard): New functions. * lisp/menu-bar.el (menu-bar-close-window): New option. (menu-bar-edit-menu): Bind execute-extended-command to a menu item. (kill-this-buffer, kill-this-buffer-enabled-p): Respect menu-bar-close-window. * lisp/mail/rmail.el (rmail-autodetect, rmail-insert-inbox-text): Don't hard-code the name of movemail; rather, use movemail-program-name. * lisp/mail/emacsbug.el (emacs-build-description): Insert the Android version and manufacturer. * lisp/ls-lisp.el (ls-lisp-use-insert-directory-program): Default to off on Android. * lisp/loadup.el: Set load-list to empty load list after startup; dump the first time Emacs starts, and load Android related miscellanea. * lisp/isearch.el (isearch-text-conversion-style): New variable. (isearch-mode, isearch-done): Display the OSK, then temporarily disable and restore the on screen keyboard. * lisp/international/mule-cmds.el (set-coding-system-map): Update menu definition for Android. * lisp/international/fontset.el (script-representative-chars) (setup-default-fontset): Improve detection of CJK fonts. * lisp/image/wallpaper.el: Fix compiler warning. * lisp/ielm.el (inferior-emacs-lisp-mode): Don't hard-code name of hexl, replacing that with hexl-program-name. * lisp/htmlfontify.el (hfy-etags-bin): Replace hard-coded Emacs with etags-program-name. * lisp/hexl.el (hexl-program): Replace hard-coded hexl. * lisp/help-macro.el (make-help-screen): Display the on screen keyboard and disable text conversion prior to reading options. * lisp/gnus/mail-source.el (mail-source-movemail-program): Replace hard-coded movemail with movemail-program-name. * lisp/gnus/gnus-score.el (gnus-read-char): New function. (gnus-summary-increase-score): Use a dialog box to display these options on Android. * lisp/frame.el (frame-geometry, frame-edges) (mouse-absolute-pixel-position, set-mouse-absolute-pixel-position) (frame-list-z-order, frame-restack, display-mouse-p) (display-popup-menus-p, display-graphic-p, display-symbol-keys-p) (display-screens, display-pixel-height, display-pixel-width) (display-mm-height, display-mm-width, display-backing-store) (display-save-under, display-planes, display-color-cells) (display-visual-class, display-monitor-attributes-list): Implement window system specific functions on Android. * lisp/files.el (basic-save-buffer): Allow files to exist without a parent directory. * lisp/faces.el (tool-bar): Use default definition on Android. * lisp/emacs-lisp/eldoc.el (eldoc-add-command-completions): Add touch-screen-handle-touch and analyze-text-conversion. * lisp/elec-pair.el (electric-pair-analyze-conversion): New function. * lisp/doc-view.el (doc-view-menu): Improve menu. (doc-view-tool-bar-map): Add a new tool bar for Doc View. (doc-view-new-search): New command. (doc-view-mode): Enable that new tool bar. * lisp/dired-aux.el (dired-do-chxxx, dired-do-chmod) (dired-do-print, dired-do-shell-command, dired-do-compress-to) (dired-do-create-files, dired-do-rename, dired-do-isearch) (dired-do-isearch-regexp, dired-do-search) (dired-do-query-replace-regexp, dired-do-find-regexp) (dired-vc-next-action): Disable ``click to select'' after running this command. * lisp/dired.el (dired-insert-set-properties): Attach click-to-select keymap to file names if necessary. (dired-mode-map): Bind `touchscreen-hold' to click to select mode. (dired-post-do-command): New function. (dired-do-delete): Call it. (dired-mark-for-click, dired-enable-click-to-select-mode): New functions. (dired-click-to-select-mode): New minor mode. * lisp/cus-edit.el (custom-button-mouse, custom-button-pressed) (custom-display): Define faces to their default values on Android. * lisp/comint.el (comint-mode): Enable text conversion. * lisp/cedet/semantic/db-ebrowse.el (semanticdb-create-ebrowse-database): Replace fixed ebrowse with ebrowse-program-name. * lisp/calc/calc.el (calc-mode): Display the on screen keyboard. (calc): Insist on displaying the on screen keyboard. * lisp/button.el (button-map): Bind touch screen events to push-button. (push-button): Deal with touch screen events. * lisp/bindings.el (cut, paste, cut, text-conversion): New bindings. * lisp/battery.el (battery-status-function): Use `battery-android'. (battery-android): New function. * lib/gnulib.mk.in: * lib/getline.c: * lib/getdelim.c: * lib/Makefile.in: Update from Gnulib. * lib-src/emacsclient.c (decode_options): Set `alt_display' to `android'. * lib-src/asset-directory-tool.c: New file. * lib-src/Makefile.in: Adapt for cross-compilation. * java/res/xml/preferences.xml: * java/res/values/style.xml: * java/res/values/strings.xml: * java/res/values/bool.xml: * java/res/values-v29/style.xml: * java/res/values-v24/bool.xml: * java/res/values-v19/bool.xml: * java/res/values-v14/style.xml: * java/res/values-v11/style.xml: * java/org/gnu/emacs/EmacsWindowAttachmentManager.java: * java/org/gnu/emacs/EmacsWindow.java: * java/org/gnu/emacs/EmacsView.java: * java/org/gnu/emacs/EmacsThread.java: * java/org/gnu/emacs/EmacsSurfaceView.java: * java/org/gnu/emacs/EmacsService.java: * java/org/gnu/emacs/EmacsSdk8Clipboard.java: * java/org/gnu/emacs/EmacsSdk7FontDriver.java: * java/org/gnu/emacs/EmacsSdk23FontDriver.java: * java/org/gnu/emacs/EmacsSdk11Clipboard.java: * java/org/gnu/emacs/EmacsSafThread.java: * java/org/gnu/emacs/EmacsPreferencesActivity.java: * java/org/gnu/emacs/EmacsPixmap.java: * java/org/gnu/emacs/EmacsOpenActivity.java: * java/org/gnu/emacs/EmacsNoninteractive.java: * java/org/gnu/emacs/EmacsNative.java: * java/org/gnu/emacs/EmacsMultitaskActivity.java: * java/org/gnu/emacs/EmacsLauncherPreferencesActivity.java: * java/org/gnu/emacs/EmacsInputConnection.java: * java/org/gnu/emacs/EmacsHolder.java: * java/org/gnu/emacs/EmacsHandleObject.java: * java/org/gnu/emacs/EmacsGC.java: * java/org/gnu/emacs/EmacsFontDriver.java: * java/org/gnu/emacs/EmacsFillRectangle.java: * java/org/gnu/emacs/EmacsFillPolygon.java: * java/org/gnu/emacs/EmacsDrawable.java: * java/org/gnu/emacs/EmacsDrawRectangle.java: * java/org/gnu/emacs/EmacsDrawPoint.java: * java/org/gnu/emacs/EmacsDrawLine.java: * java/org/gnu/emacs/EmacsDocumentsProvider.java: * java/org/gnu/emacs/EmacsDirectoryEntry.java: * java/org/gnu/emacs/EmacsDialogButtonLayout.java: * java/org/gnu/emacs/EmacsDialog.java: * java/org/gnu/emacs/EmacsCursor.java: * java/org/gnu/emacs/EmacsContextMenu.java: * java/org/gnu/emacs/EmacsClipboard.java: * java/org/gnu/emacs/EmacsApplication.java: * java/org/gnu/emacs/EmacsActivity.java: * java/debug.sh: * java/README: * java/Makefile.in: * java/INSTALL: * java/AndroidManifest.xml.in: * exec/trace.c: * exec/test.c: * exec/mipsfpu.h: * exec/mipsfpu.c: * exec/mipsel-user.h: * exec/loader-x86_64.s: * exec/loader-x86.s: * exec/loader-mipsel.s: * exec/loader-mips64el.s: * exec/loader-armeabi.s: * exec/loader-aarch64.s: * exec/install-sh: * exec/exec1.c: * exec/exec.h: * exec/exec.c: * exec/deps.mk: * exec/configure.ac: * exec/config.sub: * exec/config.h.in: * exec/config.guess: * exec/config-mips.m4.in: * exec/README: * exec/Makefile.in: * etc/images/last-page.xpm: New files. * etc/PROBLEMS: Expound upon problems with font instructing on Android. * etc/NEWS: Announce changes. * etc/MACHINES: Describe support for Android. * etc/DEBUG: Illustrate the steps to debug Emacs on Android. * doc/lispref/processes.texi (Subprocess Creation): * doc/lispref/os.texi (System Environment): * doc/lispref/keymaps.texi (Translation Keymaps): (Extended Menu Items): (Tool Bar): * doc/lispref/frames.texi (Frames): (Frame Layout): (Font and Color Parameters): (Pop-Up Menus): (Window System Selections): * doc/lispref/elisp.texi (Top): * doc/lispref/display.texi (Defining Faces): (Window Systems): * doc/lispref/commands.texi (Touchscreen Events): (Touchscreen Events): (Misc Events): (Key Sequence Input): * doc/emacs/windows.texi (Tab Line): * doc/emacs/input.texi: * doc/emacs/frames.texi (Tool Bars): (Tab Bars): * doc/emacs/emacs.texi (Top): * doc/emacs/dired.texi (Marks vs Flags): * doc/emacs/android.texi: * doc/emacs/Makefile.in (EMACSSOURCES): Update the documentation to properly describe changes effected. * cross/verbose.mk.android: * cross/ndk-build/ndk-resolve.mk: * cross/ndk-build/ndk-prebuilt-static-library.mk: * cross/ndk-build/ndk-prebuilt-shared-library.mk: * cross/ndk-build/ndk-clear-vars.mk: * cross/ndk-build/ndk-build.mk.in: * cross/ndk-build/ndk-build-static-library.mk: * cross/ndk-build/ndk-build-shared-library.mk: * cross/ndk-build/ndk-build-executable.mk: * cross/ndk-build/README: * cross/ndk-build/Makefile.in: * cross/langinfo.h: * cross/README: * cross/Makefile.in: New files. * configure.ac: Configure Emacs for cross-compilation on Android. * build-aux/ndk-module-extract.awk: * build-aux/ndk-build-helper.mk: * build-aux/ndk-build-helper-4.mk: * build-aux/ndk-build-helper-3.mk: * build-aux/ndk-build-helper-2.mk: * build-aux/ndk-build-helper-1.mk: * build-aux/makecounter.sh: New file. * autogen.sh: Autogen in exec as well. * admin/merge-gnulib (GNULIB_MODULES): Add getline, stpncpy and strnlen. Clean lib. * README: * Makefile.in: * INSTALL: Update for Android. * .dir-locals.el (c-mode): Add a few new types. commit 9a9f73041d09d2da7ed562c7ffae0d9519562fba Author: Po Lu Date: Mon Aug 7 08:30:42 2023 +0800 ; ChangeLog.android: Update. diff --git a/ChangeLog.android b/ChangeLog.android index 54fbfc6e3b1..3cb88b6f10c 100644 --- a/ChangeLog.android +++ b/ChangeLog.android @@ -1,5 +1,7 @@ 2023-08-07 Po Lu + * nt/mingw-cfg.site: Remove additions for Gnulib printf. + * m4, lib: Update from Gnulib. * msdos/sedlibmk.inp: Remove variables deleted as part of previous commit e2d5c50f85922cbfccc54defffd26223b0bf6fde Author: Po Lu Date: Mon Aug 7 08:19:40 2023 +0800 * nt/mingw-cfg.site: Remove additions for Gnulib printf. diff --git a/nt/mingw-cfg.site b/nt/mingw-cfg.site index f78ee525bf1..9962cc46642 100644 --- a/nt/mingw-cfg.site +++ b/nt/mingw-cfg.site @@ -173,21 +173,3 @@ gl_cv_func_nanosleep=yes # Suppress configure-time diagnostic from unnecessary libxattr check, # as xattr will not be supported here. enable_xattr=no -# Don't build gnulib printf either. -gl_cv_func_printf_sizes_c99=yes -gl_cv_func_printf_sizes_c23=yes -gl_cv_func_printf_long_double=yes -gl_cv_func_printf_infinite_long_double=yes -gl_cv_func_printf_directive_a=yes -gl_cv_func_printf_directive_b=yes -gl_cv_func_printf_directive_f=yes -gl_cv_func_printf_directive_n=yes -gl_cv_func_printf_directive_ls=yes -gl_cv_func_printf_directive_lc=yes -gl_cv_func_printf_positions=yes -gl_cv_func_printf_flag_grouping=yes -gl_cv_func_printf_flag_leftadjust=yes -gl_cv_func_printf_flag_zero=yes -gl_cv_func_printf_precision=yes -gl_cv_func_printf_enomem=yes -ac_cv_func_vasprintf=yes commit 76cac8ae6f600be34f83bd3aaf9b38355eab773e Author: Po Lu Date: Mon Aug 7 08:14:38 2023 +0800 Update from Gnulib, remove printf-posix * m4, lib: Update from Gnulib. * msdos/sedlibmk.inp: Remove variables deleted as part of previous change. * admin/merge-gnulib (GNULIB_MODULES): Remove vasprintf and printf-posix. diff --git a/ChangeLog.android b/ChangeLog.android index 45d245336ec..54fbfc6e3b1 100644 --- a/ChangeLog.android +++ b/ChangeLog.android @@ -1,3 +1,13 @@ +2023-08-07 Po Lu + + * m4, lib: Update from Gnulib. + + * msdos/sedlibmk.inp: Remove variables deleted as part of previous + change. + + * admin/merge-gnulib (GNULIB_MODULES): Remove vasprintf and + printf-posix. + 2023-08-06 Po Lu * java/org/gnu/emacs/EmacsService.java (readDirectoryEntry): Fix diff --git a/admin/merge-gnulib b/admin/merge-gnulib index a0dbaae1519..b533f69cceb 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -42,7 +42,7 @@ GNULIB_MODULES= manywarnings memmem-simple mempcpy memrchr memset_explicit minmax mkostemp mktime nanosleep nproc nstrftime - pathmax pipe2 printf-posix vasprintf-posix pselect pthread_sigmask + pathmax pipe2 pselect pthread_sigmask qcopy-acl readlink readlinkat regex sig2str sigdescr_np socklen stat-time std-gnu11 stdbool stdckdint stddef stdio stpcpy stpncpy strnlen strnlen strtoimax symlink sys_stat sys_time diff --git a/lib/asnprintf.c b/lib/asnprintf.c deleted file mode 100644 index f4861bf8457..00000000000 --- a/lib/asnprintf.c +++ /dev/null @@ -1,34 +0,0 @@ -/* Formatted output to strings. - Copyright (C) 1999, 2002, 2006, 2009-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -/* Specification. */ -#include "vasnprintf.h" - -#include - -char * -asnprintf (char *resultbuf, size_t *lengthp, const char *format, ...) -{ - va_list args; - char *result; - - va_start (args, format); - result = vasnprintf (resultbuf, lengthp, format, args); - va_end (args); - return result; -} diff --git a/lib/asprintf.c b/lib/asprintf.c deleted file mode 100644 index ba58e06481f..00000000000 --- a/lib/asprintf.c +++ /dev/null @@ -1,39 +0,0 @@ -/* Formatted output to strings. - Copyright (C) 1999, 2002, 2006-2007, 2009-2023 Free Software Foundation, - Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -/* Specification. */ -#ifdef IN_LIBASPRINTF -# include "vasprintf.h" -#else -# include -#endif - -#include - -int -asprintf (char **resultp, const char *format, ...) -{ - va_list args; - int result; - - va_start (args, format); - result = vasprintf (resultp, format, args); - va_end (args); - return result; -} diff --git a/lib/float+.h b/lib/float+.h deleted file mode 100644 index e7531e46a38..00000000000 --- a/lib/float+.h +++ /dev/null @@ -1,147 +0,0 @@ -/* Supplemental information about the floating-point formats. - Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. - Written by Bruno Haible , 2007. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#ifndef _FLOATPLUS_H -#define _FLOATPLUS_H - -#include -#include - -/* Number of bits in the mantissa of a floating-point number, including the - "hidden bit". */ -#if FLT_RADIX == 2 -# define FLT_MANT_BIT FLT_MANT_DIG -# define DBL_MANT_BIT DBL_MANT_DIG -# define LDBL_MANT_BIT LDBL_MANT_DIG -#elif FLT_RADIX == 4 -# define FLT_MANT_BIT (FLT_MANT_DIG * 2) -# define DBL_MANT_BIT (DBL_MANT_DIG * 2) -# define LDBL_MANT_BIT (LDBL_MANT_DIG * 2) -#elif FLT_RADIX == 16 -# define FLT_MANT_BIT (FLT_MANT_DIG * 4) -# define DBL_MANT_BIT (DBL_MANT_DIG * 4) -# define LDBL_MANT_BIT (LDBL_MANT_DIG * 4) -#endif - -/* Bit mask that can be used to mask the exponent, as an unsigned number. */ -#define FLT_EXP_MASK ((FLT_MAX_EXP - FLT_MIN_EXP) | 7) -#define DBL_EXP_MASK ((DBL_MAX_EXP - DBL_MIN_EXP) | 7) -#define LDBL_EXP_MASK ((LDBL_MAX_EXP - LDBL_MIN_EXP) | 7) - -/* Number of bits used for the exponent of a floating-point number, including - the exponent's sign. */ -#define FLT_EXP_BIT \ - (FLT_EXP_MASK < 0x100 ? 8 : \ - FLT_EXP_MASK < 0x200 ? 9 : \ - FLT_EXP_MASK < 0x400 ? 10 : \ - FLT_EXP_MASK < 0x800 ? 11 : \ - FLT_EXP_MASK < 0x1000 ? 12 : \ - FLT_EXP_MASK < 0x2000 ? 13 : \ - FLT_EXP_MASK < 0x4000 ? 14 : \ - FLT_EXP_MASK < 0x8000 ? 15 : \ - FLT_EXP_MASK < 0x10000 ? 16 : \ - FLT_EXP_MASK < 0x20000 ? 17 : \ - FLT_EXP_MASK < 0x40000 ? 18 : \ - FLT_EXP_MASK < 0x80000 ? 19 : \ - FLT_EXP_MASK < 0x100000 ? 20 : \ - FLT_EXP_MASK < 0x200000 ? 21 : \ - FLT_EXP_MASK < 0x400000 ? 22 : \ - FLT_EXP_MASK < 0x800000 ? 23 : \ - FLT_EXP_MASK < 0x1000000 ? 24 : \ - FLT_EXP_MASK < 0x2000000 ? 25 : \ - FLT_EXP_MASK < 0x4000000 ? 26 : \ - FLT_EXP_MASK < 0x8000000 ? 27 : \ - FLT_EXP_MASK < 0x10000000 ? 28 : \ - FLT_EXP_MASK < 0x20000000 ? 29 : \ - FLT_EXP_MASK < 0x40000000 ? 30 : \ - FLT_EXP_MASK <= 0x7fffffff ? 31 : \ - 32) -#define DBL_EXP_BIT \ - (DBL_EXP_MASK < 0x100 ? 8 : \ - DBL_EXP_MASK < 0x200 ? 9 : \ - DBL_EXP_MASK < 0x400 ? 10 : \ - DBL_EXP_MASK < 0x800 ? 11 : \ - DBL_EXP_MASK < 0x1000 ? 12 : \ - DBL_EXP_MASK < 0x2000 ? 13 : \ - DBL_EXP_MASK < 0x4000 ? 14 : \ - DBL_EXP_MASK < 0x8000 ? 15 : \ - DBL_EXP_MASK < 0x10000 ? 16 : \ - DBL_EXP_MASK < 0x20000 ? 17 : \ - DBL_EXP_MASK < 0x40000 ? 18 : \ - DBL_EXP_MASK < 0x80000 ? 19 : \ - DBL_EXP_MASK < 0x100000 ? 20 : \ - DBL_EXP_MASK < 0x200000 ? 21 : \ - DBL_EXP_MASK < 0x400000 ? 22 : \ - DBL_EXP_MASK < 0x800000 ? 23 : \ - DBL_EXP_MASK < 0x1000000 ? 24 : \ - DBL_EXP_MASK < 0x2000000 ? 25 : \ - DBL_EXP_MASK < 0x4000000 ? 26 : \ - DBL_EXP_MASK < 0x8000000 ? 27 : \ - DBL_EXP_MASK < 0x10000000 ? 28 : \ - DBL_EXP_MASK < 0x20000000 ? 29 : \ - DBL_EXP_MASK < 0x40000000 ? 30 : \ - DBL_EXP_MASK <= 0x7fffffff ? 31 : \ - 32) -#define LDBL_EXP_BIT \ - (LDBL_EXP_MASK < 0x100 ? 8 : \ - LDBL_EXP_MASK < 0x200 ? 9 : \ - LDBL_EXP_MASK < 0x400 ? 10 : \ - LDBL_EXP_MASK < 0x800 ? 11 : \ - LDBL_EXP_MASK < 0x1000 ? 12 : \ - LDBL_EXP_MASK < 0x2000 ? 13 : \ - LDBL_EXP_MASK < 0x4000 ? 14 : \ - LDBL_EXP_MASK < 0x8000 ? 15 : \ - LDBL_EXP_MASK < 0x10000 ? 16 : \ - LDBL_EXP_MASK < 0x20000 ? 17 : \ - LDBL_EXP_MASK < 0x40000 ? 18 : \ - LDBL_EXP_MASK < 0x80000 ? 19 : \ - LDBL_EXP_MASK < 0x100000 ? 20 : \ - LDBL_EXP_MASK < 0x200000 ? 21 : \ - LDBL_EXP_MASK < 0x400000 ? 22 : \ - LDBL_EXP_MASK < 0x800000 ? 23 : \ - LDBL_EXP_MASK < 0x1000000 ? 24 : \ - LDBL_EXP_MASK < 0x2000000 ? 25 : \ - LDBL_EXP_MASK < 0x4000000 ? 26 : \ - LDBL_EXP_MASK < 0x8000000 ? 27 : \ - LDBL_EXP_MASK < 0x10000000 ? 28 : \ - LDBL_EXP_MASK < 0x20000000 ? 29 : \ - LDBL_EXP_MASK < 0x40000000 ? 30 : \ - LDBL_EXP_MASK <= 0x7fffffff ? 31 : \ - 32) - -/* Number of bits used for a floating-point number: the mantissa (not - counting the "hidden bit", since it may or may not be explicit), the - exponent, and the sign. */ -#define FLT_TOTAL_BIT ((FLT_MANT_BIT - 1) + FLT_EXP_BIT + 1) -#define DBL_TOTAL_BIT ((DBL_MANT_BIT - 1) + DBL_EXP_BIT + 1) -#define LDBL_TOTAL_BIT ((LDBL_MANT_BIT - 1) + LDBL_EXP_BIT + 1) - -/* Number of bytes used for a floating-point number. - This can be smaller than the 'sizeof'. For example, on i386 systems, - 'long double' most often have LDBL_MANT_BIT = 64, LDBL_EXP_BIT = 16, hence - LDBL_TOTAL_BIT = 80 bits, i.e. 10 bytes of consecutive memory, but - sizeof (long double) = 12 or = 16. */ -#define SIZEOF_FLT ((FLT_TOTAL_BIT + CHAR_BIT - 1) / CHAR_BIT) -#define SIZEOF_DBL ((DBL_TOTAL_BIT + CHAR_BIT - 1) / CHAR_BIT) -#define SIZEOF_LDBL ((LDBL_TOTAL_BIT + CHAR_BIT - 1) / CHAR_BIT) - -/* Verify that SIZEOF_FLT <= sizeof (float) etc. */ -typedef int verify_sizeof_flt[SIZEOF_FLT <= sizeof (float) ? 1 : -1]; -typedef int verify_sizeof_dbl[SIZEOF_DBL <= sizeof (double) ? 1 : - 1]; -typedef int verify_sizeof_ldbl[SIZEOF_LDBL <= sizeof (long double) ? 1 : - 1]; - -#endif /* _FLOATPLUS_H */ diff --git a/lib/float.c b/lib/float.c deleted file mode 100644 index f81ff33d3b7..00000000000 --- a/lib/float.c +++ /dev/null @@ -1,33 +0,0 @@ -/* Auxiliary definitions for . - Copyright (C) 2011-2023 Free Software Foundation, Inc. - Written by Bruno Haible , 2011. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -/* Specification. */ -#include - -#if (defined _ARCH_PPC || defined _POWER) && (defined _AIX || defined __linux__) && (LDBL_MANT_DIG == 106) && defined __GNUC__ -const union gl_long_double_union gl_LDBL_MAX = - { { DBL_MAX, DBL_MAX / (double)134217728UL / (double)134217728UL } }; -#elif defined __i386__ -const union gl_long_double_union gl_LDBL_MAX = - { { 0xFFFFFFFF, 0xFFFFFFFF, 32766 } }; -#else -/* This declaration is solely to ensure that after preprocessing - this file is never empty. */ -typedef int dummy; -#endif diff --git a/lib/float.in.h b/lib/float.in.h deleted file mode 100644 index bf2c502c7f5..00000000000 --- a/lib/float.in.h +++ /dev/null @@ -1,194 +0,0 @@ -/* A correct . - - Copyright (C) 2007-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#ifndef _@GUARD_PREFIX@_FLOAT_H - -#if __GNUC__ >= 3 -@PRAGMA_SYSTEM_HEADER@ -#endif -@PRAGMA_COLUMNS@ - -/* The include_next requires a split double-inclusion guard. */ -#@INCLUDE_NEXT@ @NEXT_FLOAT_H@ - -#ifndef _@GUARD_PREFIX@_FLOAT_H -#define _@GUARD_PREFIX@_FLOAT_H - -/* 'long double' properties. */ - -#if defined __i386__ && (defined __BEOS__ || defined __OpenBSD__) -/* Number of mantissa units, in base FLT_RADIX. */ -# undef LDBL_MANT_DIG -# define LDBL_MANT_DIG 64 -/* Number of decimal digits that is sufficient for representing a number. */ -# undef LDBL_DIG -# define LDBL_DIG 18 -/* x-1 where x is the smallest representable number > 1. */ -# undef LDBL_EPSILON -# define LDBL_EPSILON 1.0842021724855044340E-19L -/* Minimum e such that FLT_RADIX^(e-1) is a normalized number. */ -# undef LDBL_MIN_EXP -# define LDBL_MIN_EXP (-16381) -/* Maximum e such that FLT_RADIX^(e-1) is a representable finite number. */ -# undef LDBL_MAX_EXP -# define LDBL_MAX_EXP 16384 -/* Minimum positive normalized number. */ -# undef LDBL_MIN -# define LDBL_MIN 3.3621031431120935063E-4932L -/* Maximum representable finite number. */ -# undef LDBL_MAX -# define LDBL_MAX 1.1897314953572317650E+4932L -/* Minimum e such that 10^e is in the range of normalized numbers. */ -# undef LDBL_MIN_10_EXP -# define LDBL_MIN_10_EXP (-4931) -/* Maximum e such that 10^e is in the range of representable finite numbers. */ -# undef LDBL_MAX_10_EXP -# define LDBL_MAX_10_EXP 4932 -#endif - -/* On FreeBSD/x86 6.4, the 'long double' type really has only 53 bits of - precision in the compiler but 64 bits of precision at runtime. See - . */ -#if defined __i386__ && (defined __FreeBSD__ || defined __DragonFly__) -/* Number of mantissa units, in base FLT_RADIX. */ -# undef LDBL_MANT_DIG -# define LDBL_MANT_DIG 64 -/* Number of decimal digits that is sufficient for representing a number. */ -# undef LDBL_DIG -# define LDBL_DIG 18 -/* x-1 where x is the smallest representable number > 1. */ -# undef LDBL_EPSILON -# define LDBL_EPSILON 1.084202172485504434007452800869941711426e-19L /* 2^-63 */ -/* Minimum e such that FLT_RADIX^(e-1) is a normalized number. */ -# undef LDBL_MIN_EXP -# define LDBL_MIN_EXP (-16381) -/* Maximum e such that FLT_RADIX^(e-1) is a representable finite number. */ -# undef LDBL_MAX_EXP -# define LDBL_MAX_EXP 16384 -/* Minimum positive normalized number. */ -# undef LDBL_MIN -# define LDBL_MIN 3.362103143112093506262677817321752E-4932L /* = 0x1p-16382L */ -/* Maximum representable finite number. */ -# undef LDBL_MAX -/* LDBL_MAX is represented as { 0xFFFFFFFF, 0xFFFFFFFF, 32766 }. - But the largest literal that GCC allows us to write is - 0x0.fffffffffffff8p16384L = { 0xFFFFF800, 0xFFFFFFFF, 32766 }. - So, define it like this through a reference to an external variable - - const unsigned int LDBL_MAX[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 32766 }; - extern const long double LDBL_MAX; - - Unfortunately, this is not a constant expression. */ -# if !GNULIB_defined_long_double_union -union gl_long_double_union - { - struct { unsigned int lo; unsigned int hi; unsigned int exponent; } xd; - long double ld; - }; -# define GNULIB_defined_long_double_union 1 -# endif -extern const union gl_long_double_union gl_LDBL_MAX; -# define LDBL_MAX (gl_LDBL_MAX.ld) -/* Minimum e such that 10^e is in the range of normalized numbers. */ -# undef LDBL_MIN_10_EXP -# define LDBL_MIN_10_EXP (-4931) -/* Maximum e such that 10^e is in the range of representable finite numbers. */ -# undef LDBL_MAX_10_EXP -# define LDBL_MAX_10_EXP 4932 -#endif - -/* On AIX 7.1 with gcc 4.2, the values of LDBL_MIN_EXP, LDBL_MIN, LDBL_MAX are - wrong. - On Linux/PowerPC with gcc 4.4, the value of LDBL_MAX is wrong. */ -#if (defined _ARCH_PPC || defined _POWER) && defined _AIX && (LDBL_MANT_DIG == 106) && defined __GNUC__ -# undef LDBL_MIN_EXP -# define LDBL_MIN_EXP DBL_MIN_EXP -# undef LDBL_MIN_10_EXP -# define LDBL_MIN_10_EXP DBL_MIN_10_EXP -# undef LDBL_MIN -# define LDBL_MIN 2.22507385850720138309023271733240406422e-308L /* DBL_MIN = 2^-1022 */ -#endif -#if (defined _ARCH_PPC || defined _POWER) && (defined _AIX || defined __linux__) && (LDBL_MANT_DIG == 106) && defined __GNUC__ -# undef LDBL_MAX -/* LDBL_MAX is represented as { 0x7FEFFFFF, 0xFFFFFFFF, 0x7C8FFFFF, 0xFFFFFFFF }. - It is not easy to define: - #define LDBL_MAX 1.79769313486231580793728971405302307166e308L - is too small, whereas - #define LDBL_MAX 1.79769313486231580793728971405302307167e308L - is too large. Apparently a bug in GCC decimal-to-binary conversion. - Also, I can't get values larger than - #define LDBL63 ((long double) (1ULL << 63)) - #define LDBL882 (LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63) - #define LDBL945 (LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63) - #define LDBL1008 (LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63) - #define LDBL_MAX (LDBL1008 * 65535.0L + LDBL945 * (long double) 9223372036821221375ULL + LDBL882 * (long double) 4611686018427387904ULL) - which is represented as { 0x7FEFFFFF, 0xFFFFFFFF, 0x7C8FFFFF, 0xF8000000 }. - So, define it like this through a reference to an external variable - - const double LDBL_MAX[2] = { DBL_MAX, DBL_MAX / (double)134217728UL / (double)134217728UL }; - extern const long double LDBL_MAX; - - or through a pointer cast - - #define LDBL_MAX \ - (*(const long double *) (double[]) { DBL_MAX, DBL_MAX / (double)134217728UL / (double)134217728UL }) - - Unfortunately, this is not a constant expression, and the latter expression - does not work well when GCC is optimizing.. */ -# if !GNULIB_defined_long_double_union -union gl_long_double_union - { - struct { double hi; double lo; } dd; - long double ld; - }; -# define GNULIB_defined_long_double_union 1 -# endif -extern const union gl_long_double_union gl_LDBL_MAX; -# define LDBL_MAX (gl_LDBL_MAX.ld) -#endif - -/* On IRIX 6.5, with cc, the value of LDBL_MANT_DIG is wrong. - On IRIX 6.5, with gcc 4.2, the values of LDBL_MIN_EXP, LDBL_MIN, LDBL_EPSILON - are wrong. */ -#if defined __sgi && (LDBL_MANT_DIG >= 106) -# undef LDBL_MANT_DIG -# define LDBL_MANT_DIG 106 -# if defined __GNUC__ -# undef LDBL_MIN_EXP -# define LDBL_MIN_EXP DBL_MIN_EXP -# undef LDBL_MIN_10_EXP -# define LDBL_MIN_10_EXP DBL_MIN_10_EXP -# undef LDBL_MIN -# define LDBL_MIN 2.22507385850720138309023271733240406422e-308L /* DBL_MIN = 2^-1022 */ -# undef LDBL_EPSILON -# define LDBL_EPSILON 2.46519032881566189191165176650870696773e-32L /* 2^-105 */ -# endif -#endif - -#if @REPLACE_ITOLD@ -/* Pull in a function that fixes the 'int' to 'long double' conversion - of glibc 2.7. */ -extern -# ifdef __cplusplus -"C" -# endif -void _Qp_itoq (long double *, int); -static void (*_gl_float_fix_itold) (long double *, int) = _Qp_itoq; -#endif - -#endif /* _@GUARD_PREFIX@_FLOAT_H */ -#endif /* _@GUARD_PREFIX@_FLOAT_H */ diff --git a/lib/fpucw.h b/lib/fpucw.h deleted file mode 100644 index 7dcb310eaf5..00000000000 --- a/lib/fpucw.h +++ /dev/null @@ -1,108 +0,0 @@ -/* Manipulating the FPU control word. -*- coding: utf-8 -*- - Copyright (C) 2007-2023 Free Software Foundation, Inc. - Written by Bruno Haible , 2007. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#ifndef _FPUCW_H -#define _FPUCW_H - -/* The i386 floating point hardware (the 387 compatible FPU, not the modern - SSE/SSE2 hardware) has a controllable rounding precision. It is specified - through the 'PC' bits in the FPU control word ('fctrl' register). (See - the GNU libc i386 header for details.) - - On some platforms, such as Linux or Solaris, the default precision setting - is set to "extended precision". This means that 'long double' instructions - operate correctly, but 'double' computations often produce slightly - different results as on strictly IEEE 754 conforming systems. - - On some platforms, such as NetBSD, the default precision is set to - "double precision". This means that 'long double' instructions will operate - only as 'double', i.e. lead to wrong results. Similarly on FreeBSD 6.4, at - least for the division of 'long double' numbers. - - The FPU control word is under control of the application, i.e. it is - not required to be set either way by the ABI. (In fact, the i386 ABI - https://www.linux-mips.org/pub/linux/mips/doc/ABI/abi386-4.pdf page 3-12 = page 38 - is not clear about it. But in any case, gcc treats the control word - like a "preserved" register: it emits code that assumes that the control - word is preserved across calls, and it restores the control word at the - end of functions that modify it.) - - See Vincent Lefèvre's page https://www.vinc17.net/research/extended.en.html - for a good explanation. - See https://web.archive.org/web/20060905133417/http://www.uwsg.iu.edu/hypermail/linux/kernel/0103.0/0453.html - some argumentation which setting should be the default. */ - -/* This header file provides the following facilities: - fpucw_t integral type holding the value of 'fctrl' - FPU_PC_MASK bit mask denoting the precision control - FPU_PC_DOUBLE precision control for 53 bits mantissa - FPU_PC_EXTENDED precision control for 64 bits mantissa - GET_FPUCW () yields the current FPU control word - SET_FPUCW (word) sets the FPU control word - DECL_LONG_DOUBLE_ROUNDING variable declaration for - BEGIN/END_LONG_DOUBLE_ROUNDING - BEGIN_LONG_DOUBLE_ROUNDING () starts a sequence of instructions with - 'long double' safe operation precision - END_LONG_DOUBLE_ROUNDING () ends a sequence of instructions with - 'long double' safe operation precision - */ - -/* Inline assembler like this works only with GNU C and clang. */ -#if (defined __i386__ || defined __x86_64__) && (defined __GNUC__ || defined __clang__) - -typedef unsigned short fpucw_t; /* glibc calls this fpu_control_t */ - -# define FPU_PC_MASK 0x0300 -# define FPU_PC_DOUBLE 0x200 /* glibc calls this _FPU_DOUBLE */ -# define FPU_PC_EXTENDED 0x300 /* glibc calls this _FPU_EXTENDED */ - -# define GET_FPUCW() __extension__ \ - ({ fpucw_t _cw; \ - __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_cw)); \ - _cw; \ - }) -# define SET_FPUCW(word) __extension__ \ - (void)({ fpucw_t _ncw = (word); \ - __asm__ __volatile__ ("fldcw %0" : : "m" (*&_ncw)); \ - }) - -# define DECL_LONG_DOUBLE_ROUNDING \ - fpucw_t oldcw; -# define BEGIN_LONG_DOUBLE_ROUNDING() \ - (void)(oldcw = GET_FPUCW (), \ - SET_FPUCW ((oldcw & ~FPU_PC_MASK) | FPU_PC_EXTENDED)) -# define END_LONG_DOUBLE_ROUNDING() \ - SET_FPUCW (oldcw) - -#else - -typedef unsigned int fpucw_t; - -# define FPU_PC_MASK 0 -# define FPU_PC_DOUBLE 0 -# define FPU_PC_EXTENDED 0 - -# define GET_FPUCW() 0 -# define SET_FPUCW(word) (void)(word) - -# define DECL_LONG_DOUBLE_ROUNDING -# define BEGIN_LONG_DOUBLE_ROUNDING() -# define END_LONG_DOUBLE_ROUNDING() - -#endif - -#endif /* _FPUCW_H */ diff --git a/lib/frexp.c b/lib/frexp.c deleted file mode 100644 index 78d627b5876..00000000000 --- a/lib/frexp.c +++ /dev/null @@ -1,168 +0,0 @@ -/* Split a double into fraction and mantissa. - Copyright (C) 2007-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by Paolo Bonzini , 2003, and - Bruno Haible , 2007. */ - -#if ! defined USE_LONG_DOUBLE -# include -#endif - -/* Specification. */ -#include - -#include -#ifdef USE_LONG_DOUBLE -# include "isnanl-nolibm.h" -# include "fpucw.h" -#else -# include "isnand-nolibm.h" -#endif - -/* This file assumes FLT_RADIX = 2. If FLT_RADIX is a power of 2 greater - than 2, or not even a power of 2, some rounding errors can occur, so that - then the returned mantissa is only guaranteed to be <= 1.0, not < 1.0. */ - -#ifdef USE_LONG_DOUBLE -# define FUNC frexpl -# define DOUBLE long double -# define ISNAN isnanl -# define DECL_ROUNDING DECL_LONG_DOUBLE_ROUNDING -# define BEGIN_ROUNDING() BEGIN_LONG_DOUBLE_ROUNDING () -# define END_ROUNDING() END_LONG_DOUBLE_ROUNDING () -# define L_(literal) literal##L -#else -# define FUNC frexp -# define DOUBLE double -# define ISNAN isnand -# define DECL_ROUNDING -# define BEGIN_ROUNDING() -# define END_ROUNDING() -# define L_(literal) literal -#endif - -DOUBLE -FUNC (DOUBLE x, int *expptr) -{ - int sign; - int exponent; - DECL_ROUNDING - - /* Test for NaN, infinity, and zero. */ - if (ISNAN (x) || x + x == x) - { - *expptr = 0; - return x; - } - - sign = 0; - if (x < 0) - { - x = - x; - sign = -1; - } - - BEGIN_ROUNDING (); - - { - /* Since the exponent is an 'int', it fits in 64 bits. Therefore the - loops are executed no more than 64 times. */ - DOUBLE pow2[64]; /* pow2[i] = 2^2^i */ - DOUBLE powh[64]; /* powh[i] = 2^-2^i */ - int i; - - exponent = 0; - if (x >= L_(1.0)) - { - /* A positive exponent. */ - DOUBLE pow2_i; /* = pow2[i] */ - DOUBLE powh_i; /* = powh[i] */ - - /* Invariants: pow2_i = 2^2^i, powh_i = 2^-2^i, - x * 2^exponent = argument, x >= 1.0. */ - for (i = 0, pow2_i = L_(2.0), powh_i = L_(0.5); - ; - i++, pow2_i = pow2_i * pow2_i, powh_i = powh_i * powh_i) - { - if (x >= pow2_i) - { - exponent += (1 << i); - x *= powh_i; - } - else - break; - - pow2[i] = pow2_i; - powh[i] = powh_i; - } - /* Avoid making x too small, as it could become a denormalized - number and thus lose precision. */ - while (i > 0 && x < pow2[i - 1]) - { - i--; - powh_i = powh[i]; - } - exponent += (1 << i); - x *= powh_i; - /* Here 2^-2^i <= x < 1.0. */ - } - else - { - /* A negative or zero exponent. */ - DOUBLE pow2_i; /* = pow2[i] */ - DOUBLE powh_i; /* = powh[i] */ - - /* Invariants: pow2_i = 2^2^i, powh_i = 2^-2^i, - x * 2^exponent = argument, x < 1.0. */ - for (i = 0, pow2_i = L_(2.0), powh_i = L_(0.5); - ; - i++, pow2_i = pow2_i * pow2_i, powh_i = powh_i * powh_i) - { - if (x < powh_i) - { - exponent -= (1 << i); - x *= pow2_i; - } - else - break; - - pow2[i] = pow2_i; - powh[i] = powh_i; - } - /* Here 2^-2^i <= x < 1.0. */ - } - - /* Invariants: x * 2^exponent = argument, and 2^-2^i <= x < 1.0. */ - while (i > 0) - { - i--; - if (x < powh[i]) - { - exponent -= (1 << i); - x *= pow2[i]; - } - } - /* Here 0.5 <= x < 1.0. */ - } - - if (sign < 0) - x = - x; - - END_ROUNDING (); - - *expptr = exponent; - return x; -} diff --git a/lib/frexpl.c b/lib/frexpl.c deleted file mode 100644 index 8b17094d50c..00000000000 --- a/lib/frexpl.c +++ /dev/null @@ -1,35 +0,0 @@ -/* Split a 'long double' into fraction and mantissa. - Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -#if HAVE_SAME_LONG_DOUBLE_AS_DOUBLE - -/* Specification. */ -# include - -long double -frexpl (long double x, int *expptr) -{ - return frexp (x, expptr); -} - -#else - -# define USE_LONG_DOUBLE -# include "frexp.c" - -#endif diff --git a/lib/fseterr.c b/lib/fseterr.c deleted file mode 100644 index 9b39305c6d5..00000000000 --- a/lib/fseterr.c +++ /dev/null @@ -1,84 +0,0 @@ -/* Set the error indicator of a stream. - Copyright (C) 2007-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -/* Specification. */ -#include "fseterr.h" - -#include - -#include "stdio-impl.h" - -/* This file is not used on systems that have the __fseterr function, - namely musl libc. */ - -void -fseterr (FILE *fp) -{ - /* Most systems provide FILE as a struct and the necessary bitmask in - , because they need it for implementing getc() and putc() as - fast macros. */ -#if defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1 - /* GNU libc, BeOS, Haiku, Linux libc5 */ - fp->_flags |= _IO_ERR_SEEN; -#elif defined __sferror || defined __DragonFly__ || defined __ANDROID__ - /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */ - fp_->_flags |= __SERR; -#elif defined __EMX__ /* emx+gcc */ - fp->_flags |= _IOERR; -#elif defined __minix /* Minix */ - fp->_flags |= _IOERR; -#elif defined _IOERR /* AIX, HP-UX, IRIX, OSF/1, Solaris, OpenServer, UnixWare, mingw, MSVC, NonStop Kernel, OpenVMS */ - fp_->_flag |= _IOERR; -#elif defined __UCLIBC__ /* uClibc */ - fp->__modeflags |= __FLAG_ERROR; -#elif defined __QNX__ /* QNX */ - fp->_Mode |= 0x200 /* _MERR */; -#elif defined __MINT__ /* Atari FreeMiNT */ - fp->__error = 1; -#elif defined EPLAN9 /* Plan9 */ - if (fp->state != 0 /* CLOSED */) - fp->state = 5 /* ERR */; -#elif 0 /* unknown */ - /* Portable fallback, based on an idea by Rich Felker. - Wow! 6 system calls for something that is just a bit operation! - Not activated on any system, because there is no way to repair FP when - the sequence of system calls fails, and library code should not call - abort(). */ - int saved_errno; - int fd; - int fd2; - - saved_errno = errno; - fflush (fp); - fd = fileno (fp); - fd2 = dup (fd); - if (fd2 >= 0) - { - close (fd); - fputc ('\0', fp); /* This should set the error indicator. */ - fflush (fp); /* Or this. */ - if (dup2 (fd2, fd) < 0) - /* Whee... we botched the stream and now cannot restore it! */ - abort (); - close (fd2); - } - errno = saved_errno; -#else - #error "Please port gnulib fseterr.c to your platform! Look at the definitions of ferror and clearerr on your system, then report this to bug-gnulib." -#endif -} diff --git a/lib/fseterr.h b/lib/fseterr.h deleted file mode 100644 index 87dc347332d..00000000000 --- a/lib/fseterr.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Set the error indicator of a stream. - Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#ifndef _FSETERR_H -#define _FSETERR_H - -/* This file uses HAVE___FSETERR. */ -#if !_GL_CONFIG_H_INCLUDED - #error "Please include config.h first." -#endif - -#include - -/* Set the error indicator of the stream FP. - The "error indicator" is set when an I/O operation on the stream fails, and - is cleared (together with the "end-of-file" indicator) by clearerr (FP). */ - -#if HAVE___FSETERR /* musl libc */ - -# include -# define fseterr(fp) __fseterr (fp) - -#else - -# ifdef __cplusplus -extern "C" { -# endif - -extern void fseterr (FILE *fp); - -# ifdef __cplusplus -} -# endif - -#endif - -#endif /* _FSETERR_H */ diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 4fef3c4c82d..1bfa1daa0b3 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -136,7 +136,6 @@ # nstrftime \ # pathmax \ # pipe2 \ -# printf-posix \ # pselect \ # pthread_sigmask \ # qcopy-acl \ @@ -170,7 +169,6 @@ # unlocked-io \ # update-copyright \ # utimensat \ -# vasprintf-posix \ # vla \ # warnings \ # year2038 @@ -198,11 +196,11 @@ ANDROID_OBJ = @ANDROID_OBJ@ ANDROID_SDK_18_OR_EARLIER = @ANDROID_SDK_18_OR_EARLIER@ ANDROID_SDK_8_OR_EARLIER = @ANDROID_SDK_8_OR_EARLIER@ ANDROID_SHARED_USER_ID = @ANDROID_SHARED_USER_ID@ +ANDROID_SHARED_USER_NAME = @ANDROID_SHARED_USER_NAME@ APKSIGNER = @APKSIGNER@ APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@ AR = @AR@ ARFLAGS = @ARFLAGS@ -ASM_SYMBOL_PREFIX = @ASM_SYMBOL_PREFIX@ ASSERT_H = @ASSERT_H@ AUTO_DEPEND = @AUTO_DEPEND@ AWK = @AWK@ @@ -270,7 +268,6 @@ EXEEXT = @EXEEXT@ FILE_HAS_ACL_LIB = @FILE_HAS_ACL_LIB@ FIND_DELETE = @FIND_DELETE@ FIRSTFILE_OBJ = @FIRSTFILE_OBJ@ -FLOAT_H = @FLOAT_H@ FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@ FONTCONFIG_LIBS = @FONTCONFIG_LIBS@ FONT_OBJ = @FONT_OBJ@ @@ -299,10 +296,8 @@ GL_COND_OBJ_FACCESSAT_CONDITION = @GL_COND_OBJ_FACCESSAT_CONDITION@ GL_COND_OBJ_FCHMODAT_CONDITION = @GL_COND_OBJ_FCHMODAT_CONDITION@ GL_COND_OBJ_FCNTL_CONDITION = @GL_COND_OBJ_FCNTL_CONDITION@ GL_COND_OBJ_FDOPENDIR_CONDITION = @GL_COND_OBJ_FDOPENDIR_CONDITION@ -GL_COND_OBJ_FLOAT_CONDITION = @GL_COND_OBJ_FLOAT_CONDITION@ GL_COND_OBJ_FPENDING_CONDITION = @GL_COND_OBJ_FPENDING_CONDITION@ GL_COND_OBJ_FREE_CONDITION = @GL_COND_OBJ_FREE_CONDITION@ -GL_COND_OBJ_FSETERR_CONDITION = @GL_COND_OBJ_FSETERR_CONDITION@ GL_COND_OBJ_FSTATAT_CONDITION = @GL_COND_OBJ_FSTATAT_CONDITION@ GL_COND_OBJ_FSUSAGE_CONDITION = @GL_COND_OBJ_FSUSAGE_CONDITION@ GL_COND_OBJ_FSYNC_CONDITION = @GL_COND_OBJ_FSYNC_CONDITION@ @@ -316,7 +311,6 @@ GL_COND_OBJ_GETOPT_CONDITION = @GL_COND_OBJ_GETOPT_CONDITION@ GL_COND_OBJ_GETRANDOM_CONDITION = @GL_COND_OBJ_GETRANDOM_CONDITION@ GL_COND_OBJ_GETTIMEOFDAY_CONDITION = @GL_COND_OBJ_GETTIMEOFDAY_CONDITION@ GL_COND_OBJ_GROUP_MEMBER_CONDITION = @GL_COND_OBJ_GROUP_MEMBER_CONDITION@ -GL_COND_OBJ_ITOLD_CONDITION = @GL_COND_OBJ_ITOLD_CONDITION@ GL_COND_OBJ_LCHMOD_CONDITION = @GL_COND_OBJ_LCHMOD_CONDITION@ GL_COND_OBJ_LSTAT_CONDITION = @GL_COND_OBJ_LSTAT_CONDITION@ GL_COND_OBJ_MEMPCPY_CONDITION = @GL_COND_OBJ_MEMPCPY_CONDITION@ @@ -334,7 +328,6 @@ GL_COND_OBJ_READLINK_CONDITION = @GL_COND_OBJ_READLINK_CONDITION@ GL_COND_OBJ_REGEX_CONDITION = @GL_COND_OBJ_REGEX_CONDITION@ GL_COND_OBJ_SIG2STR_CONDITION = @GL_COND_OBJ_SIG2STR_CONDITION@ GL_COND_OBJ_SIGDESCR_NP_CONDITION = @GL_COND_OBJ_SIGDESCR_NP_CONDITION@ -GL_COND_OBJ_SIGNBIT3_CONDITION = @GL_COND_OBJ_SIGNBIT3_CONDITION@ GL_COND_OBJ_STDIO_READ_CONDITION = @GL_COND_OBJ_STDIO_READ_CONDITION@ GL_COND_OBJ_STDIO_WRITE_CONDITION = @GL_COND_OBJ_STDIO_WRITE_CONDITION@ GL_COND_OBJ_STPCPY_CONDITION = @GL_COND_OBJ_STPCPY_CONDITION@ @@ -352,7 +345,6 @@ GL_GENERATE_ASSERT_H_CONDITION = @GL_GENERATE_ASSERT_H_CONDITION@ GL_GENERATE_BYTESWAP_H_CONDITION = @GL_GENERATE_BYTESWAP_H_CONDITION@ GL_GENERATE_ERRNO_H_CONDITION = @GL_GENERATE_ERRNO_H_CONDITION@ GL_GENERATE_EXECINFO_H_CONDITION = @GL_GENERATE_EXECINFO_H_CONDITION@ -GL_GENERATE_FLOAT_H_CONDITION = @GL_GENERATE_FLOAT_H_CONDITION@ GL_GENERATE_GETOPT_CDEFS_H_CONDITION = @GL_GENERATE_GETOPT_CDEFS_H_CONDITION@ GL_GENERATE_GETOPT_H_CONDITION = @GL_GENERATE_GETOPT_H_CONDITION@ GL_GENERATE_GMP_GMP_H_CONDITION = @GL_GENERATE_GMP_GMP_H_CONDITION@ @@ -364,37 +356,18 @@ GL_GENERATE_STDCKDINT_H_CONDITION = @GL_GENERATE_STDCKDINT_H_CONDITION@ GL_GENERATE_STDDEF_H_CONDITION = @GL_GENERATE_STDDEF_H_CONDITION@ GL_GENERATE_STDINT_H_CONDITION = @GL_GENERATE_STDINT_H_CONDITION@ GL_GNULIB_ACCESS = @GL_GNULIB_ACCESS@ -GL_GNULIB_ACOSF = @GL_GNULIB_ACOSF@ -GL_GNULIB_ACOSL = @GL_GNULIB_ACOSL@ GL_GNULIB_ALIGNED_ALLOC = @GL_GNULIB_ALIGNED_ALLOC@ GL_GNULIB_ALPHASORT = @GL_GNULIB_ALPHASORT@ -GL_GNULIB_ASINF = @GL_GNULIB_ASINF@ -GL_GNULIB_ASINL = @GL_GNULIB_ASINL@ -GL_GNULIB_ATAN2F = @GL_GNULIB_ATAN2F@ -GL_GNULIB_ATANF = @GL_GNULIB_ATANF@ -GL_GNULIB_ATANL = @GL_GNULIB_ATANL@ GL_GNULIB_ATOLL = @GL_GNULIB_ATOLL@ GL_GNULIB_CALLOC_GNU = @GL_GNULIB_CALLOC_GNU@ GL_GNULIB_CALLOC_POSIX = @GL_GNULIB_CALLOC_POSIX@ GL_GNULIB_CANONICALIZE_FILE_NAME = @GL_GNULIB_CANONICALIZE_FILE_NAME@ -GL_GNULIB_CBRT = @GL_GNULIB_CBRT@ -GL_GNULIB_CBRTF = @GL_GNULIB_CBRTF@ -GL_GNULIB_CBRTL = @GL_GNULIB_CBRTL@ -GL_GNULIB_CEIL = @GL_GNULIB_CEIL@ -GL_GNULIB_CEILF = @GL_GNULIB_CEILF@ -GL_GNULIB_CEILL = @GL_GNULIB_CEILL@ GL_GNULIB_CHDIR = @GL_GNULIB_CHDIR@ GL_GNULIB_CHMOD = @GL_GNULIB_CHMOD@ GL_GNULIB_CHOWN = @GL_GNULIB_CHOWN@ GL_GNULIB_CLOSE = @GL_GNULIB_CLOSE@ GL_GNULIB_CLOSEDIR = @GL_GNULIB_CLOSEDIR@ -GL_GNULIB_COPYSIGN = @GL_GNULIB_COPYSIGN@ -GL_GNULIB_COPYSIGNF = @GL_GNULIB_COPYSIGNF@ -GL_GNULIB_COPYSIGNL = @GL_GNULIB_COPYSIGNL@ GL_GNULIB_COPY_FILE_RANGE = @GL_GNULIB_COPY_FILE_RANGE@ -GL_GNULIB_COSF = @GL_GNULIB_COSF@ -GL_GNULIB_COSHF = @GL_GNULIB_COSHF@ -GL_GNULIB_COSL = @GL_GNULIB_COSL@ GL_GNULIB_CREAT = @GL_GNULIB_CREAT@ GL_GNULIB_CTIME = @GL_GNULIB_CTIME@ GL_GNULIB_DIRFD = @GL_GNULIB_DIRFD@ @@ -411,17 +384,7 @@ GL_GNULIB_EXECV = @GL_GNULIB_EXECV@ GL_GNULIB_EXECVE = @GL_GNULIB_EXECVE@ GL_GNULIB_EXECVP = @GL_GNULIB_EXECVP@ GL_GNULIB_EXECVPE = @GL_GNULIB_EXECVPE@ -GL_GNULIB_EXP2 = @GL_GNULIB_EXP2@ -GL_GNULIB_EXP2F = @GL_GNULIB_EXP2F@ -GL_GNULIB_EXP2L = @GL_GNULIB_EXP2L@ -GL_GNULIB_EXPF = @GL_GNULIB_EXPF@ -GL_GNULIB_EXPL = @GL_GNULIB_EXPL@ GL_GNULIB_EXPLICIT_BZERO = @GL_GNULIB_EXPLICIT_BZERO@ -GL_GNULIB_EXPM1 = @GL_GNULIB_EXPM1@ -GL_GNULIB_EXPM1F = @GL_GNULIB_EXPM1F@ -GL_GNULIB_EXPM1L = @GL_GNULIB_EXPM1L@ -GL_GNULIB_FABSF = @GL_GNULIB_FABSF@ -GL_GNULIB_FABSL = @GL_GNULIB_FABSL@ GL_GNULIB_FACCESSAT = @GL_GNULIB_FACCESSAT@ GL_GNULIB_FCHDIR = @GL_GNULIB_FCHDIR@ GL_GNULIB_FCHMODAT = @GL_GNULIB_FCHMODAT@ @@ -436,15 +399,6 @@ GL_GNULIB_FFSL = @GL_GNULIB_FFSL@ GL_GNULIB_FFSLL = @GL_GNULIB_FFSLL@ GL_GNULIB_FGETC = @GL_GNULIB_FGETC@ GL_GNULIB_FGETS = @GL_GNULIB_FGETS@ -GL_GNULIB_FLOOR = @GL_GNULIB_FLOOR@ -GL_GNULIB_FLOORF = @GL_GNULIB_FLOORF@ -GL_GNULIB_FLOORL = @GL_GNULIB_FLOORL@ -GL_GNULIB_FMA = @GL_GNULIB_FMA@ -GL_GNULIB_FMAF = @GL_GNULIB_FMAF@ -GL_GNULIB_FMAL = @GL_GNULIB_FMAL@ -GL_GNULIB_FMOD = @GL_GNULIB_FMOD@ -GL_GNULIB_FMODF = @GL_GNULIB_FMODF@ -GL_GNULIB_FMODL = @GL_GNULIB_FMODL@ GL_GNULIB_FOPEN = @GL_GNULIB_FOPEN@ GL_GNULIB_FOPEN_GNU = @GL_GNULIB_FOPEN_GNU@ GL_GNULIB_FPRINTF = @GL_GNULIB_FPRINTF@ @@ -455,9 +409,6 @@ GL_GNULIB_FPUTS = @GL_GNULIB_FPUTS@ GL_GNULIB_FREAD = @GL_GNULIB_FREAD@ GL_GNULIB_FREE_POSIX = @GL_GNULIB_FREE_POSIX@ GL_GNULIB_FREOPEN = @GL_GNULIB_FREOPEN@ -GL_GNULIB_FREXP = @GL_GNULIB_FREXP@ -GL_GNULIB_FREXPF = @GL_GNULIB_FREXPF@ -GL_GNULIB_FREXPL = @GL_GNULIB_FREXPL@ GL_GNULIB_FSCANF = @GL_GNULIB_FSCANF@ GL_GNULIB_FSEEK = @GL_GNULIB_FSEEK@ GL_GNULIB_FSEEKO = @GL_GNULIB_FSEEKO@ @@ -494,43 +445,14 @@ GL_GNULIB_GETUMASK = @GL_GNULIB_GETUMASK@ GL_GNULIB_GETUSERSHELL = @GL_GNULIB_GETUSERSHELL@ GL_GNULIB_GRANTPT = @GL_GNULIB_GRANTPT@ GL_GNULIB_GROUP_MEMBER = @GL_GNULIB_GROUP_MEMBER@ -GL_GNULIB_HYPOT = @GL_GNULIB_HYPOT@ -GL_GNULIB_HYPOTF = @GL_GNULIB_HYPOTF@ -GL_GNULIB_HYPOTL = @GL_GNULIB_HYPOTL@ -GL_GNULIB_ILOGB = @GL_GNULIB_ILOGB@ -GL_GNULIB_ILOGBF = @GL_GNULIB_ILOGBF@ -GL_GNULIB_ILOGBL = @GL_GNULIB_ILOGBL@ GL_GNULIB_IMAXABS = @GL_GNULIB_IMAXABS@ GL_GNULIB_IMAXDIV = @GL_GNULIB_IMAXDIV@ GL_GNULIB_ISATTY = @GL_GNULIB_ISATTY@ -GL_GNULIB_ISFINITE = @GL_GNULIB_ISFINITE@ -GL_GNULIB_ISINF = @GL_GNULIB_ISINF@ -GL_GNULIB_ISNAN = @GL_GNULIB_ISNAN@ -GL_GNULIB_ISNAND = @GL_GNULIB_ISNAND@ -GL_GNULIB_ISNANF = @GL_GNULIB_ISNANF@ -GL_GNULIB_ISNANL = @GL_GNULIB_ISNANL@ GL_GNULIB_LCHMOD = @GL_GNULIB_LCHMOD@ GL_GNULIB_LCHOWN = @GL_GNULIB_LCHOWN@ -GL_GNULIB_LDEXPF = @GL_GNULIB_LDEXPF@ -GL_GNULIB_LDEXPL = @GL_GNULIB_LDEXPL@ GL_GNULIB_LINK = @GL_GNULIB_LINK@ GL_GNULIB_LINKAT = @GL_GNULIB_LINKAT@ GL_GNULIB_LOCALTIME = @GL_GNULIB_LOCALTIME@ -GL_GNULIB_LOG = @GL_GNULIB_LOG@ -GL_GNULIB_LOG10 = @GL_GNULIB_LOG10@ -GL_GNULIB_LOG10F = @GL_GNULIB_LOG10F@ -GL_GNULIB_LOG10L = @GL_GNULIB_LOG10L@ -GL_GNULIB_LOG1P = @GL_GNULIB_LOG1P@ -GL_GNULIB_LOG1PF = @GL_GNULIB_LOG1PF@ -GL_GNULIB_LOG1PL = @GL_GNULIB_LOG1PL@ -GL_GNULIB_LOG2 = @GL_GNULIB_LOG2@ -GL_GNULIB_LOG2F = @GL_GNULIB_LOG2F@ -GL_GNULIB_LOG2L = @GL_GNULIB_LOG2L@ -GL_GNULIB_LOGB = @GL_GNULIB_LOGB@ -GL_GNULIB_LOGBF = @GL_GNULIB_LOGBF@ -GL_GNULIB_LOGBL = @GL_GNULIB_LOGBL@ -GL_GNULIB_LOGF = @GL_GNULIB_LOGF@ -GL_GNULIB_LOGL = @GL_GNULIB_LOGL@ GL_GNULIB_LSEEK = @GL_GNULIB_LSEEK@ GL_GNULIB_LSTAT = @GL_GNULIB_LSTAT@ GL_GNULIB_MALLOC_GNU = @GL_GNULIB_MALLOC_GNU@ @@ -575,9 +497,6 @@ GL_GNULIB_MDA_GETCWD = @GL_GNULIB_MDA_GETCWD@ GL_GNULIB_MDA_GETPID = @GL_GNULIB_MDA_GETPID@ GL_GNULIB_MDA_GETW = @GL_GNULIB_MDA_GETW@ GL_GNULIB_MDA_ISATTY = @GL_GNULIB_MDA_ISATTY@ -GL_GNULIB_MDA_J0 = @GL_GNULIB_MDA_J0@ -GL_GNULIB_MDA_J1 = @GL_GNULIB_MDA_J1@ -GL_GNULIB_MDA_JN = @GL_GNULIB_MDA_JN@ GL_GNULIB_MDA_LSEEK = @GL_GNULIB_MDA_LSEEK@ GL_GNULIB_MDA_MEMCCPY = @GL_GNULIB_MDA_MEMCCPY@ GL_GNULIB_MDA_MKDIR = @GL_GNULIB_MDA_MKDIR@ @@ -594,9 +513,6 @@ GL_GNULIB_MDA_TZSET = @GL_GNULIB_MDA_TZSET@ GL_GNULIB_MDA_UMASK = @GL_GNULIB_MDA_UMASK@ GL_GNULIB_MDA_UNLINK = @GL_GNULIB_MDA_UNLINK@ GL_GNULIB_MDA_WRITE = @GL_GNULIB_MDA_WRITE@ -GL_GNULIB_MDA_Y0 = @GL_GNULIB_MDA_Y0@ -GL_GNULIB_MDA_Y1 = @GL_GNULIB_MDA_Y1@ -GL_GNULIB_MDA_YN = @GL_GNULIB_MDA_YN@ GL_GNULIB_MEMCHR = @GL_GNULIB_MEMCHR@ GL_GNULIB_MEMMEM = @GL_GNULIB_MEMMEM@ GL_GNULIB_MEMPCPY = @GL_GNULIB_MEMPCPY@ @@ -614,9 +530,6 @@ GL_GNULIB_MKOSTEMPS = @GL_GNULIB_MKOSTEMPS@ GL_GNULIB_MKSTEMP = @GL_GNULIB_MKSTEMP@ GL_GNULIB_MKSTEMPS = @GL_GNULIB_MKSTEMPS@ GL_GNULIB_MKTIME = @GL_GNULIB_MKTIME@ -GL_GNULIB_MODF = @GL_GNULIB_MODF@ -GL_GNULIB_MODFF = @GL_GNULIB_MODFF@ -GL_GNULIB_MODFL = @GL_GNULIB_MODFL@ GL_GNULIB_NANOSLEEP = @GL_GNULIB_NANOSLEEP@ GL_GNULIB_NONBLOCKING = @GL_GNULIB_NONBLOCKING@ GL_GNULIB_OBSTACK_PRINTF = @GL_GNULIB_OBSTACK_PRINTF@ @@ -632,7 +545,6 @@ GL_GNULIB_PIPE2 = @GL_GNULIB_PIPE2@ GL_GNULIB_POPEN = @GL_GNULIB_POPEN@ GL_GNULIB_POSIX_MEMALIGN = @GL_GNULIB_POSIX_MEMALIGN@ GL_GNULIB_POSIX_OPENPT = @GL_GNULIB_POSIX_OPENPT@ -GL_GNULIB_POWF = @GL_GNULIB_POWF@ GL_GNULIB_PREAD = @GL_GNULIB_PREAD@ GL_GNULIB_PRINTF = @GL_GNULIB_PRINTF@ GL_GNULIB_PRINTF_POSIX = @GL_GNULIB_PRINTF_POSIX@ @@ -658,20 +570,11 @@ GL_GNULIB_REALLOCARRAY = @GL_GNULIB_REALLOCARRAY@ GL_GNULIB_REALLOC_GNU = @GL_GNULIB_REALLOC_GNU@ GL_GNULIB_REALLOC_POSIX = @GL_GNULIB_REALLOC_POSIX@ GL_GNULIB_REALPATH = @GL_GNULIB_REALPATH@ -GL_GNULIB_REMAINDER = @GL_GNULIB_REMAINDER@ -GL_GNULIB_REMAINDERF = @GL_GNULIB_REMAINDERF@ -GL_GNULIB_REMAINDERL = @GL_GNULIB_REMAINDERL@ GL_GNULIB_REMOVE = @GL_GNULIB_REMOVE@ GL_GNULIB_RENAME = @GL_GNULIB_RENAME@ GL_GNULIB_RENAMEAT = @GL_GNULIB_RENAMEAT@ GL_GNULIB_REWINDDIR = @GL_GNULIB_REWINDDIR@ -GL_GNULIB_RINT = @GL_GNULIB_RINT@ -GL_GNULIB_RINTF = @GL_GNULIB_RINTF@ -GL_GNULIB_RINTL = @GL_GNULIB_RINTL@ GL_GNULIB_RMDIR = @GL_GNULIB_RMDIR@ -GL_GNULIB_ROUND = @GL_GNULIB_ROUND@ -GL_GNULIB_ROUNDF = @GL_GNULIB_ROUNDF@ -GL_GNULIB_ROUNDL = @GL_GNULIB_ROUNDL@ GL_GNULIB_RPMATCH = @GL_GNULIB_RPMATCH@ GL_GNULIB_SCANDIR = @GL_GNULIB_SCANDIR@ GL_GNULIB_SCANF = @GL_GNULIB_SCANF@ @@ -683,16 +586,10 @@ GL_GNULIB_SIGABBREV_NP = @GL_GNULIB_SIGABBREV_NP@ GL_GNULIB_SIGACTION = @GL_GNULIB_SIGACTION@ GL_GNULIB_SIGDESCR_NP = @GL_GNULIB_SIGDESCR_NP@ GL_GNULIB_SIGNAL_H_SIGPIPE = @GL_GNULIB_SIGNAL_H_SIGPIPE@ -GL_GNULIB_SIGNBIT = @GL_GNULIB_SIGNBIT@ GL_GNULIB_SIGPROCMASK = @GL_GNULIB_SIGPROCMASK@ -GL_GNULIB_SINF = @GL_GNULIB_SINF@ -GL_GNULIB_SINHF = @GL_GNULIB_SINHF@ -GL_GNULIB_SINL = @GL_GNULIB_SINL@ GL_GNULIB_SLEEP = @GL_GNULIB_SLEEP@ GL_GNULIB_SNPRINTF = @GL_GNULIB_SNPRINTF@ GL_GNULIB_SPRINTF_POSIX = @GL_GNULIB_SPRINTF_POSIX@ -GL_GNULIB_SQRTF = @GL_GNULIB_SQRTF@ -GL_GNULIB_SQRTL = @GL_GNULIB_SQRTL@ GL_GNULIB_STAT = @GL_GNULIB_STAT@ GL_GNULIB_STDIO_H_NONBLOCKING = @GL_GNULIB_STDIO_H_NONBLOCKING@ GL_GNULIB_STDIO_H_SIGPIPE = @GL_GNULIB_STDIO_H_SIGPIPE@ @@ -726,9 +623,6 @@ GL_GNULIB_STRVERSCMP = @GL_GNULIB_STRVERSCMP@ GL_GNULIB_SYMLINK = @GL_GNULIB_SYMLINK@ GL_GNULIB_SYMLINKAT = @GL_GNULIB_SYMLINKAT@ GL_GNULIB_SYSTEM_POSIX = @GL_GNULIB_SYSTEM_POSIX@ -GL_GNULIB_TANF = @GL_GNULIB_TANF@ -GL_GNULIB_TANHF = @GL_GNULIB_TANHF@ -GL_GNULIB_TANL = @GL_GNULIB_TANL@ GL_GNULIB_TIME = @GL_GNULIB_TIME@ GL_GNULIB_TIMEGM = @GL_GNULIB_TIMEGM@ GL_GNULIB_TIMESPEC_GET = @GL_GNULIB_TIMESPEC_GET@ @@ -736,10 +630,7 @@ GL_GNULIB_TIMESPEC_GETRES = @GL_GNULIB_TIMESPEC_GETRES@ GL_GNULIB_TIME_R = @GL_GNULIB_TIME_R@ GL_GNULIB_TIME_RZ = @GL_GNULIB_TIME_RZ@ GL_GNULIB_TMPFILE = @GL_GNULIB_TMPFILE@ -GL_GNULIB_TRUNC = @GL_GNULIB_TRUNC@ GL_GNULIB_TRUNCATE = @GL_GNULIB_TRUNCATE@ -GL_GNULIB_TRUNCF = @GL_GNULIB_TRUNCF@ -GL_GNULIB_TRUNCL = @GL_GNULIB_TRUNCL@ GL_GNULIB_TTYNAME_R = @GL_GNULIB_TTYNAME_R@ GL_GNULIB_TZSET = @GL_GNULIB_TZSET@ GL_GNULIB_UNISTD_H_GETOPT = @GL_GNULIB_UNISTD_H_GETOPT@ @@ -786,58 +677,26 @@ HAIKU_LIBS = @HAIKU_LIBS@ HAIKU_OBJ = @HAIKU_OBJ@ HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@ HARFBUZZ_LIBS = @HARFBUZZ_LIBS@ -HAVE_ACOSF = @HAVE_ACOSF@ -HAVE_ACOSL = @HAVE_ACOSL@ HAVE_ALIGNED_ALLOC = @HAVE_ALIGNED_ALLOC@ HAVE_ALLOCA_H = @HAVE_ALLOCA_H@ HAVE_ALPHASORT = @HAVE_ALPHASORT@ -HAVE_ASINF = @HAVE_ASINF@ -HAVE_ASINL = @HAVE_ASINL@ -HAVE_ATAN2F = @HAVE_ATAN2F@ -HAVE_ATANF = @HAVE_ATANF@ -HAVE_ATANL = @HAVE_ATANL@ HAVE_ATOLL = @HAVE_ATOLL@ HAVE_BE_APP = @HAVE_BE_APP@ HAVE_C99_STDINT_H = @HAVE_C99_STDINT_H@ HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@ -HAVE_CBRT = @HAVE_CBRT@ -HAVE_CBRTF = @HAVE_CBRTF@ -HAVE_CBRTL = @HAVE_CBRTL@ HAVE_CHOWN = @HAVE_CHOWN@ HAVE_CLOSEDIR = @HAVE_CLOSEDIR@ -HAVE_COPYSIGN = @HAVE_COPYSIGN@ -HAVE_COPYSIGNL = @HAVE_COPYSIGNL@ HAVE_COPY_FILE_RANGE = @HAVE_COPY_FILE_RANGE@ -HAVE_COSF = @HAVE_COSF@ -HAVE_COSHF = @HAVE_COSHF@ -HAVE_COSL = @HAVE_COSL@ -HAVE_DECL_ACOSL = @HAVE_DECL_ACOSL@ -HAVE_DECL_ASINL = @HAVE_DECL_ASINL@ -HAVE_DECL_ATANL = @HAVE_DECL_ATANL@ -HAVE_DECL_CBRTF = @HAVE_DECL_CBRTF@ -HAVE_DECL_CBRTL = @HAVE_DECL_CBRTL@ -HAVE_DECL_CEILF = @HAVE_DECL_CEILF@ -HAVE_DECL_CEILL = @HAVE_DECL_CEILL@ -HAVE_DECL_COPYSIGNF = @HAVE_DECL_COPYSIGNF@ -HAVE_DECL_COSL = @HAVE_DECL_COSL@ HAVE_DECL_DIRFD = @HAVE_DECL_DIRFD@ HAVE_DECL_ECVT = @HAVE_DECL_ECVT@ HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@ HAVE_DECL_EXECVPE = @HAVE_DECL_EXECVPE@ -HAVE_DECL_EXP2 = @HAVE_DECL_EXP2@ -HAVE_DECL_EXP2F = @HAVE_DECL_EXP2F@ -HAVE_DECL_EXP2L = @HAVE_DECL_EXP2L@ -HAVE_DECL_EXPL = @HAVE_DECL_EXPL@ -HAVE_DECL_EXPM1L = @HAVE_DECL_EXPM1L@ HAVE_DECL_FCHDIR = @HAVE_DECL_FCHDIR@ HAVE_DECL_FCLOSEALL = @HAVE_DECL_FCLOSEALL@ HAVE_DECL_FCVT = @HAVE_DECL_FCVT@ HAVE_DECL_FDATASYNC = @HAVE_DECL_FDATASYNC@ HAVE_DECL_FDOPENDIR = @HAVE_DECL_FDOPENDIR@ -HAVE_DECL_FLOORF = @HAVE_DECL_FLOORF@ -HAVE_DECL_FLOORL = @HAVE_DECL_FLOORL@ HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@ -HAVE_DECL_FREXPL = @HAVE_DECL_FREXPL@ HAVE_DECL_FSEEKO = @HAVE_DECL_FSEEKO@ HAVE_DECL_FTELLO = @HAVE_DECL_FTELLO@ HAVE_DECL_GCVT = @HAVE_DECL_GCVT@ @@ -853,32 +712,17 @@ HAVE_DECL_GETW = @HAVE_DECL_GETW@ HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@ HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@ HAVE_DECL_INITSTATE = @HAVE_DECL_INITSTATE@ -HAVE_DECL_LDEXPL = @HAVE_DECL_LDEXPL@ HAVE_DECL_LOCALTIME_R = @HAVE_DECL_LOCALTIME_R@ -HAVE_DECL_LOG10L = @HAVE_DECL_LOG10L@ -HAVE_DECL_LOG2 = @HAVE_DECL_LOG2@ -HAVE_DECL_LOG2F = @HAVE_DECL_LOG2F@ -HAVE_DECL_LOG2L = @HAVE_DECL_LOG2L@ -HAVE_DECL_LOGB = @HAVE_DECL_LOGB@ -HAVE_DECL_LOGL = @HAVE_DECL_LOGL@ HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@ HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@ HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@ HAVE_DECL_POSIX_SPAWN_SETSID = @HAVE_DECL_POSIX_SPAWN_SETSID@ HAVE_DECL_PROGRAM_INVOCATION_NAME = @HAVE_DECL_PROGRAM_INVOCATION_NAME@ HAVE_DECL_PUTW = @HAVE_DECL_PUTW@ -HAVE_DECL_REMAINDER = @HAVE_DECL_REMAINDER@ -HAVE_DECL_REMAINDERL = @HAVE_DECL_REMAINDERL@ -HAVE_DECL_RINTF = @HAVE_DECL_RINTF@ -HAVE_DECL_ROUND = @HAVE_DECL_ROUND@ -HAVE_DECL_ROUNDF = @HAVE_DECL_ROUNDF@ -HAVE_DECL_ROUNDL = @HAVE_DECL_ROUNDL@ HAVE_DECL_SETENV = @HAVE_DECL_SETENV@ HAVE_DECL_SETHOSTNAME = @HAVE_DECL_SETHOSTNAME@ HAVE_DECL_SETSTATE = @HAVE_DECL_SETSTATE@ -HAVE_DECL_SINL = @HAVE_DECL_SINL@ HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@ -HAVE_DECL_SQRTL = @HAVE_DECL_SQRTL@ HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@ HAVE_DECL_STRERROR_R = @HAVE_DECL_STRERROR_R@ HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@ @@ -887,11 +731,7 @@ HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@ HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@ HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@ HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@ -HAVE_DECL_TANL = @HAVE_DECL_TANL@ -HAVE_DECL_TRUNC = @HAVE_DECL_TRUNC@ HAVE_DECL_TRUNCATE = @HAVE_DECL_TRUNCATE@ -HAVE_DECL_TRUNCF = @HAVE_DECL_TRUNCF@ -HAVE_DECL_TRUNCL = @HAVE_DECL_TRUNCL@ HAVE_DECL_TTYNAME_R = @HAVE_DECL_TTYNAME_R@ HAVE_DECL_UNSETENV = @HAVE_DECL_UNSETENV@ HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@ @@ -900,13 +740,7 @@ HAVE_DPRINTF = @HAVE_DPRINTF@ HAVE_DUP3 = @HAVE_DUP3@ HAVE_EUIDACCESS = @HAVE_EUIDACCESS@ HAVE_EXECVPE = @HAVE_EXECVPE@ -HAVE_EXPF = @HAVE_EXPF@ -HAVE_EXPL = @HAVE_EXPL@ HAVE_EXPLICIT_BZERO = @HAVE_EXPLICIT_BZERO@ -HAVE_EXPM1 = @HAVE_EXPM1@ -HAVE_EXPM1F = @HAVE_EXPM1F@ -HAVE_FABSF = @HAVE_FABSF@ -HAVE_FABSL = @HAVE_FABSL@ HAVE_FACCESSAT = @HAVE_FACCESSAT@ HAVE_FCHDIR = @HAVE_FCHDIR@ HAVE_FCHMODAT = @HAVE_FCHMODAT@ @@ -914,15 +748,8 @@ HAVE_FCHOWNAT = @HAVE_FCHOWNAT@ HAVE_FCNTL = @HAVE_FCNTL@ HAVE_FDATASYNC = @HAVE_FDATASYNC@ HAVE_FDOPENDIR = @HAVE_FDOPENDIR@ -HAVE_FEATURES_H = @HAVE_FEATURES_H@ HAVE_FFSL = @HAVE_FFSL@ HAVE_FFSLL = @HAVE_FFSLL@ -HAVE_FMA = @HAVE_FMA@ -HAVE_FMAF = @HAVE_FMAF@ -HAVE_FMAL = @HAVE_FMAL@ -HAVE_FMODF = @HAVE_FMODF@ -HAVE_FMODL = @HAVE_FMODL@ -HAVE_FREXPF = @HAVE_FREXPF@ HAVE_FSEEKO = @HAVE_FSEEKO@ HAVE_FSTATAT = @HAVE_FSTATAT@ HAVE_FSYNC = @HAVE_FSYNC@ @@ -945,35 +772,17 @@ HAVE_GETUMASK = @HAVE_GETUMASK@ HAVE_GRANTPT = @HAVE_GRANTPT@ HAVE_GROUP_MEMBER = @HAVE_GROUP_MEMBER@ HAVE_GSETTINGS = @HAVE_GSETTINGS@ -HAVE_HYPOTF = @HAVE_HYPOTF@ -HAVE_HYPOTL = @HAVE_HYPOTL@ -HAVE_ILOGB = @HAVE_ILOGB@ -HAVE_ILOGBF = @HAVE_ILOGBF@ -HAVE_ILOGBL = @HAVE_ILOGBL@ HAVE_IMAXABS = @HAVE_IMAXABS@ HAVE_IMAXDIV = @HAVE_IMAXDIV@ HAVE_IMAXDIV_T = @HAVE_IMAXDIV_T@ HAVE_INITSTATE = @HAVE_INITSTATE@ HAVE_INTTYPES_H = @HAVE_INTTYPES_H@ -HAVE_ISNAND = @HAVE_ISNAND@ -HAVE_ISNANF = @HAVE_ISNANF@ -HAVE_ISNANL = @HAVE_ISNANL@ HAVE_LCHMOD = @HAVE_LCHMOD@ HAVE_LCHOWN = @HAVE_LCHOWN@ -HAVE_LDEXPF = @HAVE_LDEXPF@ HAVE_LIBGMP = @HAVE_LIBGMP@ HAVE_LIBSECCOMP = @HAVE_LIBSECCOMP@ HAVE_LINK = @HAVE_LINK@ HAVE_LINKAT = @HAVE_LINKAT@ -HAVE_LOG10F = @HAVE_LOG10F@ -HAVE_LOG10L = @HAVE_LOG10L@ -HAVE_LOG1P = @HAVE_LOG1P@ -HAVE_LOG1PF = @HAVE_LOG1PF@ -HAVE_LOG1PL = @HAVE_LOG1PL@ -HAVE_LOGBF = @HAVE_LOGBF@ -HAVE_LOGBL = @HAVE_LOGBL@ -HAVE_LOGF = @HAVE_LOGF@ -HAVE_LOGL = @HAVE_LOGL@ HAVE_LSTAT = @HAVE_LSTAT@ HAVE_MACPORTS = @HAVE_MACPORTS@ HAVE_MAX_ALIGN_T = @HAVE_MAX_ALIGN_T@ @@ -991,8 +800,6 @@ HAVE_MKOSTEMP = @HAVE_MKOSTEMP@ HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@ HAVE_MKSTEMP = @HAVE_MKSTEMP@ HAVE_MKSTEMPS = @HAVE_MKSTEMPS@ -HAVE_MODFF = @HAVE_MODFF@ -HAVE_MODFL = @HAVE_MODFL@ HAVE_MODULES = @HAVE_MODULES@ HAVE_NANOSLEEP = @HAVE_NANOSLEEP@ HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@ @@ -1012,7 +819,6 @@ HAVE_POSIX_SPAWN = @HAVE_POSIX_SPAWN@ HAVE_POSIX_SPAWNATTR_SETFLAGS = @HAVE_POSIX_SPAWNATTR_SETFLAGS@ HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR = @HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR@ HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP = @HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP@ -HAVE_POWF = @HAVE_POWF@ HAVE_PREAD = @HAVE_PREAD@ HAVE_PSELECT = @HAVE_PSELECT@ HAVE_PTHREAD_SIGMASK = @HAVE_PTHREAD_SIGMASK@ @@ -1030,14 +836,9 @@ HAVE_READLINK = @HAVE_READLINK@ HAVE_READLINKAT = @HAVE_READLINKAT@ HAVE_REALLOCARRAY = @HAVE_REALLOCARRAY@ HAVE_REALPATH = @HAVE_REALPATH@ -HAVE_REMAINDER = @HAVE_REMAINDER@ -HAVE_REMAINDERF = @HAVE_REMAINDERF@ HAVE_RENAMEAT = @HAVE_RENAMEAT@ HAVE_REWINDDIR = @HAVE_REWINDDIR@ -HAVE_RINT = @HAVE_RINT@ -HAVE_RINTL = @HAVE_RINTL@ HAVE_RPMATCH = @HAVE_RPMATCH@ -HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = @HAVE_SAME_LONG_DOUBLE_AS_DOUBLE@ HAVE_SCANDIR = @HAVE_SCANDIR@ HAVE_SECCOMP = @HAVE_SECCOMP@ HAVE_SECURE_GETENV = @HAVE_SECURE_GETENV@ @@ -1053,13 +854,8 @@ HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@ HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@ HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@ HAVE_SIGSET_T = @HAVE_SIGSET_T@ -HAVE_SINF = @HAVE_SINF@ -HAVE_SINHF = @HAVE_SINHF@ -HAVE_SINL = @HAVE_SINL@ HAVE_SLEEP = @HAVE_SLEEP@ HAVE_SPAWN_H = @HAVE_SPAWN_H@ -HAVE_SQRTF = @HAVE_SQRTF@ -HAVE_SQRTL = @HAVE_SQRTL@ HAVE_STDINT_H = @HAVE_STDINT_H@ HAVE_STPCPY = @HAVE_STPCPY@ HAVE_STPNCPY = @HAVE_STPNCPY@ @@ -1090,9 +886,6 @@ HAVE_SYS_RANDOM_H = @HAVE_SYS_RANDOM_H@ HAVE_SYS_SELECT_H = @HAVE_SYS_SELECT_H@ HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@ HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@ -HAVE_TANF = @HAVE_TANF@ -HAVE_TANHF = @HAVE_TANHF@ -HAVE_TANL = @HAVE_TANL@ HAVE_TIMEGM = @HAVE_TIMEGM@ HAVE_TIMESPEC_GET = @HAVE_TIMESPEC_GET@ HAVE_TIMESPEC_GETRES = @HAVE_TIMESPEC_GETRES@ @@ -1227,11 +1020,9 @@ NEXT_AS_FIRST_DIRECTIVE_ASSERT_H = @NEXT_AS_FIRST_DIRECTIVE_ASSERT_H@ NEXT_AS_FIRST_DIRECTIVE_DIRENT_H = @NEXT_AS_FIRST_DIRECTIVE_DIRENT_H@ NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@ NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@ -NEXT_AS_FIRST_DIRECTIVE_FLOAT_H = @NEXT_AS_FIRST_DIRECTIVE_FLOAT_H@ NEXT_AS_FIRST_DIRECTIVE_GETOPT_H = @NEXT_AS_FIRST_DIRECTIVE_GETOPT_H@ NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H = @NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H@ NEXT_AS_FIRST_DIRECTIVE_LIMITS_H = @NEXT_AS_FIRST_DIRECTIVE_LIMITS_H@ -NEXT_AS_FIRST_DIRECTIVE_MATH_H = @NEXT_AS_FIRST_DIRECTIVE_MATH_H@ NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H = @NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H@ NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@ NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@ @@ -1248,11 +1039,9 @@ NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@ NEXT_DIRENT_H = @NEXT_DIRENT_H@ NEXT_ERRNO_H = @NEXT_ERRNO_H@ NEXT_FCNTL_H = @NEXT_FCNTL_H@ -NEXT_FLOAT_H = @NEXT_FLOAT_H@ NEXT_GETOPT_H = @NEXT_GETOPT_H@ NEXT_INTTYPES_H = @NEXT_INTTYPES_H@ NEXT_LIMITS_H = @NEXT_LIMITS_H@ -NEXT_MATH_H = @NEXT_MATH_H@ NEXT_SIGNAL_H = @NEXT_SIGNAL_H@ NEXT_STDDEF_H = @NEXT_STDDEF_H@ NEXT_STDINT_H = @NEXT_STDINT_H@ @@ -1307,26 +1096,15 @@ QCOPY_ACL_LIB = @QCOPY_ACL_LIB@ RALLOC_OBJ = @RALLOC_OBJ@ RANLIB = @RANLIB@ REPLACE_ACCESS = @REPLACE_ACCESS@ -REPLACE_ACOSF = @REPLACE_ACOSF@ REPLACE_ALIGNED_ALLOC = @REPLACE_ALIGNED_ALLOC@ -REPLACE_ASINF = @REPLACE_ASINF@ -REPLACE_ATAN2F = @REPLACE_ATAN2F@ -REPLACE_ATANF = @REPLACE_ATANF@ REPLACE_CALLOC_FOR_CALLOC_GNU = @REPLACE_CALLOC_FOR_CALLOC_GNU@ REPLACE_CALLOC_FOR_CALLOC_POSIX = @REPLACE_CALLOC_FOR_CALLOC_POSIX@ REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@ -REPLACE_CBRTF = @REPLACE_CBRTF@ -REPLACE_CBRTL = @REPLACE_CBRTL@ -REPLACE_CEIL = @REPLACE_CEIL@ -REPLACE_CEILF = @REPLACE_CEILF@ -REPLACE_CEILL = @REPLACE_CEILL@ REPLACE_CHMOD = @REPLACE_CHMOD@ REPLACE_CHOWN = @REPLACE_CHOWN@ REPLACE_CLOSE = @REPLACE_CLOSE@ REPLACE_CLOSEDIR = @REPLACE_CLOSEDIR@ REPLACE_COPY_FILE_RANGE = @REPLACE_COPY_FILE_RANGE@ -REPLACE_COSF = @REPLACE_COSF@ -REPLACE_COSHF = @REPLACE_COSHF@ REPLACE_CREAT = @REPLACE_CREAT@ REPLACE_CTIME = @REPLACE_CTIME@ REPLACE_DIRFD = @REPLACE_DIRFD@ @@ -1341,14 +1119,6 @@ REPLACE_EXECV = @REPLACE_EXECV@ REPLACE_EXECVE = @REPLACE_EXECVE@ REPLACE_EXECVP = @REPLACE_EXECVP@ REPLACE_EXECVPE = @REPLACE_EXECVPE@ -REPLACE_EXP2 = @REPLACE_EXP2@ -REPLACE_EXP2L = @REPLACE_EXP2L@ -REPLACE_EXPF = @REPLACE_EXPF@ -REPLACE_EXPL = @REPLACE_EXPL@ -REPLACE_EXPM1 = @REPLACE_EXPM1@ -REPLACE_EXPM1F = @REPLACE_EXPM1F@ -REPLACE_EXPM1L = @REPLACE_EXPM1L@ -REPLACE_FABSL = @REPLACE_FABSL@ REPLACE_FACCESSAT = @REPLACE_FACCESSAT@ REPLACE_FCHMODAT = @REPLACE_FCHMODAT@ REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@ @@ -1359,24 +1129,12 @@ REPLACE_FDOPEN = @REPLACE_FDOPEN@ REPLACE_FDOPENDIR = @REPLACE_FDOPENDIR@ REPLACE_FFLUSH = @REPLACE_FFLUSH@ REPLACE_FFSLL = @REPLACE_FFSLL@ -REPLACE_FLOOR = @REPLACE_FLOOR@ -REPLACE_FLOORF = @REPLACE_FLOORF@ -REPLACE_FLOORL = @REPLACE_FLOORL@ -REPLACE_FMA = @REPLACE_FMA@ -REPLACE_FMAF = @REPLACE_FMAF@ -REPLACE_FMAL = @REPLACE_FMAL@ -REPLACE_FMOD = @REPLACE_FMOD@ -REPLACE_FMODF = @REPLACE_FMODF@ -REPLACE_FMODL = @REPLACE_FMODL@ REPLACE_FOPEN = @REPLACE_FOPEN@ REPLACE_FOPEN_FOR_FOPEN_GNU = @REPLACE_FOPEN_FOR_FOPEN_GNU@ REPLACE_FPRINTF = @REPLACE_FPRINTF@ REPLACE_FPURGE = @REPLACE_FPURGE@ REPLACE_FREE = @REPLACE_FREE@ REPLACE_FREOPEN = @REPLACE_FREOPEN@ -REPLACE_FREXP = @REPLACE_FREXP@ -REPLACE_FREXPF = @REPLACE_FREXPF@ -REPLACE_FREXPL = @REPLACE_FREXPL@ REPLACE_FSEEK = @REPLACE_FSEEK@ REPLACE_FSEEKO = @REPLACE_FSEEKO@ REPLACE_FSTAT = @REPLACE_FSTAT@ @@ -1402,42 +1160,15 @@ REPLACE_GETRANDOM = @REPLACE_GETRANDOM@ REPLACE_GETSUBOPT = @REPLACE_GETSUBOPT@ REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@ REPLACE_GMTIME = @REPLACE_GMTIME@ -REPLACE_HUGE_VAL = @REPLACE_HUGE_VAL@ -REPLACE_HYPOT = @REPLACE_HYPOT@ -REPLACE_HYPOTF = @REPLACE_HYPOTF@ -REPLACE_HYPOTL = @REPLACE_HYPOTL@ -REPLACE_ILOGB = @REPLACE_ILOGB@ -REPLACE_ILOGBF = @REPLACE_ILOGBF@ -REPLACE_ILOGBL = @REPLACE_ILOGBL@ REPLACE_IMAXABS = @REPLACE_IMAXABS@ REPLACE_IMAXDIV = @REPLACE_IMAXDIV@ REPLACE_INITSTATE = @REPLACE_INITSTATE@ REPLACE_ISATTY = @REPLACE_ISATTY@ -REPLACE_ISFINITE = @REPLACE_ISFINITE@ -REPLACE_ISINF = @REPLACE_ISINF@ -REPLACE_ISNAN = @REPLACE_ISNAN@ -REPLACE_ITOLD = @REPLACE_ITOLD@ REPLACE_LCHOWN = @REPLACE_LCHOWN@ -REPLACE_LDEXPL = @REPLACE_LDEXPL@ REPLACE_LINK = @REPLACE_LINK@ REPLACE_LINKAT = @REPLACE_LINKAT@ REPLACE_LOCALTIME = @REPLACE_LOCALTIME@ REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@ -REPLACE_LOG = @REPLACE_LOG@ -REPLACE_LOG10 = @REPLACE_LOG10@ -REPLACE_LOG10F = @REPLACE_LOG10F@ -REPLACE_LOG10L = @REPLACE_LOG10L@ -REPLACE_LOG1P = @REPLACE_LOG1P@ -REPLACE_LOG1PF = @REPLACE_LOG1PF@ -REPLACE_LOG1PL = @REPLACE_LOG1PL@ -REPLACE_LOG2 = @REPLACE_LOG2@ -REPLACE_LOG2F = @REPLACE_LOG2F@ -REPLACE_LOG2L = @REPLACE_LOG2L@ -REPLACE_LOGB = @REPLACE_LOGB@ -REPLACE_LOGBF = @REPLACE_LOGBF@ -REPLACE_LOGBL = @REPLACE_LOGBL@ -REPLACE_LOGF = @REPLACE_LOGF@ -REPLACE_LOGL = @REPLACE_LOGL@ REPLACE_LSEEK = @REPLACE_LSEEK@ REPLACE_LSTAT = @REPLACE_LSTAT@ REPLACE_MALLOC_FOR_MALLOC_GNU = @REPLACE_MALLOC_FOR_MALLOC_GNU@ @@ -1457,10 +1188,6 @@ REPLACE_MKOSTEMP = @REPLACE_MKOSTEMP@ REPLACE_MKOSTEMPS = @REPLACE_MKOSTEMPS@ REPLACE_MKSTEMP = @REPLACE_MKSTEMP@ REPLACE_MKTIME = @REPLACE_MKTIME@ -REPLACE_MODF = @REPLACE_MODF@ -REPLACE_MODFF = @REPLACE_MODFF@ -REPLACE_MODFL = @REPLACE_MODFL@ -REPLACE_NAN = @REPLACE_NAN@ REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@ REPLACE_NULL = @REPLACE_NULL@ REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@ @@ -1492,31 +1219,18 @@ REPLACE_REALLOCARRAY = @REPLACE_REALLOCARRAY@ REPLACE_REALLOC_FOR_REALLOC_GNU = @REPLACE_REALLOC_FOR_REALLOC_GNU@ REPLACE_REALLOC_FOR_REALLOC_POSIX = @REPLACE_REALLOC_FOR_REALLOC_POSIX@ REPLACE_REALPATH = @REPLACE_REALPATH@ -REPLACE_REMAINDER = @REPLACE_REMAINDER@ -REPLACE_REMAINDERF = @REPLACE_REMAINDERF@ -REPLACE_REMAINDERL = @REPLACE_REMAINDERL@ REPLACE_REMOVE = @REPLACE_REMOVE@ REPLACE_RENAME = @REPLACE_RENAME@ REPLACE_RENAMEAT = @REPLACE_RENAMEAT@ REPLACE_REWINDDIR = @REPLACE_REWINDDIR@ -REPLACE_RINTL = @REPLACE_RINTL@ REPLACE_RMDIR = @REPLACE_RMDIR@ -REPLACE_ROUND = @REPLACE_ROUND@ -REPLACE_ROUNDF = @REPLACE_ROUNDF@ -REPLACE_ROUNDL = @REPLACE_ROUNDL@ REPLACE_SELECT = @REPLACE_SELECT@ REPLACE_SETENV = @REPLACE_SETENV@ REPLACE_SETHOSTNAME = @REPLACE_SETHOSTNAME@ REPLACE_SETSTATE = @REPLACE_SETSTATE@ -REPLACE_SIGNBIT = @REPLACE_SIGNBIT@ -REPLACE_SIGNBIT_USING_BUILTINS = @REPLACE_SIGNBIT_USING_BUILTINS@ -REPLACE_SINF = @REPLACE_SINF@ -REPLACE_SINHF = @REPLACE_SINHF@ REPLACE_SLEEP = @REPLACE_SLEEP@ REPLACE_SNPRINTF = @REPLACE_SNPRINTF@ REPLACE_SPRINTF = @REPLACE_SPRINTF@ -REPLACE_SQRTF = @REPLACE_SQRTF@ -REPLACE_SQRTL = @REPLACE_SQRTL@ REPLACE_STAT = @REPLACE_STAT@ REPLACE_STDIO_READ_FUNCS = @REPLACE_STDIO_READ_FUNCS@ REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@ @@ -1546,16 +1260,11 @@ REPLACE_STRTOUMAX = @REPLACE_STRTOUMAX@ REPLACE_STRUCT_TIMEVAL = @REPLACE_STRUCT_TIMEVAL@ REPLACE_SYMLINK = @REPLACE_SYMLINK@ REPLACE_SYMLINKAT = @REPLACE_SYMLINKAT@ -REPLACE_TANF = @REPLACE_TANF@ -REPLACE_TANHF = @REPLACE_TANHF@ REPLACE_TIME = @REPLACE_TIME@ REPLACE_TIMEGM = @REPLACE_TIMEGM@ REPLACE_TIMESPEC_GET = @REPLACE_TIMESPEC_GET@ REPLACE_TMPFILE = @REPLACE_TMPFILE@ -REPLACE_TRUNC = @REPLACE_TRUNC@ REPLACE_TRUNCATE = @REPLACE_TRUNCATE@ -REPLACE_TRUNCF = @REPLACE_TRUNCF@ -REPLACE_TRUNCL = @REPLACE_TRUNCL@ REPLACE_TTYNAME_R = @REPLACE_TTYNAME_R@ REPLACE_TZSET = @REPLACE_TZSET@ REPLACE_UNLINK = @REPLACE_UNLINK@ @@ -1691,7 +1400,6 @@ gamegroup = @gamegroup@ gameuser = @gameuser@ gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7_CONDITION = @gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7_CONDITION@ gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b_CONDITION = @gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b_CONDITION@ -gl_GNULIB_ENABLED_3f0e593033d1fc2c127581960f641b66_CONDITION = @gl_GNULIB_ENABLED_3f0e593033d1fc2c127581960f641b66_CONDITION@ gl_GNULIB_ENABLED_5264294aa0a5557541b53c8c741f7f31_CONDITION = @gl_GNULIB_ENABLED_5264294aa0a5557541b53c8c741f7f31_CONDITION@ gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c_CONDITION = @gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c_CONDITION@ gl_GNULIB_ENABLED_61bcaca76b3e6f9ae55d57a1c3193bc4_CONDITION = @gl_GNULIB_ENABLED_61bcaca76b3e6f9ae55d57a1c3193bc4_CONDITION@ @@ -1704,22 +1412,17 @@ gl_GNULIB_ENABLED_cloexec_CONDITION = @gl_GNULIB_ENABLED_cloexec_CONDITION@ gl_GNULIB_ENABLED_d3b2383720ee0e541357aa2aac598e2b_CONDITION = @gl_GNULIB_ENABLED_d3b2383720ee0e541357aa2aac598e2b_CONDITION@ gl_GNULIB_ENABLED_dirfd_CONDITION = @gl_GNULIB_ENABLED_dirfd_CONDITION@ gl_GNULIB_ENABLED_e80bf6f757095d2e5fc94dafb8f8fc8b_CONDITION = @gl_GNULIB_ENABLED_e80bf6f757095d2e5fc94dafb8f8fc8b_CONDITION@ -gl_GNULIB_ENABLED_ed5616be3593d355b981ffab56b9f37b_CONDITION = @gl_GNULIB_ENABLED_ed5616be3593d355b981ffab56b9f37b_CONDITION@ gl_GNULIB_ENABLED_ef455225c00f5049c808c2eda3e76866_CONDITION = @gl_GNULIB_ENABLED_ef455225c00f5049c808c2eda3e76866_CONDITION@ gl_GNULIB_ENABLED_euidaccess_CONDITION = @gl_GNULIB_ENABLED_euidaccess_CONDITION@ gl_GNULIB_ENABLED_fd38c7e463b54744b77b98aeafb4fa7c_CONDITION = @gl_GNULIB_ENABLED_fd38c7e463b54744b77b98aeafb4fa7c_CONDITION@ -gl_GNULIB_ENABLED_fseterr_CONDITION = @gl_GNULIB_ENABLED_fseterr_CONDITION@ gl_GNULIB_ENABLED_getdelim_CONDITION = @gl_GNULIB_ENABLED_getdelim_CONDITION@ gl_GNULIB_ENABLED_getdtablesize_CONDITION = @gl_GNULIB_ENABLED_getdtablesize_CONDITION@ gl_GNULIB_ENABLED_getgroups_CONDITION = @gl_GNULIB_ENABLED_getgroups_CONDITION@ gl_GNULIB_ENABLED_lchmod_CONDITION = @gl_GNULIB_ENABLED_lchmod_CONDITION@ gl_GNULIB_ENABLED_open_CONDITION = @gl_GNULIB_ENABLED_open_CONDITION@ gl_GNULIB_ENABLED_rawmemchr_CONDITION = @gl_GNULIB_ENABLED_rawmemchr_CONDITION@ -gl_GNULIB_ENABLED_size_max_CONDITION = @gl_GNULIB_ENABLED_size_max_CONDITION@ gl_GNULIB_ENABLED_strtoll_CONDITION = @gl_GNULIB_ENABLED_strtoll_CONDITION@ gl_GNULIB_ENABLED_utimens_CONDITION = @gl_GNULIB_ENABLED_utimens_CONDITION@ -gl_GNULIB_ENABLED_vasnprintf_CONDITION = @gl_GNULIB_ENABLED_vasnprintf_CONDITION@ -gl_GNULIB_ENABLED_xsize_CONDITION = @gl_GNULIB_ENABLED_xsize_CONDITION@ gl_LIBOBJDEPS = @gl_LIBOBJDEPS@ gl_LIBOBJS = @gl_LIBOBJS@ gl_LTLIBOBJS = @gl_LTLIBOBJS@ @@ -2387,42 +2090,6 @@ EXTRA_DIST += flexmember.h endif ## end gnulib module flexmember -## begin gnulib module float -ifeq (,$(OMIT_GNULIB_MODULE_float)) - -BUILT_SOURCES += $(FLOAT_H) - -# We need the following in order to create when the system -# doesn't have one that works with the given compiler. -ifneq (,$(GL_GENERATE_FLOAT_H_CONDITION)) -float.h: float.in.h $(top_builddir)/config.status - $(gl_V_at)$(SED_HEADER_STDOUT) \ - -e 's|@''GUARD_PREFIX''@|GL|g' \ - -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ - -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ - -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ - -e 's|@''NEXT_FLOAT_H''@|$(NEXT_FLOAT_H)|g' \ - -e 's|@''REPLACE_ITOLD''@|$(REPLACE_ITOLD)|g' \ - $(srcdir)/float.in.h > $@-t - $(AM_V_at)mv $@-t $@ -else -float.h: $(top_builddir)/config.status - rm -f $@ -endif -MOSTLYCLEANFILES += float.h float.h-t - -ifneq (,$(GL_COND_OBJ_FLOAT_CONDITION)) -libgnu_a_SOURCES += float.c -endif -ifneq (,$(GL_COND_OBJ_ITOLD_CONDITION)) -libgnu_a_SOURCES += itold.c -endif - -EXTRA_DIST += float.in.h - -endif -## end gnulib module float - ## begin gnulib module fpending ifeq (,$(OMIT_GNULIB_MODULE_fpending)) @@ -2435,15 +2102,6 @@ EXTRA_DIST += fpending.h stdio-impl.h endif ## end gnulib module fpending -## begin gnulib module fpucw -ifeq (,$(OMIT_GNULIB_MODULE_fpucw)) - - -EXTRA_DIST += fpucw.h - -endif -## end gnulib module fpucw - ## begin gnulib module free-posix ifeq (,$(OMIT_GNULIB_MODULE_free-posix)) @@ -2454,42 +2112,6 @@ endif endif ## end gnulib module free-posix -## begin gnulib module frexp-nolibm -ifeq (,$(OMIT_GNULIB_MODULE_frexp-nolibm)) - - -EXTRA_DIST += frexp.c - -EXTRA_libgnu_a_SOURCES += frexp.c - -endif -## end gnulib module frexp-nolibm - -## begin gnulib module frexpl-nolibm -ifeq (,$(OMIT_GNULIB_MODULE_frexpl-nolibm)) - - -EXTRA_DIST += frexp.c frexpl.c - -EXTRA_libgnu_a_SOURCES += frexp.c frexpl.c - -endif -## end gnulib module frexpl-nolibm - -## begin gnulib module fseterr -ifeq (,$(OMIT_GNULIB_MODULE_fseterr)) - -ifneq (,$(gl_GNULIB_ENABLED_fseterr_CONDITION)) -ifneq (,$(GL_COND_OBJ_FSETERR_CONDITION)) -libgnu_a_SOURCES += fseterr.c -endif - -endif -EXTRA_DIST += fseterr.h stdio-impl.h - -endif -## end gnulib module fseterr - ## begin gnulib module fstatat ifeq (,$(OMIT_GNULIB_MODULE_fstatat)) @@ -2875,41 +2497,6 @@ EXTRA_DIST += inttypes.in.h endif ## end gnulib module inttypes-incomplete -## begin gnulib module isnand-nolibm -ifeq (,$(OMIT_GNULIB_MODULE_isnand-nolibm)) - - -EXTRA_DIST += float+.h isnan.c isnand-nolibm.h isnand.c - -EXTRA_libgnu_a_SOURCES += isnan.c isnand.c - -endif -## end gnulib module isnand-nolibm - -## begin gnulib module isnanf-nolibm -ifeq (,$(OMIT_GNULIB_MODULE_isnanf-nolibm)) - -ifneq (,$(gl_GNULIB_ENABLED_3f0e593033d1fc2c127581960f641b66_CONDITION)) - -endif -EXTRA_DIST += float+.h isnan.c isnanf-nolibm.h isnanf.c - -EXTRA_libgnu_a_SOURCES += isnan.c isnanf.c - -endif -## end gnulib module isnanf-nolibm - -## begin gnulib module isnanl-nolibm -ifeq (,$(OMIT_GNULIB_MODULE_isnanl-nolibm)) - - -EXTRA_DIST += float+.h isnan.c isnanl-nolibm.h isnanl.c - -EXTRA_libgnu_a_SOURCES += isnan.c isnanl.c - -endif -## end gnulib module isnanl-nolibm - ## begin gnulib module lchmod ifeq (,$(OMIT_GNULIB_MODULE_lchmod)) @@ -3030,313 +2617,6 @@ EXTRA_libgnu_a_SOURCES += malloc.c endif ## end gnulib module malloc-posix -## begin gnulib module math -ifeq (,$(OMIT_GNULIB_MODULE_math)) - -BUILT_SOURCES += math.h -libgnu_a_SOURCES += math.c - -# We need the following in order to create when the system -# doesn't have one that works with the given compiler. -math.h: math.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H) - $(gl_V_at)$(SED_HEADER_STDOUT) \ - -e 's|@''GUARD_PREFIX''@|GL|g' \ - -e 's|@''INCLUDE_NEXT_AS_FIRST_DIRECTIVE''@|$(INCLUDE_NEXT_AS_FIRST_DIRECTIVE)|g' \ - -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ - -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ - -e 's|@''NEXT_AS_FIRST_DIRECTIVE_MATH_H''@|$(NEXT_AS_FIRST_DIRECTIVE_MATH_H)|g' \ - -e 's/@''GNULIB_ACOSF''@/$(GL_GNULIB_ACOSF)/g' \ - -e 's/@''GNULIB_ACOSL''@/$(GL_GNULIB_ACOSL)/g' \ - -e 's/@''GNULIB_ASINF''@/$(GL_GNULIB_ASINF)/g' \ - -e 's/@''GNULIB_ASINL''@/$(GL_GNULIB_ASINL)/g' \ - -e 's/@''GNULIB_ATANF''@/$(GL_GNULIB_ATANF)/g' \ - -e 's/@''GNULIB_ATANL''@/$(GL_GNULIB_ATANL)/g' \ - -e 's/@''GNULIB_ATAN2F''@/$(GL_GNULIB_ATAN2F)/g' \ - -e 's/@''GNULIB_CBRT''@/$(GL_GNULIB_CBRT)/g' \ - -e 's/@''GNULIB_CBRTF''@/$(GL_GNULIB_CBRTF)/g' \ - -e 's/@''GNULIB_CBRTL''@/$(GL_GNULIB_CBRTL)/g' \ - -e 's/@''GNULIB_CEIL''@/$(GL_GNULIB_CEIL)/g' \ - -e 's/@''GNULIB_CEILF''@/$(GL_GNULIB_CEILF)/g' \ - -e 's/@''GNULIB_CEILL''@/$(GL_GNULIB_CEILL)/g' \ - -e 's/@''GNULIB_COPYSIGN''@/$(GL_GNULIB_COPYSIGN)/g' \ - -e 's/@''GNULIB_COPYSIGNF''@/$(GL_GNULIB_COPYSIGNF)/g' \ - -e 's/@''GNULIB_COPYSIGNL''@/$(GL_GNULIB_COPYSIGNL)/g' \ - -e 's/@''GNULIB_COSF''@/$(GL_GNULIB_COSF)/g' \ - -e 's/@''GNULIB_COSL''@/$(GL_GNULIB_COSL)/g' \ - -e 's/@''GNULIB_COSHF''@/$(GL_GNULIB_COSHF)/g' \ - -e 's/@''GNULIB_EXPF''@/$(GL_GNULIB_EXPF)/g' \ - -e 's/@''GNULIB_EXPL''@/$(GL_GNULIB_EXPL)/g' \ - -e 's/@''GNULIB_EXP2''@/$(GL_GNULIB_EXP2)/g' \ - -e 's/@''GNULIB_EXP2F''@/$(GL_GNULIB_EXP2F)/g' \ - -e 's/@''GNULIB_EXP2L''@/$(GL_GNULIB_EXP2L)/g' \ - -e 's/@''GNULIB_EXPM1''@/$(GL_GNULIB_EXPM1)/g' \ - -e 's/@''GNULIB_EXPM1F''@/$(GL_GNULIB_EXPM1F)/g' \ - -e 's/@''GNULIB_EXPM1L''@/$(GL_GNULIB_EXPM1L)/g' \ - -e 's/@''GNULIB_FABSF''@/$(GL_GNULIB_FABSF)/g' \ - -e 's/@''GNULIB_FABSL''@/$(GL_GNULIB_FABSL)/g' \ - -e 's/@''GNULIB_FLOOR''@/$(GL_GNULIB_FLOOR)/g' \ - -e 's/@''GNULIB_FLOORF''@/$(GL_GNULIB_FLOORF)/g' \ - -e 's/@''GNULIB_FLOORL''@/$(GL_GNULIB_FLOORL)/g' \ - -e 's/@''GNULIB_FMA''@/$(GL_GNULIB_FMA)/g' \ - -e 's/@''GNULIB_FMAF''@/$(GL_GNULIB_FMAF)/g' \ - -e 's/@''GNULIB_FMAL''@/$(GL_GNULIB_FMAL)/g' \ - -e 's/@''GNULIB_FMOD''@/$(GL_GNULIB_FMOD)/g' \ - -e 's/@''GNULIB_FMODF''@/$(GL_GNULIB_FMODF)/g' \ - -e 's/@''GNULIB_FMODL''@/$(GL_GNULIB_FMODL)/g' \ - -e 's/@''GNULIB_FREXPF''@/$(GL_GNULIB_FREXPF)/g' \ - -e 's/@''GNULIB_FREXP''@/$(GL_GNULIB_FREXP)/g' \ - -e 's/@''GNULIB_FREXPL''@/$(GL_GNULIB_FREXPL)/g' \ - -e 's/@''GNULIB_HYPOT''@/$(GL_GNULIB_HYPOT)/g' \ - -e 's/@''GNULIB_HYPOTF''@/$(GL_GNULIB_HYPOTF)/g' \ - -e 's/@''GNULIB_HYPOTL''@/$(GL_GNULIB_HYPOTL)/g' \ - < $(srcdir)/math.in.h | \ - sed -e 's/@''GNULIB_ILOGB''@/$(GL_GNULIB_ILOGB)/g' \ - -e 's/@''GNULIB_ILOGBF''@/$(GL_GNULIB_ILOGBF)/g' \ - -e 's/@''GNULIB_ILOGBL''@/$(GL_GNULIB_ILOGBL)/g' \ - -e 's/@''GNULIB_ISFINITE''@/$(GL_GNULIB_ISFINITE)/g' \ - -e 's/@''GNULIB_ISINF''@/$(GL_GNULIB_ISINF)/g' \ - -e 's/@''GNULIB_ISNAN''@/$(GL_GNULIB_ISNAN)/g' \ - -e 's/@''GNULIB_ISNANF''@/$(GL_GNULIB_ISNANF)/g' \ - -e 's/@''GNULIB_ISNAND''@/$(GL_GNULIB_ISNAND)/g' \ - -e 's/@''GNULIB_ISNANL''@/$(GL_GNULIB_ISNANL)/g' \ - -e 's/@''GNULIB_LDEXPF''@/$(GL_GNULIB_LDEXPF)/g' \ - -e 's/@''GNULIB_LDEXPL''@/$(GL_GNULIB_LDEXPL)/g' \ - -e 's/@''GNULIB_LOG''@/$(GL_GNULIB_LOG)/g' \ - -e 's/@''GNULIB_LOGF''@/$(GL_GNULIB_LOGF)/g' \ - -e 's/@''GNULIB_LOGL''@/$(GL_GNULIB_LOGL)/g' \ - -e 's/@''GNULIB_LOG10''@/$(GL_GNULIB_LOG10)/g' \ - -e 's/@''GNULIB_LOG10F''@/$(GL_GNULIB_LOG10F)/g' \ - -e 's/@''GNULIB_LOG10L''@/$(GL_GNULIB_LOG10L)/g' \ - -e 's/@''GNULIB_LOG1P''@/$(GL_GNULIB_LOG1P)/g' \ - -e 's/@''GNULIB_LOG1PF''@/$(GL_GNULIB_LOG1PF)/g' \ - -e 's/@''GNULIB_LOG1PL''@/$(GL_GNULIB_LOG1PL)/g' \ - -e 's/@''GNULIB_LOG2''@/$(GL_GNULIB_LOG2)/g' \ - -e 's/@''GNULIB_LOG2F''@/$(GL_GNULIB_LOG2F)/g' \ - -e 's/@''GNULIB_LOG2L''@/$(GL_GNULIB_LOG2L)/g' \ - -e 's/@''GNULIB_LOGB''@/$(GL_GNULIB_LOGB)/g' \ - -e 's/@''GNULIB_LOGBF''@/$(GL_GNULIB_LOGBF)/g' \ - -e 's/@''GNULIB_LOGBL''@/$(GL_GNULIB_LOGBL)/g' \ - -e 's/@''GNULIB_MODF''@/$(GL_GNULIB_MODF)/g' \ - -e 's/@''GNULIB_MODFF''@/$(GL_GNULIB_MODFF)/g' \ - -e 's/@''GNULIB_MODFL''@/$(GL_GNULIB_MODFL)/g' \ - -e 's/@''GNULIB_POWF''@/$(GL_GNULIB_POWF)/g' \ - -e 's/@''GNULIB_REMAINDER''@/$(GL_GNULIB_REMAINDER)/g' \ - -e 's/@''GNULIB_REMAINDERF''@/$(GL_GNULIB_REMAINDERF)/g' \ - -e 's/@''GNULIB_REMAINDERL''@/$(GL_GNULIB_REMAINDERL)/g' \ - -e 's/@''GNULIB_RINT''@/$(GL_GNULIB_RINT)/g' \ - -e 's/@''GNULIB_RINTF''@/$(GL_GNULIB_RINTF)/g' \ - -e 's/@''GNULIB_RINTL''@/$(GL_GNULIB_RINTL)/g' \ - -e 's/@''GNULIB_ROUND''@/$(GL_GNULIB_ROUND)/g' \ - -e 's/@''GNULIB_ROUNDF''@/$(GL_GNULIB_ROUNDF)/g' \ - -e 's/@''GNULIB_ROUNDL''@/$(GL_GNULIB_ROUNDL)/g' \ - -e 's/@''GNULIB_SIGNBIT''@/$(GL_GNULIB_SIGNBIT)/g' \ - -e 's/@''GNULIB_SINF''@/$(GL_GNULIB_SINF)/g' \ - -e 's/@''GNULIB_SINL''@/$(GL_GNULIB_SINL)/g' \ - -e 's/@''GNULIB_SINHF''@/$(GL_GNULIB_SINHF)/g' \ - -e 's/@''GNULIB_SQRTF''@/$(GL_GNULIB_SQRTF)/g' \ - -e 's/@''GNULIB_SQRTL''@/$(GL_GNULIB_SQRTL)/g' \ - -e 's/@''GNULIB_TANF''@/$(GL_GNULIB_TANF)/g' \ - -e 's/@''GNULIB_TANL''@/$(GL_GNULIB_TANL)/g' \ - -e 's/@''GNULIB_TANHF''@/$(GL_GNULIB_TANHF)/g' \ - -e 's/@''GNULIB_TRUNC''@/$(GL_GNULIB_TRUNC)/g' \ - -e 's/@''GNULIB_TRUNCF''@/$(GL_GNULIB_TRUNCF)/g' \ - -e 's/@''GNULIB_TRUNCL''@/$(GL_GNULIB_TRUNCL)/g' \ - -e 's/@''GNULIB_MDA_J0''@/$(GL_GNULIB_MDA_J0)/g' \ - -e 's/@''GNULIB_MDA_J1''@/$(GL_GNULIB_MDA_J1)/g' \ - -e 's/@''GNULIB_MDA_JN''@/$(GL_GNULIB_MDA_JN)/g' \ - -e 's/@''GNULIB_MDA_Y0''@/$(GL_GNULIB_MDA_Y0)/g' \ - -e 's/@''GNULIB_MDA_Y1''@/$(GL_GNULIB_MDA_Y1)/g' \ - -e 's/@''GNULIB_MDA_YN''@/$(GL_GNULIB_MDA_YN)/g' \ - | \ - sed -e 's|@''HAVE_ACOSF''@|$(HAVE_ACOSF)|g' \ - -e 's|@''HAVE_ACOSL''@|$(HAVE_ACOSL)|g' \ - -e 's|@''HAVE_ASINF''@|$(HAVE_ASINF)|g' \ - -e 's|@''HAVE_ASINL''@|$(HAVE_ASINL)|g' \ - -e 's|@''HAVE_ATANF''@|$(HAVE_ATANF)|g' \ - -e 's|@''HAVE_ATANL''@|$(HAVE_ATANL)|g' \ - -e 's|@''HAVE_ATAN2F''@|$(HAVE_ATAN2F)|g' \ - -e 's|@''HAVE_CBRT''@|$(HAVE_CBRT)|g' \ - -e 's|@''HAVE_CBRTF''@|$(HAVE_CBRTF)|g' \ - -e 's|@''HAVE_CBRTL''@|$(HAVE_CBRTL)|g' \ - -e 's|@''HAVE_COPYSIGN''@|$(HAVE_COPYSIGN)|g' \ - -e 's|@''HAVE_COPYSIGNL''@|$(HAVE_COPYSIGNL)|g' \ - -e 's|@''HAVE_COSF''@|$(HAVE_COSF)|g' \ - -e 's|@''HAVE_COSL''@|$(HAVE_COSL)|g' \ - -e 's|@''HAVE_COSHF''@|$(HAVE_COSHF)|g' \ - -e 's|@''HAVE_EXPF''@|$(HAVE_EXPF)|g' \ - -e 's|@''HAVE_EXPL''@|$(HAVE_EXPL)|g' \ - -e 's|@''HAVE_EXPM1''@|$(HAVE_EXPM1)|g' \ - -e 's|@''HAVE_EXPM1F''@|$(HAVE_EXPM1F)|g' \ - -e 's|@''HAVE_FABSF''@|$(HAVE_FABSF)|g' \ - -e 's|@''HAVE_FABSL''@|$(HAVE_FABSL)|g' \ - -e 's|@''HAVE_FMA''@|$(HAVE_FMA)|g' \ - -e 's|@''HAVE_FMAF''@|$(HAVE_FMAF)|g' \ - -e 's|@''HAVE_FMAL''@|$(HAVE_FMAL)|g' \ - -e 's|@''HAVE_FMODF''@|$(HAVE_FMODF)|g' \ - -e 's|@''HAVE_FMODL''@|$(HAVE_FMODL)|g' \ - -e 's|@''HAVE_FREXPF''@|$(HAVE_FREXPF)|g' \ - -e 's|@''HAVE_HYPOTF''@|$(HAVE_HYPOTF)|g' \ - -e 's|@''HAVE_HYPOTL''@|$(HAVE_HYPOTL)|g' \ - -e 's|@''HAVE_ILOGB''@|$(HAVE_ILOGB)|g' \ - -e 's|@''HAVE_ILOGBF''@|$(HAVE_ILOGBF)|g' \ - -e 's|@''HAVE_ILOGBL''@|$(HAVE_ILOGBL)|g' \ - -e 's|@''HAVE_ISNANF''@|$(HAVE_ISNANF)|g' \ - -e 's|@''HAVE_ISNAND''@|$(HAVE_ISNAND)|g' \ - -e 's|@''HAVE_ISNANL''@|$(HAVE_ISNANL)|g' \ - -e 's|@''HAVE_LDEXPF''@|$(HAVE_LDEXPF)|g' \ - -e 's|@''HAVE_LOGF''@|$(HAVE_LOGF)|g' \ - -e 's|@''HAVE_LOGL''@|$(HAVE_LOGL)|g' \ - -e 's|@''HAVE_LOG10F''@|$(HAVE_LOG10F)|g' \ - -e 's|@''HAVE_LOG10L''@|$(HAVE_LOG10L)|g' \ - -e 's|@''HAVE_LOG1P''@|$(HAVE_LOG1P)|g' \ - -e 's|@''HAVE_LOG1PF''@|$(HAVE_LOG1PF)|g' \ - -e 's|@''HAVE_LOG1PL''@|$(HAVE_LOG1PL)|g' \ - -e 's|@''HAVE_LOGBF''@|$(HAVE_LOGBF)|g' \ - -e 's|@''HAVE_LOGBL''@|$(HAVE_LOGBL)|g' \ - -e 's|@''HAVE_MODFF''@|$(HAVE_MODFF)|g' \ - -e 's|@''HAVE_MODFL''@|$(HAVE_MODFL)|g' \ - -e 's|@''HAVE_POWF''@|$(HAVE_POWF)|g' \ - -e 's|@''HAVE_REMAINDER''@|$(HAVE_REMAINDER)|g' \ - -e 's|@''HAVE_REMAINDERF''@|$(HAVE_REMAINDERF)|g' \ - -e 's|@''HAVE_RINT''@|$(HAVE_RINT)|g' \ - -e 's|@''HAVE_RINTL''@|$(HAVE_RINTL)|g' \ - -e 's|@''HAVE_SINF''@|$(HAVE_SINF)|g' \ - -e 's|@''HAVE_SINL''@|$(HAVE_SINL)|g' \ - -e 's|@''HAVE_SINHF''@|$(HAVE_SINHF)|g' \ - -e 's|@''HAVE_SQRTF''@|$(HAVE_SQRTF)|g' \ - -e 's|@''HAVE_SQRTL''@|$(HAVE_SQRTL)|g' \ - -e 's|@''HAVE_TANF''@|$(HAVE_TANF)|g' \ - -e 's|@''HAVE_TANL''@|$(HAVE_TANL)|g' \ - -e 's|@''HAVE_TANHF''@|$(HAVE_TANHF)|g' \ - -e 's|@''HAVE_DECL_ACOSL''@|$(HAVE_DECL_ACOSL)|g' \ - -e 's|@''HAVE_DECL_ASINL''@|$(HAVE_DECL_ASINL)|g' \ - -e 's|@''HAVE_DECL_ATANL''@|$(HAVE_DECL_ATANL)|g' \ - -e 's|@''HAVE_DECL_CBRTF''@|$(HAVE_DECL_CBRTF)|g' \ - -e 's|@''HAVE_DECL_CBRTL''@|$(HAVE_DECL_CBRTL)|g' \ - -e 's|@''HAVE_DECL_CEILF''@|$(HAVE_DECL_CEILF)|g' \ - -e 's|@''HAVE_DECL_CEILL''@|$(HAVE_DECL_CEILL)|g' \ - -e 's|@''HAVE_DECL_COPYSIGNF''@|$(HAVE_DECL_COPYSIGNF)|g' \ - -e 's|@''HAVE_DECL_COSL''@|$(HAVE_DECL_COSL)|g' \ - -e 's|@''HAVE_DECL_EXPL''@|$(HAVE_DECL_EXPL)|g' \ - -e 's|@''HAVE_DECL_EXP2''@|$(HAVE_DECL_EXP2)|g' \ - -e 's|@''HAVE_DECL_EXP2F''@|$(HAVE_DECL_EXP2F)|g' \ - -e 's|@''HAVE_DECL_EXP2L''@|$(HAVE_DECL_EXP2L)|g' \ - -e 's|@''HAVE_DECL_EXPM1L''@|$(HAVE_DECL_EXPM1L)|g' \ - -e 's|@''HAVE_DECL_FLOORF''@|$(HAVE_DECL_FLOORF)|g' \ - -e 's|@''HAVE_DECL_FLOORL''@|$(HAVE_DECL_FLOORL)|g' \ - -e 's|@''HAVE_DECL_FREXPL''@|$(HAVE_DECL_FREXPL)|g' \ - -e 's|@''HAVE_DECL_LDEXPL''@|$(HAVE_DECL_LDEXPL)|g' \ - -e 's|@''HAVE_DECL_LOGL''@|$(HAVE_DECL_LOGL)|g' \ - -e 's|@''HAVE_DECL_LOG10L''@|$(HAVE_DECL_LOG10L)|g' \ - -e 's|@''HAVE_DECL_LOG2''@|$(HAVE_DECL_LOG2)|g' \ - -e 's|@''HAVE_DECL_LOG2F''@|$(HAVE_DECL_LOG2F)|g' \ - -e 's|@''HAVE_DECL_LOG2L''@|$(HAVE_DECL_LOG2L)|g' \ - -e 's|@''HAVE_DECL_LOGB''@|$(HAVE_DECL_LOGB)|g' \ - -e 's|@''HAVE_DECL_REMAINDER''@|$(HAVE_DECL_REMAINDER)|g' \ - -e 's|@''HAVE_DECL_REMAINDERL''@|$(HAVE_DECL_REMAINDERL)|g' \ - -e 's|@''HAVE_DECL_RINTF''@|$(HAVE_DECL_RINTF)|g' \ - -e 's|@''HAVE_DECL_ROUND''@|$(HAVE_DECL_ROUND)|g' \ - -e 's|@''HAVE_DECL_ROUNDF''@|$(HAVE_DECL_ROUNDF)|g' \ - -e 's|@''HAVE_DECL_ROUNDL''@|$(HAVE_DECL_ROUNDL)|g' \ - -e 's|@''HAVE_DECL_SINL''@|$(HAVE_DECL_SINL)|g' \ - -e 's|@''HAVE_DECL_SQRTL''@|$(HAVE_DECL_SQRTL)|g' \ - -e 's|@''HAVE_DECL_TANL''@|$(HAVE_DECL_TANL)|g' \ - -e 's|@''HAVE_DECL_TRUNC''@|$(HAVE_DECL_TRUNC)|g' \ - -e 's|@''HAVE_DECL_TRUNCF''@|$(HAVE_DECL_TRUNCF)|g' \ - -e 's|@''HAVE_DECL_TRUNCL''@|$(HAVE_DECL_TRUNCL)|g' \ - | \ - sed -e 's|@''REPLACE_ACOSF''@|$(REPLACE_ACOSF)|g' \ - -e 's|@''REPLACE_ASINF''@|$(REPLACE_ASINF)|g' \ - -e 's|@''REPLACE_ATANF''@|$(REPLACE_ATANF)|g' \ - -e 's|@''REPLACE_ATAN2F''@|$(REPLACE_ATAN2F)|g' \ - -e 's|@''REPLACE_CBRTF''@|$(REPLACE_CBRTF)|g' \ - -e 's|@''REPLACE_CBRTL''@|$(REPLACE_CBRTL)|g' \ - -e 's|@''REPLACE_CEIL''@|$(REPLACE_CEIL)|g' \ - -e 's|@''REPLACE_CEILF''@|$(REPLACE_CEILF)|g' \ - -e 's|@''REPLACE_CEILL''@|$(REPLACE_CEILL)|g' \ - -e 's|@''REPLACE_COSF''@|$(REPLACE_COSF)|g' \ - -e 's|@''REPLACE_COSHF''@|$(REPLACE_COSHF)|g' \ - -e 's|@''REPLACE_EXPF''@|$(REPLACE_EXPF)|g' \ - -e 's|@''REPLACE_EXPL''@|$(REPLACE_EXPL)|g' \ - -e 's|@''REPLACE_EXPM1''@|$(REPLACE_EXPM1)|g' \ - -e 's|@''REPLACE_EXPM1F''@|$(REPLACE_EXPM1F)|g' \ - -e 's|@''REPLACE_EXPM1L''@|$(REPLACE_EXPM1L)|g' \ - -e 's|@''REPLACE_EXP2''@|$(REPLACE_EXP2)|g' \ - -e 's|@''REPLACE_EXP2L''@|$(REPLACE_EXP2L)|g' \ - -e 's|@''REPLACE_FABSL''@|$(REPLACE_FABSL)|g' \ - -e 's|@''REPLACE_FLOOR''@|$(REPLACE_FLOOR)|g' \ - -e 's|@''REPLACE_FLOORF''@|$(REPLACE_FLOORF)|g' \ - -e 's|@''REPLACE_FLOORL''@|$(REPLACE_FLOORL)|g' \ - -e 's|@''REPLACE_FMA''@|$(REPLACE_FMA)|g' \ - -e 's|@''REPLACE_FMAF''@|$(REPLACE_FMAF)|g' \ - -e 's|@''REPLACE_FMAL''@|$(REPLACE_FMAL)|g' \ - -e 's|@''REPLACE_FMOD''@|$(REPLACE_FMOD)|g' \ - -e 's|@''REPLACE_FMODF''@|$(REPLACE_FMODF)|g' \ - -e 's|@''REPLACE_FMODL''@|$(REPLACE_FMODL)|g' \ - -e 's|@''REPLACE_FREXPF''@|$(REPLACE_FREXPF)|g' \ - -e 's|@''REPLACE_FREXP''@|$(REPLACE_FREXP)|g' \ - -e 's|@''REPLACE_FREXPL''@|$(REPLACE_FREXPL)|g' \ - -e 's|@''REPLACE_HUGE_VAL''@|$(REPLACE_HUGE_VAL)|g' \ - -e 's|@''REPLACE_HYPOT''@|$(REPLACE_HYPOT)|g' \ - -e 's|@''REPLACE_HYPOTF''@|$(REPLACE_HYPOTF)|g' \ - -e 's|@''REPLACE_HYPOTL''@|$(REPLACE_HYPOTL)|g' \ - -e 's|@''REPLACE_ILOGB''@|$(REPLACE_ILOGB)|g' \ - -e 's|@''REPLACE_ILOGBF''@|$(REPLACE_ILOGBF)|g' \ - -e 's|@''REPLACE_ILOGBL''@|$(REPLACE_ILOGBL)|g' \ - -e 's|@''REPLACE_ISFINITE''@|$(REPLACE_ISFINITE)|g' \ - -e 's|@''REPLACE_ISINF''@|$(REPLACE_ISINF)|g' \ - -e 's|@''REPLACE_ISNAN''@|$(REPLACE_ISNAN)|g' \ - -e 's|@''REPLACE_ITOLD''@|$(REPLACE_ITOLD)|g' \ - -e 's|@''REPLACE_LDEXPL''@|$(REPLACE_LDEXPL)|g' \ - -e 's|@''REPLACE_LOG''@|$(REPLACE_LOG)|g' \ - -e 's|@''REPLACE_LOGF''@|$(REPLACE_LOGF)|g' \ - -e 's|@''REPLACE_LOGL''@|$(REPLACE_LOGL)|g' \ - -e 's|@''REPLACE_LOG10''@|$(REPLACE_LOG10)|g' \ - -e 's|@''REPLACE_LOG10F''@|$(REPLACE_LOG10F)|g' \ - -e 's|@''REPLACE_LOG10L''@|$(REPLACE_LOG10L)|g' \ - -e 's|@''REPLACE_LOG1P''@|$(REPLACE_LOG1P)|g' \ - -e 's|@''REPLACE_LOG1PF''@|$(REPLACE_LOG1PF)|g' \ - -e 's|@''REPLACE_LOG1PL''@|$(REPLACE_LOG1PL)|g' \ - -e 's|@''REPLACE_LOG2''@|$(REPLACE_LOG2)|g' \ - -e 's|@''REPLACE_LOG2F''@|$(REPLACE_LOG2F)|g' \ - -e 's|@''REPLACE_LOG2L''@|$(REPLACE_LOG2L)|g' \ - -e 's|@''REPLACE_LOGB''@|$(REPLACE_LOGB)|g' \ - -e 's|@''REPLACE_LOGBF''@|$(REPLACE_LOGBF)|g' \ - -e 's|@''REPLACE_LOGBL''@|$(REPLACE_LOGBL)|g' \ - -e 's|@''REPLACE_MODF''@|$(REPLACE_MODF)|g' \ - -e 's|@''REPLACE_MODFF''@|$(REPLACE_MODFF)|g' \ - -e 's|@''REPLACE_MODFL''@|$(REPLACE_MODFL)|g' \ - -e 's|@''REPLACE_NAN''@|$(REPLACE_NAN)|g' \ - -e 's|@''REPLACE_REMAINDER''@|$(REPLACE_REMAINDER)|g' \ - -e 's|@''REPLACE_REMAINDERF''@|$(REPLACE_REMAINDERF)|g' \ - -e 's|@''REPLACE_REMAINDERL''@|$(REPLACE_REMAINDERL)|g' \ - -e 's|@''REPLACE_RINTL''@|$(REPLACE_RINTL)|g' \ - -e 's|@''REPLACE_ROUND''@|$(REPLACE_ROUND)|g' \ - -e 's|@''REPLACE_ROUNDF''@|$(REPLACE_ROUNDF)|g' \ - -e 's|@''REPLACE_ROUNDL''@|$(REPLACE_ROUNDL)|g' \ - -e 's|@''REPLACE_SIGNBIT''@|$(REPLACE_SIGNBIT)|g' \ - -e 's|@''REPLACE_SIGNBIT_USING_BUILTINS''@|$(REPLACE_SIGNBIT_USING_BUILTINS)|g' \ - -e 's|@''REPLACE_SINF''@|$(REPLACE_SINF)|g' \ - -e 's|@''REPLACE_SINHF''@|$(REPLACE_SINHF)|g' \ - -e 's|@''REPLACE_SQRTF''@|$(REPLACE_SQRTF)|g' \ - -e 's|@''REPLACE_SQRTL''@|$(REPLACE_SQRTL)|g' \ - -e 's|@''REPLACE_TANF''@|$(REPLACE_TANF)|g' \ - -e 's|@''REPLACE_TANHF''@|$(REPLACE_TANHF)|g' \ - -e 's|@''REPLACE_TRUNC''@|$(REPLACE_TRUNC)|g' \ - -e 's|@''REPLACE_TRUNCF''@|$(REPLACE_TRUNCF)|g' \ - -e 's|@''REPLACE_TRUNCL''@|$(REPLACE_TRUNCL)|g' \ - -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ - -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ - -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ - > $@-t - $(AM_V_at)mv $@-t $@ -MOSTLYCLEANFILES += math.h math.h-t - -EXTRA_DIST += math.in.h - -endif -## end gnulib module math - ## begin gnulib module memmem-simple ifeq (,$(OMIT_GNULIB_MODULE_memmem-simple)) @@ -3490,39 +2770,6 @@ libgnu_a_SOURCES += pipe2.c endif ## end gnulib module pipe2 -## begin gnulib module printf-frexp -ifeq (,$(OMIT_GNULIB_MODULE_printf-frexp)) - -libgnu_a_SOURCES += printf-frexp.c - -EXTRA_DIST += printf-frexp.h - -endif -## end gnulib module printf-frexp - -## begin gnulib module printf-frexpl -ifeq (,$(OMIT_GNULIB_MODULE_printf-frexpl)) - -libgnu_a_SOURCES += printf-frexpl.c - -EXTRA_DIST += printf-frexp.c printf-frexpl.h - -EXTRA_libgnu_a_SOURCES += printf-frexp.c - -endif -## end gnulib module printf-frexpl - -## begin gnulib module printf-posix -ifeq (,$(OMIT_GNULIB_MODULE_printf-posix)) - - -EXTRA_DIST += printf.c - -EXTRA_libgnu_a_SOURCES += printf.c - -endif -## end gnulib module printf-posix - ## begin gnulib module pselect ifeq (,$(OMIT_GNULIB_MODULE_pselect)) @@ -3704,28 +2951,6 @@ EXTRA_DIST += signal.in.h endif ## end gnulib module signal-h -## begin gnulib module signbit -ifeq (,$(OMIT_GNULIB_MODULE_signbit)) - -ifneq (,$(GL_COND_OBJ_SIGNBIT3_CONDITION)) -libgnu_a_SOURCES += signbitf.c signbitd.c signbitl.c -endif - -EXTRA_DIST += float+.h - -endif -## end gnulib module signbit - -## begin gnulib module size_max -ifeq (,$(OMIT_GNULIB_MODULE_size_max)) - -ifneq (,$(gl_GNULIB_ENABLED_size_max_CONDITION)) -libgnu_a_SOURCES += size_max.h - -endif -endif -## end gnulib module size_max - ## begin gnulib module snippet/_Noreturn ifeq (,$(OMIT_GNULIB_MODULE_snippet/_Noreturn)) @@ -3970,8 +3195,9 @@ stdio.h: stdio.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) -e 's/@''GNULIB_MDA_GETW''@/$(GL_GNULIB_MDA_GETW)/g' \ -e 's/@''GNULIB_MDA_PUTW''@/$(GL_GNULIB_MDA_PUTW)/g' \ -e 's/@''GNULIB_MDA_TEMPNAM''@/$(GL_GNULIB_MDA_TEMPNAM)/g' \ - < $(srcdir)/stdio.in.h | \ - sed -e 's|@''HAVE_DECL_FCLOSEALL''@|$(HAVE_DECL_FCLOSEALL)|g' \ + < $(srcdir)/stdio.in.h > $@-t1 + $(AM_V_at)sed \ + -e 's|@''HAVE_DECL_FCLOSEALL''@|$(HAVE_DECL_FCLOSEALL)|g' \ -e 's|@''HAVE_DECL_FPURGE''@|$(HAVE_DECL_FPURGE)|g' \ -e 's|@''HAVE_DECL_FSEEKO''@|$(HAVE_DECL_FSEEKO)|g' \ -e 's|@''HAVE_DECL_FTELLO''@|$(HAVE_DECL_FTELLO)|g' \ @@ -3990,6 +3216,8 @@ stdio.h: stdio.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) -e 's|@''HAVE_RENAMEAT''@|$(HAVE_RENAMEAT)|g' \ -e 's|@''HAVE_VASPRINTF''@|$(HAVE_VASPRINTF)|g' \ -e 's|@''HAVE_VDPRINTF''@|$(HAVE_VDPRINTF)|g' \ + < $@-t1 > $@-t2 + $(AM_V_at)sed \ -e 's|@''REPLACE_DPRINTF''@|$(REPLACE_DPRINTF)|g' \ -e 's|@''REPLACE_FCLOSE''@|$(REPLACE_FCLOSE)|g' \ -e 's|@''REPLACE_FDOPEN''@|$(REPLACE_FDOPEN)|g' \ @@ -4027,9 +3255,10 @@ stdio.h: stdio.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ - > $@-t - $(AM_V_at)mv $@-t $@ -MOSTLYCLEANFILES += stdio.h stdio.h-t + < $@-t2 > $@-t3 + $(AM_V_at)rm -f $@-t1 $@-t2 + $(AM_V_at)mv $@-t3 $@ +MOSTLYCLEANFILES += stdio.h stdio.h-t1 stdio.h-t2 stdio.h-t3 ifneq (,$(GL_COND_OBJ_STDIO_READ_CONDITION)) libgnu_a_SOURCES += stdio-read.c @@ -4108,8 +3337,9 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \ -e 's/@''GNULIB_MDA_GCVT''@/$(GL_GNULIB_MDA_GCVT)/g' \ -e 's/@''GNULIB_MDA_MKTEMP''@/$(GL_GNULIB_MDA_MKTEMP)/g' \ -e 's/@''GNULIB_MDA_PUTENV''@/$(GL_GNULIB_MDA_PUTENV)/g' \ - < $(srcdir)/stdlib.in.h | \ - sed -e 's|@''HAVE__EXIT''@|$(HAVE__EXIT)|g' \ + < $(srcdir)/stdlib.in.h > $@-t1 + $(AM_V_at)sed \ + -e 's|@''HAVE__EXIT''@|$(HAVE__EXIT)|g' \ -e 's|@''HAVE_ALIGNED_ALLOC''@|$(HAVE_ALIGNED_ALLOC)|g' \ -e 's|@''HAVE_ATOLL''@|$(HAVE_ATOLL)|g' \ -e 's|@''HAVE_CANONICALIZE_FILE_NAME''@|$(HAVE_CANONICALIZE_FILE_NAME)|g' \ @@ -4154,6 +3384,8 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \ -e 's|@''HAVE_SYS_LOADAVG_H''@|$(HAVE_SYS_LOADAVG_H)|g' \ -e 's|@''HAVE_UNLOCKPT''@|$(HAVE_UNLOCKPT)|g' \ -e 's|@''HAVE_DECL_UNSETENV''@|$(HAVE_DECL_UNSETENV)|g' \ + < $@-t1 > $@-t2 + $(AM_V_at)sed \ -e 's|@''REPLACE__EXIT''@|$(REPLACE__EXIT)|g' \ -e 's|@''REPLACE_ALIGNED_ALLOC''@|$(REPLACE_ALIGNED_ALLOC)|g' \ -e 's|@''REPLACE_CALLOC_FOR_CALLOC_GNU''@|$(REPLACE_CALLOC_FOR_CALLOC_GNU)|g' \ @@ -4198,9 +3430,10 @@ stdlib.h: stdlib.in.h $(top_builddir)/config.status $(CXXDEFS_H) \ -e '/definition of _Noreturn/r $(_NORETURN_H)' \ -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ - > $@-t - $(AM_V_at)mv $@-t $@ -MOSTLYCLEANFILES += stdlib.h stdlib.h-t + < $@-t2 > $@-t3 + $(AM_V_at)rm -f $@-t1 $@-t2 + $(AM_V_at)mv $@-t3 $@ +MOSTLYCLEANFILES += stdlib.h stdlib.h-t1 stdlib.h-t2 stdlib.h-t3 EXTRA_DIST += stdlib.in.h @@ -4286,8 +3519,9 @@ string.h: string.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e 's/@''GNULIB_MDA_MEMCCPY''@/$(GL_GNULIB_MDA_MEMCCPY)/g' \ -e 's/@''GNULIB_MDA_STRDUP''@/$(GL_GNULIB_MDA_STRDUP)/g' \ -e 's/@''GNULIB_FREE_POSIX''@/$(GL_GNULIB_FREE_POSIX)/g' \ - < $(srcdir)/string.in.h | \ - sed -e 's|@''HAVE_EXPLICIT_BZERO''@|$(HAVE_EXPLICIT_BZERO)|g' \ + < $(srcdir)/string.in.h > $@-t1 + $(AM_V_at)sed \ + -e 's|@''HAVE_EXPLICIT_BZERO''@|$(HAVE_EXPLICIT_BZERO)|g' \ -e 's|@''HAVE_FFSL''@|$(HAVE_FFSL)|g' \ -e 's|@''HAVE_FFSLL''@|$(HAVE_FFSLL)|g' \ -e 's|@''HAVE_MBSLEN''@|$(HAVE_MBSLEN)|g' \ @@ -4335,9 +3569,10 @@ string.h: string.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ - > $@-t - $(AM_V_at)mv $@-t $@ -MOSTLYCLEANFILES += string.h string.h-t + < $@-t1 > $@-t2 + $(AM_V_at)rm -f $@-t1 + $(AM_V_at)mv $@-t2 $@ +MOSTLYCLEANFILES += string.h string.h-t1 string.h-t2 EXTRA_DIST += string.in.h @@ -4774,6 +4009,8 @@ unistd.h: unistd.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e 's/@''GNULIB_FDATASYNC''@/$(GL_GNULIB_FDATASYNC)/g' \ -e 's/@''GNULIB_FSYNC''@/$(GL_GNULIB_FSYNC)/g' \ -e 's/@''GNULIB_FTRUNCATE''@/$(GL_GNULIB_FTRUNCATE)/g' \ + < $(srcdir)/unistd.in.h > $@-t1 + $(AM_V_at)sed \ -e 's/@''GNULIB_GETCWD''@/$(GL_GNULIB_GETCWD)/g' \ -e 's/@''GNULIB_GETDOMAINNAME''@/$(GL_GNULIB_GETDOMAINNAME)/g' \ -e 's/@''GNULIB_GETDTABLESIZE''@/$(GL_GNULIB_GETDTABLESIZE)/g' \ @@ -4835,8 +4072,9 @@ unistd.h: unistd.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e 's/@''GNULIB_MDA_SWAB''@/$(GL_GNULIB_MDA_SWAB)/g' \ -e 's/@''GNULIB_MDA_UNLINK''@/$(GL_GNULIB_MDA_UNLINK)/g' \ -e 's/@''GNULIB_MDA_WRITE''@/$(GL_GNULIB_MDA_WRITE)/g' \ - < $(srcdir)/unistd.in.h | \ - sed -e 's|@''HAVE_CHOWN''@|$(HAVE_CHOWN)|g' \ + < $@-t1 > $@-t2 + $(AM_V_at)sed \ + -e 's|@''HAVE_CHOWN''@|$(HAVE_CHOWN)|g' \ -e 's|@''HAVE_COPY_FILE_RANGE''@|$(HAVE_COPY_FILE_RANGE)|g' \ -e 's|@''HAVE_DUP3''@|$(HAVE_DUP3)|g' \ -e 's|@''HAVE_EUIDACCESS''@|$(HAVE_EUIDACCESS)|g' \ @@ -4883,8 +4121,9 @@ unistd.h: unistd.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e 's|@''HAVE_DECL_TTYNAME_R''@|$(HAVE_DECL_TTYNAME_R)|g' \ -e 's|@''HAVE_OS_H''@|$(HAVE_OS_H)|g' \ -e 's|@''HAVE_SYS_PARAM_H''@|$(HAVE_SYS_PARAM_H)|g' \ - | \ - sed -e 's|@''REPLACE_ACCESS''@|$(REPLACE_ACCESS)|g' \ + < $@-t2 > $@-t3 + $(AM_V_at)sed \ + -e 's|@''REPLACE_ACCESS''@|$(REPLACE_ACCESS)|g' \ -e 's|@''REPLACE_CHOWN''@|$(REPLACE_CHOWN)|g' \ -e 's|@''REPLACE_CLOSE''@|$(REPLACE_CLOSE)|g' \ -e 's|@''REPLACE_COPY_FILE_RANGE''@|$(REPLACE_COPY_FILE_RANGE)|g' \ @@ -4939,9 +4178,10 @@ unistd.h: unistd.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ - > $@-t - $(AM_V_at)mv $@-t $@ -MOSTLYCLEANFILES += unistd.h unistd.h-t + < $@-t3 > $@-t4 + $(AM_V_at)rm -f $@-t1 $@-t2 $@-t3 + $(AM_V_at)mv $@-t4 $@ +MOSTLYCLEANFILES += unistd.h unistd.h-t1 unistd.h-t2 unistd.h-t3 unistd.h-t4 EXTRA_DIST += unistd.in.h @@ -4992,30 +4232,6 @@ EXTRA_libgnu_a_SOURCES += at-func.c endif ## end gnulib module utimensat -## begin gnulib module vasnprintf -ifeq (,$(OMIT_GNULIB_MODULE_vasnprintf)) - -ifneq (,$(gl_GNULIB_ENABLED_vasnprintf_CONDITION)) - -endif -EXTRA_DIST += asnprintf.c float+.h printf-args.c printf-args.h printf-parse.c printf-parse.h vasnprintf.c vasnprintf.h - -EXTRA_libgnu_a_SOURCES += asnprintf.c printf-args.c printf-parse.c vasnprintf.c - -endif -## end gnulib module vasnprintf - -## begin gnulib module vasprintf -ifeq (,$(OMIT_GNULIB_MODULE_vasprintf)) - - -EXTRA_DIST += asprintf.c vasprintf.c - -EXTRA_libgnu_a_SOURCES += asprintf.c vasprintf.c - -endif -## end gnulib module vasprintf - ## begin gnulib module verify ifeq (,$(OMIT_GNULIB_MODULE_verify)) @@ -5025,19 +4241,6 @@ EXTRA_DIST += verify.h endif ## end gnulib module verify -## begin gnulib module vfprintf-posix -ifeq (,$(OMIT_GNULIB_MODULE_vfprintf-posix)) - -ifneq (,$(gl_GNULIB_ENABLED_ed5616be3593d355b981ffab56b9f37b_CONDITION)) - -endif -EXTRA_DIST += vfprintf.c - -EXTRA_libgnu_a_SOURCES += vfprintf.c - -endif -## end gnulib module vfprintf-posix - ## begin gnulib module vla ifeq (,$(OMIT_GNULIB_MODULE_vla)) @@ -5058,16 +4261,6 @@ EXTRA_DIST += xalloc-oversized.h endif ## end gnulib module xalloc-oversized -## begin gnulib module xsize -ifeq (,$(OMIT_GNULIB_MODULE_xsize)) - -ifneq (,$(gl_GNULIB_ENABLED_xsize_CONDITION)) -libgnu_a_SOURCES += xsize.h xsize.c - -endif -endif -## end gnulib module xsize - mostlyclean-local: mostlyclean-generic @for dir in '' $(MOSTLYCLEANDIRS); do \ diff --git a/lib/isnan.c b/lib/isnan.c deleted file mode 100644 index 39e193b9606..00000000000 --- a/lib/isnan.c +++ /dev/null @@ -1,190 +0,0 @@ -/* Test for NaN that does not need libm. - Copyright (C) 2007-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by Bruno Haible , 2007. */ - -#include - -/* Specification. */ -#ifdef USE_LONG_DOUBLE -/* Specification found in math.h or isnanl-nolibm.h. */ -extern int rpl_isnanl (long double x) _GL_ATTRIBUTE_CONST; -#elif ! defined USE_FLOAT -/* Specification found in math.h or isnand-nolibm.h. */ -extern int rpl_isnand (double x); -#else /* defined USE_FLOAT */ -/* Specification found in math.h or isnanf-nolibm.h. */ -extern int rpl_isnanf (float x); -#endif - -#include -#include - -#include "float+.h" - -#ifdef USE_LONG_DOUBLE -# define FUNC rpl_isnanl -# define DOUBLE long double -# define MAX_EXP LDBL_MAX_EXP -# define MIN_EXP LDBL_MIN_EXP -# if defined LDBL_EXPBIT0_WORD && defined LDBL_EXPBIT0_BIT -# define KNOWN_EXPBIT0_LOCATION -# define EXPBIT0_WORD LDBL_EXPBIT0_WORD -# define EXPBIT0_BIT LDBL_EXPBIT0_BIT -# endif -# define SIZE SIZEOF_LDBL -# define L_(literal) literal##L -#elif ! defined USE_FLOAT -# define FUNC rpl_isnand -# define DOUBLE double -# define MAX_EXP DBL_MAX_EXP -# define MIN_EXP DBL_MIN_EXP -# if defined DBL_EXPBIT0_WORD && defined DBL_EXPBIT0_BIT -# define KNOWN_EXPBIT0_LOCATION -# define EXPBIT0_WORD DBL_EXPBIT0_WORD -# define EXPBIT0_BIT DBL_EXPBIT0_BIT -# endif -# define SIZE SIZEOF_DBL -# define L_(literal) literal -#else /* defined USE_FLOAT */ -# define FUNC rpl_isnanf -# define DOUBLE float -# define MAX_EXP FLT_MAX_EXP -# define MIN_EXP FLT_MIN_EXP -# if defined FLT_EXPBIT0_WORD && defined FLT_EXPBIT0_BIT -# define KNOWN_EXPBIT0_LOCATION -# define EXPBIT0_WORD FLT_EXPBIT0_WORD -# define EXPBIT0_BIT FLT_EXPBIT0_BIT -# endif -# define SIZE SIZEOF_FLT -# define L_(literal) literal##f -#endif - -#define EXP_MASK ((MAX_EXP - MIN_EXP) | 7) - -#define NWORDS \ - ((sizeof (DOUBLE) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) -typedef union { DOUBLE value; unsigned int word[NWORDS]; } memory_double; - -/* Most hosts nowadays use IEEE floating point, so they use IEC 60559 - representations, have infinities and NaNs, and do not trap on - exceptions. Define IEEE_FLOATING_POINT if this host is one of the - typical ones. The C23 macro __STDC_IEC_60559_BFP__ macro (or its cousin, - the now-obsolescent C11 macro __STDC_IEC_559__) is close to what is - wanted here, but is not quite right because this file does not require - all the features of C23 Annex F (and works even with pre-C11 platforms, - for that matter). */ - -#define IEEE_FLOATING_POINT (FLT_RADIX == 2 && FLT_MANT_DIG == 24 \ - && FLT_MIN_EXP == -125 && FLT_MAX_EXP == 128) - -int -FUNC (DOUBLE x) -{ -#if defined KNOWN_EXPBIT0_LOCATION && IEEE_FLOATING_POINT -# if defined USE_LONG_DOUBLE && ((defined __ia64 && LDBL_MANT_DIG == 64) || (defined __x86_64__ || defined __amd64__) || (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_)) && !HAVE_SAME_LONG_DOUBLE_AS_DOUBLE - /* Special CPU dependent code is needed to treat bit patterns outside the - IEEE 754 specification (such as Pseudo-NaNs, Pseudo-Infinities, - Pseudo-Zeroes, Unnormalized Numbers, and Pseudo-Denormals) as NaNs. - These bit patterns are: - - exponent = 0x0001..0x7FFF, mantissa bit 63 = 0, - - exponent = 0x0000, mantissa bit 63 = 1. - The NaN bit pattern is: - - exponent = 0x7FFF, mantissa >= 0x8000000000000001. */ - memory_double m; - unsigned int exponent; - - m.value = x; - exponent = (m.word[EXPBIT0_WORD] >> EXPBIT0_BIT) & EXP_MASK; -# ifdef WORDS_BIGENDIAN - /* Big endian: EXPBIT0_WORD = 0, EXPBIT0_BIT = 16. */ - if (exponent == 0) - return 1 & (m.word[0] >> 15); - else if (exponent == EXP_MASK) - return (((m.word[0] ^ 0x8000U) << 16) | m.word[1] | (m.word[2] >> 16)) != 0; - else - return 1 & ~(m.word[0] >> 15); -# else - /* Little endian: EXPBIT0_WORD = 2, EXPBIT0_BIT = 0. */ - if (exponent == 0) - return (m.word[1] >> 31); - else if (exponent == EXP_MASK) - return ((m.word[1] ^ 0x80000000U) | m.word[0]) != 0; - else - return (m.word[1] >> 31) ^ 1; -# endif -# else - /* Be careful to not do any floating-point operation on x, such as x == x, - because x may be a signaling NaN. */ -# if defined __SUNPRO_C || defined __ICC || defined _MSC_VER \ - || defined __DECC || defined __TINYC__ \ - || (defined __sgi && !defined __GNUC__) - /* The Sun C 5.0, Intel ICC 10.0, Microsoft Visual C/C++ 9.0, Compaq (ex-DEC) - 6.4, and TinyCC compilers don't recognize the initializers as constant - expressions. The Compaq compiler also fails when constant-folding - 0.0 / 0.0 even when constant-folding is not required. The Microsoft - Visual C/C++ compiler also fails when constant-folding 1.0 / 0.0 even - when constant-folding is not required. The SGI MIPSpro C compiler - complains about "floating-point operation result is out of range". */ - static DOUBLE zero = L_(0.0); - memory_double nan; - DOUBLE plus_inf = L_(1.0) / zero; - DOUBLE minus_inf = -L_(1.0) / zero; - nan.value = zero / zero; -# else - static memory_double nan = { L_(0.0) / L_(0.0) }; - static DOUBLE plus_inf = L_(1.0) / L_(0.0); - static DOUBLE minus_inf = -L_(1.0) / L_(0.0); -# endif - { - memory_double m; - - /* A NaN can be recognized through its exponent. But exclude +Infinity and - -Infinity, which have the same exponent. */ - m.value = x; - if (((m.word[EXPBIT0_WORD] ^ nan.word[EXPBIT0_WORD]) - & (EXP_MASK << EXPBIT0_BIT)) - == 0) - return (memcmp (&m.value, &plus_inf, SIZE) != 0 - && memcmp (&m.value, &minus_inf, SIZE) != 0); - else - return 0; - } -# endif -#else - /* The configuration did not find sufficient information, or does - not use IEEE floating point. Give up about the signaling NaNs; - handle only the quiet NaNs. */ - if (x == x) - { -# if defined USE_LONG_DOUBLE && ((defined __ia64 && LDBL_MANT_DIG == 64) || (defined __x86_64__ || defined __amd64__) || (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_)) && !HAVE_SAME_LONG_DOUBLE_AS_DOUBLE - /* Detect any special bit patterns that pass ==; see comment above. */ - memory_double m1; - memory_double m2; - - memset (&m1.value, 0, SIZE); - memset (&m2.value, 0, SIZE); - m1.value = x; - m2.value = x + (x ? 0.0L : -0.0L); - if (memcmp (&m1.value, &m2.value, SIZE) != 0) - return 1; -# endif - return 0; - } - else - return 1; -#endif -} diff --git a/lib/isnand-nolibm.h b/lib/isnand-nolibm.h deleted file mode 100644 index bb5a38b39f0..00000000000 --- a/lib/isnand-nolibm.h +++ /dev/null @@ -1,38 +0,0 @@ -/* Test for NaN that does not need libm. - Copyright (C) 2007-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* This file uses HAVE_ISNAND_IN_LIBC. */ -#if !_GL_CONFIG_H_INCLUDED - #error "Please include config.h first." -#endif - -#if HAVE_ISNAND_IN_LIBC -/* Get declaration of isnan macro. */ -# include -# if (__GNUC__ >= 4) || (__clang_major__ >= 4) - /* GCC >= 4.0 and clang provide a type-generic built-in for isnan. */ -# undef isnand -# define isnand(x) __builtin_isnan ((double)(x)) -# else -# undef isnand -# define isnand(x) isnan ((double)(x)) -# endif -#else -/* Test whether X is a NaN. */ -# undef isnand -# define isnand rpl_isnand -extern int isnand (double x); -#endif diff --git a/lib/isnand.c b/lib/isnand.c deleted file mode 100644 index e14d2e6fb29..00000000000 --- a/lib/isnand.c +++ /dev/null @@ -1,19 +0,0 @@ -/* Test for NaN that does not need libm. - Copyright (C) 2008-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by Bruno Haible , 2008. */ - -#include "isnan.c" diff --git a/lib/isnanf-nolibm.h b/lib/isnanf-nolibm.h deleted file mode 100644 index f4bcba143e6..00000000000 --- a/lib/isnanf-nolibm.h +++ /dev/null @@ -1,46 +0,0 @@ -/* Test for NaN that does not need libm. - Copyright (C) 2007-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* This file uses HAVE_ISNANF_IN_LIBC. */ -#if !_GL_CONFIG_H_INCLUDED - #error "Please include config.h first." -#endif - -#if HAVE_ISNANF_IN_LIBC -/* Get declaration of isnan macro or (older) isnanf function. */ -# include -# if (__GNUC__ >= 4) || (__clang_major__ >= 4) - /* GCC >= 4.0 and clang provide a type-generic built-in for isnan. - GCC >= 4.0 also provides __builtin_isnanf, but clang doesn't. */ -# undef isnanf -# define isnanf(x) __builtin_isnan ((float)(x)) -# elif defined isnan -# undef isnanf -# define isnanf(x) isnan ((float)(x)) -# else - /* Get declaration of isnanf(), if not declared in . */ -# if defined __sgi - /* We can't include , because it conflicts with our definition of - isnand. Therefore declare isnanf separately. */ -extern int isnanf (float x); -# endif -# endif -#else -/* Test whether X is a NaN. */ -# undef isnanf -# define isnanf rpl_isnanf -extern int isnanf (float x); -#endif diff --git a/lib/isnanf.c b/lib/isnanf.c deleted file mode 100644 index 55252ca6d6e..00000000000 --- a/lib/isnanf.c +++ /dev/null @@ -1,20 +0,0 @@ -/* Test for NaN that does not need libm. - Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by Bruno Haible , 2007. */ - -#define USE_FLOAT -#include "isnan.c" diff --git a/lib/isnanl-nolibm.h b/lib/isnanl-nolibm.h deleted file mode 100644 index 8becc5b409e..00000000000 --- a/lib/isnanl-nolibm.h +++ /dev/null @@ -1,39 +0,0 @@ -/* Test for NaN that does not need libm. - Copyright (C) 2007-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* This file uses HAVE_ISNANL_IN_LIBC. */ -#if !_GL_CONFIG_H_INCLUDED - #error "Please include config.h first." -#endif - -#if HAVE_ISNANL_IN_LIBC -/* Get declaration of isnan macro or (older) isnanl function. */ -# include -# if (__GNUC__ >= 4) || (__clang_major__ >= 4) - /* GCC >= 4.0 and clang provide a type-generic built-in for isnan. - GCC >= 4.0 also provides __builtin_isnanl, but clang doesn't. */ -# undef isnanl -# define isnanl(x) __builtin_isnan ((long double)(x)) -# elif defined isnan -# undef isnanl -# define isnanl(x) isnan ((long double)(x)) -# endif -#else -/* Test whether X is a NaN. */ -# undef isnanl -# define isnanl rpl_isnanl -extern int isnanl (long double x); -#endif diff --git a/lib/isnanl.c b/lib/isnanl.c deleted file mode 100644 index 286f245097e..00000000000 --- a/lib/isnanl.c +++ /dev/null @@ -1,20 +0,0 @@ -/* Test for NaN that does not need libm. - Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Written by Bruno Haible , 2007. */ - -#define USE_LONG_DOUBLE -#include "isnan.c" diff --git a/lib/itold.c b/lib/itold.c deleted file mode 100644 index 0ef4464eead..00000000000 --- a/lib/itold.c +++ /dev/null @@ -1,28 +0,0 @@ -/* Replacement for 'int' to 'long double' conversion routine. - Copyright (C) 2011-2023 Free Software Foundation, Inc. - Written by Bruno Haible , 2011. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -/* Specification. */ -#include - -void -_Qp_itoq (long double *result, int a) -{ - /* Convert from 'int' to 'double', then from 'double' to 'long double'. */ - *result = (double) a; -} diff --git a/lib/math.c b/lib/math.c deleted file mode 100644 index 67cabbcf8f2..00000000000 --- a/lib/math.c +++ /dev/null @@ -1,22 +0,0 @@ -/* Inline functions for . - - Copyright (C) 2012-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -#define _GL_MATH_INLINE _GL_EXTERN_INLINE -#include "math.h" -typedef int dummy; diff --git a/lib/math.in.h b/lib/math.in.h deleted file mode 100644 index f841a1356e4..00000000000 --- a/lib/math.in.h +++ /dev/null @@ -1,2743 +0,0 @@ -/* A GNU-like . - - Copyright (C) 2002-2003, 2007-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* On Android, in C++ mode, when /usr/include/c++/v1/math.h is being included - and /usr/include/math.h has not yet been included, skip this file, since it - would lead to many syntax errors. */ -#if !(defined __ANDROID__ && defined _LIBCPP_MATH_H && !defined INFINITY) - -#ifndef _@GUARD_PREFIX@_MATH_H - -#if __GNUC__ >= 3 -@PRAGMA_SYSTEM_HEADER@ -#endif -@PRAGMA_COLUMNS@ - -#if defined _GL_INCLUDING_MATH_H -/* Special invocation convention: - - On FreeBSD 12.2 we have a sequence of nested includes - -> -> -> -> - -> -> -> -> - In this situation, the functions are not yet declared, therefore we cannot - provide the C++ aliases. */ - -#@INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ @NEXT_AS_FIRST_DIRECTIVE_MATH_H@ - -#else -/* Normal invocation convention. */ - -/* The include_next requires a split double-inclusion guard. */ -#define _GL_INCLUDING_MATH_H -#@INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ @NEXT_AS_FIRST_DIRECTIVE_MATH_H@ -#undef _GL_INCLUDING_MATH_H - -#ifndef _@GUARD_PREFIX@_MATH_H -#define _@GUARD_PREFIX@_MATH_H - -/* This file uses _GL_INLINE_HEADER_BEGIN, _GL_INLINE, _GL_ATTRIBUTE_CONST, - GNULIB_POSIXCHECK, HAVE_RAW_DECL_*. */ -#if !_GL_CONFIG_H_INCLUDED - #error "Please include config.h first." -#endif - -/* On OpenVMS, NAN, INFINITY, and HUGEVAL macros are defined in . */ -#if defined __VMS && ! defined NAN -# include -#endif - -_GL_INLINE_HEADER_BEGIN -#ifndef _GL_MATH_INLINE -# define _GL_MATH_INLINE _GL_INLINE -#endif - -/* The __attribute__ feature is available in gcc versions 2.5 and later. - The attribute __const__ was added in gcc 2.95. */ -#ifndef _GL_ATTRIBUTE_CONST -# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) || defined __clang__ -# define _GL_ATTRIBUTE_CONST __attribute__ ((__const__)) -# else -# define _GL_ATTRIBUTE_CONST /* empty */ -# endif -#endif - -/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */ - -/* The definition of _GL_ARG_NONNULL is copied here. */ - -/* The definition of _GL_WARN_ON_USE is copied here. */ - -#ifdef __cplusplus -/* Helper macros to define type-generic function FUNC as overloaded functions, - rather than as macros like in C. POSIX declares these with an argument of - real-floating (that is, one of float, double, or long double). */ -# define _GL_MATH_CXX_REAL_FLOATING_DECL_1(func) \ -static inline int \ -_gl_cxx_ ## func ## f (float f) \ -{ \ - return func (f); \ -} \ -static inline int \ -_gl_cxx_ ## func ## d (double d) \ -{ \ - return func (d); \ -} \ -static inline int \ -_gl_cxx_ ## func ## l (long double l) \ -{ \ - return func (l); \ -} -# define _GL_MATH_CXX_REAL_FLOATING_DECL_2(func,rpl_func,rettype) \ -_GL_BEGIN_NAMESPACE \ -inline rettype \ -rpl_func (float f) \ -{ \ - return _gl_cxx_ ## func ## f (f); \ -} \ -inline rettype \ -rpl_func (double d) \ -{ \ - return _gl_cxx_ ## func ## d (d); \ -} \ -inline rettype \ -rpl_func (long double l) \ -{ \ - return _gl_cxx_ ## func ## l (l); \ -} \ -_GL_END_NAMESPACE -#endif - -/* Helper macros to define a portability warning for the - classification macro FUNC called with VALUE. POSIX declares the - classification macros with an argument of real-floating (that is, - one of float, double, or long double). */ -#define _GL_WARN_REAL_FLOATING_DECL(func) \ -_GL_MATH_INLINE int \ -_GL_WARN_ON_USE_ATTRIBUTE (#func " is unportable - " \ - "use gnulib module " #func " for portability") \ -rpl_ ## func ## f (float f) \ -{ \ - return func (f); \ -} \ -_GL_MATH_INLINE int \ -_GL_WARN_ON_USE_ATTRIBUTE (#func " is unportable - " \ - "use gnulib module " #func " for portability") \ -rpl_ ## func ## d (double d) \ -{ \ - return func (d); \ -} \ -_GL_MATH_INLINE int \ -_GL_WARN_ON_USE_ATTRIBUTE (#func " is unportable - " \ - "use gnulib module " #func " for portability") \ -rpl_ ## func ## l (long double l) \ -{ \ - return func (l); \ -} -#define _GL_WARN_REAL_FLOATING_IMPL(func, value) \ - (sizeof (value) == sizeof (float) ? rpl_ ## func ## f (value) \ - : sizeof (value) == sizeof (double) ? rpl_ ## func ## d (value) \ - : rpl_ ## func ## l (value)) - - -#if @REPLACE_ITOLD@ -/* Pull in a function that fixes the 'int' to 'long double' conversion - of glibc 2.7. */ -_GL_EXTERN_C void _Qp_itoq (long double *, int); -static void (*_gl_math_fix_itold) (long double *, int) = _Qp_itoq; -#endif - - -/* POSIX allows platforms that don't support NAN. But all major - machines in the past 15 years have supported something close to - IEEE NaN, so we define this unconditionally. We also must define - it on platforms like Solaris 10, where NAN is present but defined - as a function pointer rather than a floating point constant. */ -#if !defined NAN || @REPLACE_NAN@ -# if !GNULIB_defined_NAN -# undef NAN - /* The Compaq (ex-DEC) C 6.4 compiler and the Microsoft MSVC 9 compiler - choke on the expression 0.0 / 0.0. */ -# if defined __DECC || defined _MSC_VER -_GL_MATH_INLINE float -_NaN () -{ - static float zero = 0.0f; - return zero / zero; -} -# define NAN (_NaN()) -# else -# define NAN (0.0f / 0.0f) -# endif -# define GNULIB_defined_NAN 1 -# endif -#endif - -/* Solaris 10 defines HUGE_VAL, but as a function pointer rather - than a floating point constant. */ -#if @REPLACE_HUGE_VAL@ -# undef HUGE_VALF -# define HUGE_VALF (1.0f / 0.0f) -# undef HUGE_VAL -# define HUGE_VAL (1.0 / 0.0) -# undef HUGE_VALL -# define HUGE_VALL (1.0L / 0.0L) -#endif - -/* HUGE_VALF is a 'float' Infinity. */ -#ifndef HUGE_VALF -# if defined _MSC_VER -/* The Microsoft MSVC 9 compiler chokes on the expression 1.0f / 0.0f. */ -# define HUGE_VALF (1e25f * 1e25f) -# else -# define HUGE_VALF (1.0f / 0.0f) -# endif -#endif - -/* HUGE_VAL is a 'double' Infinity. */ -#ifndef HUGE_VAL -# if defined _MSC_VER -/* The Microsoft MSVC 9 compiler chokes on the expression 1.0 / 0.0. */ -# define HUGE_VAL (1e250 * 1e250) -# else -# define HUGE_VAL (1.0 / 0.0) -# endif -#endif - -/* HUGE_VALL is a 'long double' Infinity. */ -#ifndef HUGE_VALL -# if defined _MSC_VER -/* The Microsoft MSVC 9 compiler chokes on the expression 1.0L / 0.0L. */ -# define HUGE_VALL (1e250L * 1e250L) -# else -# define HUGE_VALL (1.0L / 0.0L) -# endif -#endif - - -#if defined FP_ILOGB0 && defined FP_ILOGBNAN - /* Ensure FP_ILOGB0 and FP_ILOGBNAN are correct. */ -# if defined __HAIKU__ - /* Haiku: match what ilogb() does */ -# undef FP_ILOGB0 -# undef FP_ILOGBNAN -# define FP_ILOGB0 (- 2147483647 - 1) /* INT_MIN */ -# define FP_ILOGBNAN (- 2147483647 - 1) /* INT_MIN */ -# endif -#else - /* Ensure FP_ILOGB0 and FP_ILOGBNAN are defined. */ -# if defined __NetBSD__ || defined __sgi - /* NetBSD, IRIX 6.5: match what ilogb() does */ -# define FP_ILOGB0 (- 2147483647 - 1) /* INT_MIN */ -# define FP_ILOGBNAN (- 2147483647 - 1) /* INT_MIN */ -# elif defined _AIX - /* AIX 5.1: match what ilogb() does in AIX >= 5.2 */ -# define FP_ILOGB0 (- 2147483647 - 1) /* INT_MIN */ -# define FP_ILOGBNAN 2147483647 /* INT_MAX */ -# elif defined __sun - /* Solaris 9: match what ilogb() does */ -# define FP_ILOGB0 (- 2147483647) /* - INT_MAX */ -# define FP_ILOGBNAN 2147483647 /* INT_MAX */ -# else - /* Gnulib defined values. */ -# define FP_ILOGB0 (- 2147483647) /* - INT_MAX */ -# define FP_ILOGBNAN (- 2147483647 - 1) /* INT_MIN */ -# endif -#endif - - -#if @GNULIB_ACOSF@ -# if @REPLACE_ACOSF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef acosf -# define acosf rpl_acosf -# endif -_GL_FUNCDECL_RPL (acosf, float, (float x)); -_GL_CXXALIAS_RPL (acosf, float, (float x)); -# else -# if !@HAVE_ACOSF@ -# undef acosf -_GL_FUNCDECL_SYS (acosf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (acosf, float, (float x)); -# endif -_GL_CXXALIASWARN (acosf); -#elif defined GNULIB_POSIXCHECK -# undef acosf -# if HAVE_RAW_DECL_ACOSF -_GL_WARN_ON_USE (acosf, "acosf is unportable - " - "use gnulib module acosf for portability"); -# endif -#endif - -#if @GNULIB_ACOSL@ -# if !@HAVE_ACOSL@ || !@HAVE_DECL_ACOSL@ -# undef acosl -_GL_FUNCDECL_SYS (acosl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (acosl, long double, (long double x)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (acosl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef acosl -# if HAVE_RAW_DECL_ACOSL -_GL_WARN_ON_USE (acosl, "acosl is unportable - " - "use gnulib module acosl for portability"); -# endif -#endif - - -#if @GNULIB_ASINF@ -# if @REPLACE_ASINF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef asinf -# define asinf rpl_asinf -# endif -_GL_FUNCDECL_RPL (asinf, float, (float x)); -_GL_CXXALIAS_RPL (asinf, float, (float x)); -# else -# if !@HAVE_ASINF@ -# undef asinf -_GL_FUNCDECL_SYS (asinf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (asinf, float, (float x)); -# endif -_GL_CXXALIASWARN (asinf); -#elif defined GNULIB_POSIXCHECK -# undef asinf -# if HAVE_RAW_DECL_ASINF -_GL_WARN_ON_USE (asinf, "asinf is unportable - " - "use gnulib module asinf for portability"); -# endif -#endif - -#if @GNULIB_ASINL@ -# if !@HAVE_ASINL@ || !@HAVE_DECL_ASINL@ -# undef asinl -_GL_FUNCDECL_SYS (asinl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (asinl, long double, (long double x)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (asinl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef asinl -# if HAVE_RAW_DECL_ASINL -_GL_WARN_ON_USE (asinl, "asinl is unportable - " - "use gnulib module asinl for portability"); -# endif -#endif - - -#if @GNULIB_ATANF@ -# if @REPLACE_ATANF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef atanf -# define atanf rpl_atanf -# endif -_GL_FUNCDECL_RPL (atanf, float, (float x)); -_GL_CXXALIAS_RPL (atanf, float, (float x)); -# else -# if !@HAVE_ATANF@ -# undef atanf -_GL_FUNCDECL_SYS (atanf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (atanf, float, (float x)); -# endif -_GL_CXXALIASWARN (atanf); -#elif defined GNULIB_POSIXCHECK -# undef atanf -# if HAVE_RAW_DECL_ATANF -_GL_WARN_ON_USE (atanf, "atanf is unportable - " - "use gnulib module atanf for portability"); -# endif -#endif - -#if @GNULIB_ATANL@ -# if !@HAVE_ATANL@ || !@HAVE_DECL_ATANL@ -# undef atanl -_GL_FUNCDECL_SYS (atanl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (atanl, long double, (long double x)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (atanl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef atanl -# if HAVE_RAW_DECL_ATANL -_GL_WARN_ON_USE (atanl, "atanl is unportable - " - "use gnulib module atanl for portability"); -# endif -#endif - - -#if @GNULIB_ATAN2F@ -# if @REPLACE_ATAN2F@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef atan2f -# define atan2f rpl_atan2f -# endif -_GL_FUNCDECL_RPL (atan2f, float, (float y, float x)); -_GL_CXXALIAS_RPL (atan2f, float, (float y, float x)); -# else -# if !@HAVE_ATAN2F@ -# undef atan2f -_GL_FUNCDECL_SYS (atan2f, float, (float y, float x)); -# endif -_GL_CXXALIAS_SYS (atan2f, float, (float y, float x)); -# endif -_GL_CXXALIASWARN (atan2f); -#elif defined GNULIB_POSIXCHECK -# undef atan2f -# if HAVE_RAW_DECL_ATAN2F -_GL_WARN_ON_USE (atan2f, "atan2f is unportable - " - "use gnulib module atan2f for portability"); -# endif -#endif - - -#if @GNULIB_CBRTF@ -# if @REPLACE_CBRTF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef cbrtf -# define cbrtf rpl_cbrtf -# endif -_GL_FUNCDECL_RPL (cbrtf, float, (float x)); -_GL_CXXALIAS_RPL (cbrtf, float, (float x)); -# else -# if !@HAVE_DECL_CBRTF@ -_GL_FUNCDECL_SYS (cbrtf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (cbrtf, float, (float x)); -# endif -_GL_CXXALIASWARN (cbrtf); -#elif defined GNULIB_POSIXCHECK -# undef cbrtf -# if HAVE_RAW_DECL_CBRTF -_GL_WARN_ON_USE (cbrtf, "cbrtf is unportable - " - "use gnulib module cbrtf for portability"); -# endif -#endif - -#if @GNULIB_CBRT@ -# if !@HAVE_CBRT@ -_GL_FUNCDECL_SYS (cbrt, double, (double x)); -# endif -_GL_CXXALIAS_SYS (cbrt, double, (double x)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (cbrt, double, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef cbrt -# if HAVE_RAW_DECL_CBRT -_GL_WARN_ON_USE (cbrt, "cbrt is unportable - " - "use gnulib module cbrt for portability"); -# endif -#endif - -#if @GNULIB_CBRTL@ -# if @REPLACE_CBRTL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef cbrtl -# define cbrtl rpl_cbrtl -# endif -_GL_FUNCDECL_RPL (cbrtl, long double, (long double x)); -_GL_CXXALIAS_RPL (cbrtl, long double, (long double x)); -# else -# if !@HAVE_DECL_CBRTL@ -_GL_FUNCDECL_SYS (cbrtl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (cbrtl, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (cbrtl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef cbrtl -# if HAVE_RAW_DECL_CBRTL -_GL_WARN_ON_USE (cbrtl, "cbrtl is unportable - " - "use gnulib module cbrtl for portability"); -# endif -#endif - - -#if @GNULIB_CEILF@ -# if @REPLACE_CEILF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef ceilf -# define ceilf rpl_ceilf -# endif -_GL_FUNCDECL_RPL (ceilf, float, (float x)); -_GL_CXXALIAS_RPL (ceilf, float, (float x)); -# else -# if !@HAVE_DECL_CEILF@ -# undef ceilf -_GL_FUNCDECL_SYS (ceilf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (ceilf, float, (float x)); -# endif -_GL_CXXALIASWARN (ceilf); -#elif defined GNULIB_POSIXCHECK -# undef ceilf -# if HAVE_RAW_DECL_CEILF -_GL_WARN_ON_USE (ceilf, "ceilf is unportable - " - "use gnulib module ceilf for portability"); -# endif -#endif - -#if @GNULIB_CEIL@ -# if @REPLACE_CEIL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef ceil -# define ceil rpl_ceil -# endif -_GL_FUNCDECL_RPL (ceil, double, (double x)); -_GL_CXXALIAS_RPL (ceil, double, (double x)); -# else -_GL_CXXALIAS_SYS (ceil, double, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (ceil, double, (double x)); -# endif -#endif - -#if @GNULIB_CEILL@ -# if @REPLACE_CEILL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef ceill -# define ceill rpl_ceill -# endif -_GL_FUNCDECL_RPL (ceill, long double, (long double x)); -_GL_CXXALIAS_RPL (ceill, long double, (long double x)); -# else -# if !@HAVE_DECL_CEILL@ -# undef ceill -_GL_FUNCDECL_SYS (ceill, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (ceill, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (ceill); -# endif -#elif defined GNULIB_POSIXCHECK -# undef ceill -# if HAVE_RAW_DECL_CEILL -_GL_WARN_ON_USE (ceill, "ceill is unportable - " - "use gnulib module ceill for portability"); -# endif -#endif - - -#if @GNULIB_COPYSIGNF@ -# if !@HAVE_DECL_COPYSIGNF@ -# undef copysignf -_GL_FUNCDECL_SYS (copysignf, float, (float x, float y)); -# endif -_GL_CXXALIAS_SYS (copysignf, float, (float x, float y)); -_GL_CXXALIASWARN (copysignf); -#elif defined GNULIB_POSIXCHECK -# undef copysignf -# if HAVE_RAW_DECL_COPYSIGNF -_GL_WARN_ON_USE (copysignf, "copysignf is unportable - " - "use gnulib module copysignf for portability"); -# endif -#endif - -#if @GNULIB_COPYSIGN@ -# if !@HAVE_COPYSIGN@ -_GL_FUNCDECL_SYS (copysign, double, (double x, double y)); -# endif -_GL_CXXALIAS_SYS (copysign, double, (double x, double y)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (copysign, double, (double x, double y)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef copysign -# if HAVE_RAW_DECL_COPYSIGN -_GL_WARN_ON_USE (copysign, "copysign is unportable - " - "use gnulib module copysign for portability"); -# endif -#endif - -#if @GNULIB_COPYSIGNL@ -# if !@HAVE_COPYSIGNL@ -_GL_FUNCDECL_SYS (copysignl, long double, (long double x, long double y)); -# endif -_GL_CXXALIAS_SYS (copysignl, long double, (long double x, long double y)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (copysignl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef copysignl -# if HAVE_RAW_DECL_COPYSIGNL -_GL_WARN_ON_USE (copysign, "copysignl is unportable - " - "use gnulib module copysignl for portability"); -# endif -#endif - - -#if @GNULIB_COSF@ -# if @REPLACE_COSF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef cosf -# define cosf rpl_cosf -# endif -_GL_FUNCDECL_RPL (cosf, float, (float x)); -_GL_CXXALIAS_RPL (cosf, float, (float x)); -# else -# if !@HAVE_COSF@ -# undef cosf -_GL_FUNCDECL_SYS (cosf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (cosf, float, (float x)); -# endif -_GL_CXXALIASWARN (cosf); -#elif defined GNULIB_POSIXCHECK -# undef cosf -# if HAVE_RAW_DECL_COSF -_GL_WARN_ON_USE (cosf, "cosf is unportable - " - "use gnulib module cosf for portability"); -# endif -#endif - -#if @GNULIB_COSL@ -# if !@HAVE_COSL@ || !@HAVE_DECL_COSL@ -# undef cosl -_GL_FUNCDECL_SYS (cosl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (cosl, long double, (long double x)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (cosl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef cosl -# if HAVE_RAW_DECL_COSL -_GL_WARN_ON_USE (cosl, "cosl is unportable - " - "use gnulib module cosl for portability"); -# endif -#endif - - -#if @GNULIB_COSHF@ -# if @REPLACE_COSHF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef coshf -# define coshf rpl_coshf -# endif -_GL_FUNCDECL_RPL (coshf, float, (float x)); -_GL_CXXALIAS_RPL (coshf, float, (float x)); -# else -# if !@HAVE_COSHF@ -# undef coshf -_GL_FUNCDECL_SYS (coshf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (coshf, float, (float x)); -# endif -_GL_CXXALIASWARN (coshf); -#elif defined GNULIB_POSIXCHECK -# undef coshf -# if HAVE_RAW_DECL_COSHF -_GL_WARN_ON_USE (coshf, "coshf is unportable - " - "use gnulib module coshf for portability"); -# endif -#endif - - -#if @GNULIB_EXPF@ -# if @REPLACE_EXPF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef expf -# define expf rpl_expf -# endif -_GL_FUNCDECL_RPL (expf, float, (float x)); -_GL_CXXALIAS_RPL (expf, float, (float x)); -# else -# if !@HAVE_EXPF@ -# undef expf -_GL_FUNCDECL_SYS (expf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (expf, float, (float x)); -# endif -_GL_CXXALIASWARN (expf); -#elif defined GNULIB_POSIXCHECK -# undef expf -# if HAVE_RAW_DECL_EXPF -_GL_WARN_ON_USE (expf, "expf is unportable - " - "use gnulib module expf for portability"); -# endif -#endif - -#if @GNULIB_EXPL@ -# if @REPLACE_EXPL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef expl -# define expl rpl_expl -# endif -_GL_FUNCDECL_RPL (expl, long double, (long double x)); -_GL_CXXALIAS_RPL (expl, long double, (long double x)); -# else -# if !@HAVE_EXPL@ || !@HAVE_DECL_EXPL@ -# undef expl -_GL_FUNCDECL_SYS (expl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (expl, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (expl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef expl -# if HAVE_RAW_DECL_EXPL -_GL_WARN_ON_USE (expl, "expl is unportable - " - "use gnulib module expl for portability"); -# endif -#endif - - -#if @GNULIB_EXP2F@ -# if !@HAVE_DECL_EXP2F@ -_GL_FUNCDECL_SYS (exp2f, float, (float x)); -# endif -_GL_CXXALIAS_SYS (exp2f, float, (float x)); -_GL_CXXALIASWARN (exp2f); -#elif defined GNULIB_POSIXCHECK -# undef exp2f -# if HAVE_RAW_DECL_EXP2F -_GL_WARN_ON_USE (exp2f, "exp2f is unportable - " - "use gnulib module exp2f for portability"); -# endif -#endif - -#if @GNULIB_EXP2@ -# if @REPLACE_EXP2@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef exp2 -# define exp2 rpl_exp2 -# endif -_GL_FUNCDECL_RPL (exp2, double, (double x)); -_GL_CXXALIAS_RPL (exp2, double, (double x)); -# else -# if !@HAVE_DECL_EXP2@ -_GL_FUNCDECL_SYS (exp2, double, (double x)); -# endif -_GL_CXXALIAS_SYS (exp2, double, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (exp2, double, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef exp2 -# if HAVE_RAW_DECL_EXP2 -_GL_WARN_ON_USE (exp2, "exp2 is unportable - " - "use gnulib module exp2 for portability"); -# endif -#endif - -#if @GNULIB_EXP2L@ -# if @REPLACE_EXP2L@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef exp2l -# define exp2l rpl_exp2l -# endif -_GL_FUNCDECL_RPL (exp2l, long double, (long double x)); -_GL_CXXALIAS_RPL (exp2l, long double, (long double x)); -# else -# if !@HAVE_DECL_EXP2L@ -# undef exp2l -_GL_FUNCDECL_SYS (exp2l, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (exp2l, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (exp2l); -# endif -#elif defined GNULIB_POSIXCHECK -# undef exp2l -# if HAVE_RAW_DECL_EXP2L -_GL_WARN_ON_USE (exp2l, "exp2l is unportable - " - "use gnulib module exp2l for portability"); -# endif -#endif - - -#if @GNULIB_EXPM1F@ -# if @REPLACE_EXPM1F@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef expm1f -# define expm1f rpl_expm1f -# endif -_GL_FUNCDECL_RPL (expm1f, float, (float x)); -_GL_CXXALIAS_RPL (expm1f, float, (float x)); -# else -# if !@HAVE_EXPM1F@ -_GL_FUNCDECL_SYS (expm1f, float, (float x)); -# endif -_GL_CXXALIAS_SYS (expm1f, float, (float x)); -# endif -_GL_CXXALIASWARN (expm1f); -#elif defined GNULIB_POSIXCHECK -# undef expm1f -# if HAVE_RAW_DECL_EXPM1F -_GL_WARN_ON_USE (expm1f, "expm1f is unportable - " - "use gnulib module expm1f for portability"); -# endif -#endif - -#if @GNULIB_EXPM1@ -# if @REPLACE_EXPM1@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef expm1 -# define expm1 rpl_expm1 -# endif -_GL_FUNCDECL_RPL (expm1, double, (double x)); -_GL_CXXALIAS_RPL (expm1, double, (double x)); -# else -# if !@HAVE_EXPM1@ -_GL_FUNCDECL_SYS (expm1, double, (double x)); -# endif -_GL_CXXALIAS_SYS (expm1, double, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (expm1, double, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef expm1 -# if HAVE_RAW_DECL_EXPM1 -_GL_WARN_ON_USE (expm1, "expm1 is unportable - " - "use gnulib module expm1 for portability"); -# endif -#endif - -#if @GNULIB_EXPM1L@ -# if @REPLACE_EXPM1L@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef expm1l -# define expm1l rpl_expm1l -# endif -_GL_FUNCDECL_RPL (expm1l, long double, (long double x)); -_GL_CXXALIAS_RPL (expm1l, long double, (long double x)); -# else -# if !@HAVE_DECL_EXPM1L@ -# undef expm1l -# if !(defined __cplusplus && defined _AIX) -_GL_FUNCDECL_SYS (expm1l, long double, (long double x)); -# endif -# endif -_GL_CXXALIAS_SYS (expm1l, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (expm1l); -# endif -#elif defined GNULIB_POSIXCHECK -# undef expm1l -# if HAVE_RAW_DECL_EXPM1L -_GL_WARN_ON_USE (expm1l, "expm1l is unportable - " - "use gnulib module expm1l for portability"); -# endif -#endif - - -#if @GNULIB_FABSF@ -# if !@HAVE_FABSF@ -# undef fabsf -_GL_FUNCDECL_SYS (fabsf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (fabsf, float, (float x)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (fabsf); -# endif -#elif defined GNULIB_POSIXCHECK -# undef fabsf -# if HAVE_RAW_DECL_FABSF -_GL_WARN_ON_USE (fabsf, "fabsf is unportable - " - "use gnulib module fabsf for portability"); -# endif -#endif - -#if @GNULIB_FABSL@ -# if @REPLACE_FABSL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef fabsl -# define fabsl rpl_fabsl -# endif -_GL_FUNCDECL_RPL (fabsl, long double, (long double x)); -_GL_CXXALIAS_RPL (fabsl, long double, (long double x)); -# else -# if !@HAVE_FABSL@ -# undef fabsl -_GL_FUNCDECL_SYS (fabsl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (fabsl, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (fabsl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef fabsl -# if HAVE_RAW_DECL_FABSL -_GL_WARN_ON_USE (fabsl, "fabsl is unportable - " - "use gnulib module fabsl for portability"); -# endif -#endif - - -#if @GNULIB_FLOORF@ -# if @REPLACE_FLOORF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef floorf -# define floorf rpl_floorf -# endif -_GL_FUNCDECL_RPL (floorf, float, (float x)); -_GL_CXXALIAS_RPL (floorf, float, (float x)); -# else -# if !@HAVE_DECL_FLOORF@ -# undef floorf -_GL_FUNCDECL_SYS (floorf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (floorf, float, (float x)); -# endif -_GL_CXXALIASWARN (floorf); -#elif defined GNULIB_POSIXCHECK -# undef floorf -# if HAVE_RAW_DECL_FLOORF -_GL_WARN_ON_USE (floorf, "floorf is unportable - " - "use gnulib module floorf for portability"); -# endif -#endif - -#if @GNULIB_FLOOR@ -# if @REPLACE_FLOOR@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef floor -# define floor rpl_floor -# endif -_GL_FUNCDECL_RPL (floor, double, (double x)); -_GL_CXXALIAS_RPL (floor, double, (double x)); -# else -_GL_CXXALIAS_SYS (floor, double, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (floor, double, (double x)); -# endif -#endif - -#if @GNULIB_FLOORL@ -# if @REPLACE_FLOORL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef floorl -# define floorl rpl_floorl -# endif -_GL_FUNCDECL_RPL (floorl, long double, (long double x)); -_GL_CXXALIAS_RPL (floorl, long double, (long double x)); -# else -# if !@HAVE_DECL_FLOORL@ -# undef floorl -_GL_FUNCDECL_SYS (floorl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (floorl, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (floorl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef floorl -# if HAVE_RAW_DECL_FLOORL -_GL_WARN_ON_USE (floorl, "floorl is unportable - " - "use gnulib module floorl for portability"); -# endif -#endif - - -#if @GNULIB_FMAF@ -# if @REPLACE_FMAF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef fmaf -# define fmaf rpl_fmaf -# endif -_GL_FUNCDECL_RPL (fmaf, float, (float x, float y, float z)); -_GL_CXXALIAS_RPL (fmaf, float, (float x, float y, float z)); -# else -# if !@HAVE_FMAF@ -# undef fmaf -_GL_FUNCDECL_SYS (fmaf, float, (float x, float y, float z)); -# endif -_GL_CXXALIAS_SYS (fmaf, float, (float x, float y, float z)); -# endif -_GL_CXXALIASWARN (fmaf); -#elif defined GNULIB_POSIXCHECK -# undef fmaf -# if HAVE_RAW_DECL_FMAF -_GL_WARN_ON_USE (fmaf, "fmaf is unportable - " - "use gnulib module fmaf for portability"); -# endif -#endif - -#if @GNULIB_FMA@ -# if @REPLACE_FMA@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef fma -# define fma rpl_fma -# endif -_GL_FUNCDECL_RPL (fma, double, (double x, double y, double z)); -_GL_CXXALIAS_RPL (fma, double, (double x, double y, double z)); -# else -# if !@HAVE_FMA@ -# undef fma -_GL_FUNCDECL_SYS (fma, double, (double x, double y, double z)); -# endif -_GL_CXXALIAS_SYS (fma, double, (double x, double y, double z)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (fma, double, (double x, double y, double z)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef fma -# if HAVE_RAW_DECL_FMA -_GL_WARN_ON_USE (fma, "fma is unportable - " - "use gnulib module fma for portability"); -# endif -#endif - -#if @GNULIB_FMAL@ -# if @REPLACE_FMAL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef fmal -# define fmal rpl_fmal -# endif -_GL_FUNCDECL_RPL (fmal, long double, - (long double x, long double y, long double z)); -_GL_CXXALIAS_RPL (fmal, long double, - (long double x, long double y, long double z)); -# else -# if !@HAVE_FMAL@ -# undef fmal -# if !(defined __cplusplus && defined _AIX) -_GL_FUNCDECL_SYS (fmal, long double, - (long double x, long double y, long double z)); -# endif -# endif -_GL_CXXALIAS_SYS (fmal, long double, - (long double x, long double y, long double z)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (fmal); -# endif -#elif defined GNULIB_POSIXCHECK -# undef fmal -# if HAVE_RAW_DECL_FMAL -_GL_WARN_ON_USE (fmal, "fmal is unportable - " - "use gnulib module fmal for portability"); -# endif -#endif - - -#if @GNULIB_FMODF@ -# if @REPLACE_FMODF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef fmodf -# define fmodf rpl_fmodf -# endif -_GL_FUNCDECL_RPL (fmodf, float, (float x, float y)); -_GL_CXXALIAS_RPL (fmodf, float, (float x, float y)); -# else -# if !@HAVE_FMODF@ -# undef fmodf -_GL_FUNCDECL_SYS (fmodf, float, (float x, float y)); -# endif -_GL_CXXALIAS_SYS (fmodf, float, (float x, float y)); -# endif -_GL_CXXALIASWARN (fmodf); -#elif defined GNULIB_POSIXCHECK -# undef fmodf -# if HAVE_RAW_DECL_FMODF -_GL_WARN_ON_USE (fmodf, "fmodf is unportable - " - "use gnulib module fmodf for portability"); -# endif -#endif - -#if @GNULIB_FMOD@ -# if @REPLACE_FMOD@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef fmod -# define fmod rpl_fmod -# endif -_GL_FUNCDECL_RPL (fmod, double, (double x, double y)); -_GL_CXXALIAS_RPL (fmod, double, (double x, double y)); -# else -_GL_CXXALIAS_SYS (fmod, double, (double x, double y)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (fmod, double, (double x, double y)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef fmod -# if HAVE_RAW_DECL_FMOD -_GL_WARN_ON_USE (fmod, "fmod has portability problems - " - "use gnulib module fmod for portability"); -# endif -#endif - -#if @GNULIB_FMODL@ -# if @REPLACE_FMODL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef fmodl -# define fmodl rpl_fmodl -# endif -_GL_FUNCDECL_RPL (fmodl, long double, (long double x, long double y)); -_GL_CXXALIAS_RPL (fmodl, long double, (long double x, long double y)); -# else -# if !@HAVE_FMODL@ -# undef fmodl -_GL_FUNCDECL_SYS (fmodl, long double, (long double x, long double y)); -# endif -_GL_CXXALIAS_SYS (fmodl, long double, (long double x, long double y)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (fmodl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef fmodl -# if HAVE_RAW_DECL_FMODL -_GL_WARN_ON_USE (fmodl, "fmodl is unportable - " - "use gnulib module fmodl for portability"); -# endif -#endif - - -/* Write x as - x = mantissa * 2^exp - where - If x finite and nonzero: 0.5 <= |mantissa| < 1.0. - If x is zero: mantissa = x, exp = 0. - If x is infinite or NaN: mantissa = x, exp unspecified. - Store exp in *EXPPTR and return mantissa. */ -#if @GNULIB_FREXPF@ -# if @REPLACE_FREXPF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef frexpf -# define frexpf rpl_frexpf -# endif -_GL_FUNCDECL_RPL (frexpf, float, (float x, int *expptr) _GL_ARG_NONNULL ((2))); -_GL_CXXALIAS_RPL (frexpf, float, (float x, int *expptr)); -# else -# if !@HAVE_FREXPF@ -# undef frexpf -_GL_FUNCDECL_SYS (frexpf, float, (float x, int *expptr) _GL_ARG_NONNULL ((2))); -# endif -_GL_CXXALIAS_SYS (frexpf, float, (float x, int *expptr)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (frexpf); -# endif -#elif defined GNULIB_POSIXCHECK -# undef frexpf -# if HAVE_RAW_DECL_FREXPF -_GL_WARN_ON_USE (frexpf, "frexpf is unportable - " - "use gnulib module frexpf for portability"); -# endif -#endif - -/* Write x as - x = mantissa * 2^exp - where - If x finite and nonzero: 0.5 <= |mantissa| < 1.0. - If x is zero: mantissa = x, exp = 0. - If x is infinite or NaN: mantissa = x, exp unspecified. - Store exp in *EXPPTR and return mantissa. */ -#if @GNULIB_FREXP@ -# if @REPLACE_FREXP@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef frexp -# define frexp rpl_frexp -# endif -_GL_FUNCDECL_RPL (frexp, double, (double x, int *expptr) _GL_ARG_NONNULL ((2))); -_GL_CXXALIAS_RPL (frexp, double, (double x, int *expptr)); -# else -_GL_CXXALIAS_SYS (frexp, double, (double x, int *expptr)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (frexp, double, (double x, int *expptr)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef frexp -/* Assume frexp is always declared. */ -_GL_WARN_ON_USE (frexp, "frexp is unportable - " - "use gnulib module frexp for portability"); -#endif - -/* Write x as - x = mantissa * 2^exp - where - If x finite and nonzero: 0.5 <= |mantissa| < 1.0. - If x is zero: mantissa = x, exp = 0. - If x is infinite or NaN: mantissa = x, exp unspecified. - Store exp in *EXPPTR and return mantissa. */ -#if @GNULIB_FREXPL@ && @REPLACE_FREXPL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef frexpl -# define frexpl rpl_frexpl -# endif -_GL_FUNCDECL_RPL (frexpl, long double, - (long double x, int *expptr) _GL_ARG_NONNULL ((2))); -_GL_CXXALIAS_RPL (frexpl, long double, (long double x, int *expptr)); -#else -# if !@HAVE_DECL_FREXPL@ -_GL_FUNCDECL_SYS (frexpl, long double, - (long double x, int *expptr) _GL_ARG_NONNULL ((2))); -# endif -# if @GNULIB_FREXPL@ -_GL_CXXALIAS_SYS (frexpl, long double, (long double x, int *expptr)); -# endif -#endif -#if @GNULIB_FREXPL@ && !(@REPLACE_FREXPL@ && !@HAVE_DECL_FREXPL@) -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (frexpl); -# endif -#endif -#if !@GNULIB_FREXPL@ && defined GNULIB_POSIXCHECK -# undef frexpl -# if HAVE_RAW_DECL_FREXPL -_GL_WARN_ON_USE (frexpl, "frexpl is unportable - " - "use gnulib module frexpl for portability"); -# endif -#endif - - -/* Return sqrt(x^2+y^2). */ -#if @GNULIB_HYPOTF@ -# if @REPLACE_HYPOTF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef hypotf -# define hypotf rpl_hypotf -# endif -_GL_FUNCDECL_RPL (hypotf, float, (float x, float y)); -_GL_CXXALIAS_RPL (hypotf, float, (float x, float y)); -# else -# if !@HAVE_HYPOTF@ -_GL_FUNCDECL_SYS (hypotf, float, (float x, float y)); -# endif -_GL_CXXALIAS_SYS (hypotf, float, (float x, float y)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (hypotf); -# endif -#elif defined GNULIB_POSIXCHECK -# undef hypotf -# if HAVE_RAW_DECL_HYPOTF -_GL_WARN_ON_USE (hypotf, "hypotf is unportable - " - "use gnulib module hypotf for portability"); -# endif -#endif - -/* Return sqrt(x^2+y^2). */ -#if @GNULIB_HYPOT@ -# if @REPLACE_HYPOT@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef hypot -# define hypot rpl_hypot -# endif -_GL_FUNCDECL_RPL (hypot, double, (double x, double y)); -_GL_CXXALIAS_RPL (hypot, double, (double x, double y)); -# else -_GL_CXXALIAS_SYS (hypot, double, (double x, double y)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (hypot, double, (double x, double y)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef hypot -# if HAVE_RAW_DECL_HYPOT -_GL_WARN_ON_USE (hypotf, "hypot has portability problems - " - "use gnulib module hypot for portability"); -# endif -#endif - -/* Return sqrt(x^2+y^2). */ -#if @GNULIB_HYPOTL@ -# if @REPLACE_HYPOTL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef hypotl -# define hypotl rpl_hypotl -# endif -_GL_FUNCDECL_RPL (hypotl, long double, (long double x, long double y)); -_GL_CXXALIAS_RPL (hypotl, long double, (long double x, long double y)); -# else -# if !@HAVE_HYPOTL@ -_GL_FUNCDECL_SYS (hypotl, long double, (long double x, long double y)); -# endif -_GL_CXXALIAS_SYS (hypotl, long double, (long double x, long double y)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (hypotl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef hypotl -# if HAVE_RAW_DECL_HYPOTL -_GL_WARN_ON_USE (hypotl, "hypotl is unportable - " - "use gnulib module hypotl for portability"); -# endif -#endif - - -#if @GNULIB_ILOGBF@ -# if @REPLACE_ILOGBF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef ilogbf -# define ilogbf rpl_ilogbf -# endif -_GL_FUNCDECL_RPL (ilogbf, int, (float x)); -_GL_CXXALIAS_RPL (ilogbf, int, (float x)); -# else -# if !@HAVE_ILOGBF@ -_GL_FUNCDECL_SYS (ilogbf, int, (float x)); -# endif -_GL_CXXALIAS_SYS (ilogbf, int, (float x)); -# endif -_GL_CXXALIASWARN (ilogbf); -#elif defined GNULIB_POSIXCHECK -# undef ilogbf -# if HAVE_RAW_DECL_ILOGBF -_GL_WARN_ON_USE (ilogbf, "ilogbf is unportable - " - "use gnulib module ilogbf for portability"); -# endif -#endif - -#if @GNULIB_ILOGB@ -# if @REPLACE_ILOGB@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef ilogb -# define ilogb rpl_ilogb -# endif -_GL_FUNCDECL_RPL (ilogb, int, (double x)); -_GL_CXXALIAS_RPL (ilogb, int, (double x)); -# else -# if !@HAVE_ILOGB@ -_GL_FUNCDECL_SYS (ilogb, int, (double x)); -# endif -_GL_CXXALIAS_SYS (ilogb, int, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (ilogb, int, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef ilogb -# if HAVE_RAW_DECL_ILOGB -_GL_WARN_ON_USE (ilogb, "ilogb is unportable - " - "use gnulib module ilogb for portability"); -# endif -#endif - -#if @GNULIB_ILOGBL@ -# if @REPLACE_ILOGBL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef ilogbl -# define ilogbl rpl_ilogbl -# endif -_GL_FUNCDECL_RPL (ilogbl, int, (long double x)); -_GL_CXXALIAS_RPL (ilogbl, int, (long double x)); -# else -# if !@HAVE_ILOGBL@ -# undef ilogbl -_GL_FUNCDECL_SYS (ilogbl, int, (long double x)); -# endif -_GL_CXXALIAS_SYS (ilogbl, int, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (ilogbl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef ilogbl -# if HAVE_RAW_DECL_ILOGBL -_GL_WARN_ON_USE (ilogbl, "ilogbl is unportable - " - "use gnulib module ilogbl for portability"); -# endif -#endif - - -#if @GNULIB_MDA_J0@ -/* On native Windows, map 'j0' to '_j0', so that -loldnames is not - required. In C++ with GNULIB_NAMESPACE, avoid differences between - platforms by defining GNULIB_NAMESPACE::j0 always. */ -# if defined _WIN32 && !defined __CYGWIN__ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef j0 -# define j0 _j0 -# endif -_GL_CXXALIAS_MDA (j0, double, (double x)); -# else -_GL_CXXALIAS_SYS (j0, double, (double x)); -# endif -_GL_CXXALIASWARN (j0); -#endif - -#if @GNULIB_MDA_J1@ -/* On native Windows, map 'j1' to '_j1', so that -loldnames is not - required. In C++ with GNULIB_NAMESPACE, avoid differences between - platforms by defining GNULIB_NAMESPACE::j1 always. */ -# if defined _WIN32 && !defined __CYGWIN__ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef j1 -# define j1 _j1 -# endif -_GL_CXXALIAS_MDA (j1, double, (double x)); -# else -_GL_CXXALIAS_SYS (j1, double, (double x)); -# endif -_GL_CXXALIASWARN (j1); -#endif - -#if @GNULIB_MDA_JN@ -/* On native Windows, map 'jn' to '_jn', so that -loldnames is not - required. In C++ with GNULIB_NAMESPACE, avoid differences between - platforms by defining GNULIB_NAMESPACE::jn always. */ -# if defined _WIN32 && !defined __CYGWIN__ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef jn -# define jn _jn -# endif -_GL_CXXALIAS_MDA (jn, double, (int n, double x)); -# else -_GL_CXXALIAS_SYS (jn, double, (int n, double x)); -# endif -_GL_CXXALIASWARN (jn); -#endif - - -/* Return x * 2^exp. */ -#if @GNULIB_LDEXPF@ -# if !@HAVE_LDEXPF@ -# undef ldexpf -_GL_FUNCDECL_SYS (ldexpf, float, (float x, int exp)); -# endif -_GL_CXXALIAS_SYS (ldexpf, float, (float x, int exp)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (ldexpf); -# endif -#elif defined GNULIB_POSIXCHECK -# undef ldexpf -# if HAVE_RAW_DECL_LDEXPF -_GL_WARN_ON_USE (ldexpf, "ldexpf is unportable - " - "use gnulib module ldexpf for portability"); -# endif -#endif - -/* Return x * 2^exp. */ -#if @GNULIB_LDEXPL@ && @REPLACE_LDEXPL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef ldexpl -# define ldexpl rpl_ldexpl -# endif -_GL_FUNCDECL_RPL (ldexpl, long double, (long double x, int exp)); -_GL_CXXALIAS_RPL (ldexpl, long double, (long double x, int exp)); -#else -# if !@HAVE_DECL_LDEXPL@ -_GL_FUNCDECL_SYS (ldexpl, long double, (long double x, int exp)); -# endif -# if @GNULIB_LDEXPL@ -_GL_CXXALIAS_SYS (ldexpl, long double, (long double x, int exp)); -# endif -#endif -#if @GNULIB_LDEXPL@ -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (ldexpl); -# endif -#endif -#if !@GNULIB_LDEXPL@ && defined GNULIB_POSIXCHECK -# undef ldexpl -# if HAVE_RAW_DECL_LDEXPL -_GL_WARN_ON_USE (ldexpl, "ldexpl is unportable - " - "use gnulib module ldexpl for portability"); -# endif -#endif - - -#if @GNULIB_LOGF@ -# if @REPLACE_LOGF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef logf -# define logf rpl_logf -# endif -_GL_FUNCDECL_RPL (logf, float, (float x)); -_GL_CXXALIAS_RPL (logf, float, (float x)); -# else -# if !@HAVE_LOGF@ -# undef logf -_GL_FUNCDECL_SYS (logf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (logf, float, (float x)); -# endif -_GL_CXXALIASWARN (logf); -#elif defined GNULIB_POSIXCHECK -# undef logf -# if HAVE_RAW_DECL_LOGF -_GL_WARN_ON_USE (logf, "logf is unportable - " - "use gnulib module logf for portability"); -# endif -#endif - -#if @GNULIB_LOG@ -# if @REPLACE_LOG@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef log -# define log rpl_log -# endif -_GL_FUNCDECL_RPL (log, double, (double x)); -_GL_CXXALIAS_RPL (log, double, (double x)); -# else -_GL_CXXALIAS_SYS (log, double, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (log, double, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef log -# if HAVE_RAW_DECL_LOG -_GL_WARN_ON_USE (log, "log has portability problems - " - "use gnulib module log for portability"); -# endif -#endif - -#if @GNULIB_LOGL@ -# if @REPLACE_LOGL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef logl -# define logl rpl_logl -# endif -_GL_FUNCDECL_RPL (logl, long double, (long double x)); -_GL_CXXALIAS_RPL (logl, long double, (long double x)); -# else -# if !@HAVE_LOGL@ || !@HAVE_DECL_LOGL@ -# undef logl -_GL_FUNCDECL_SYS (logl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (logl, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (logl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef logl -# if HAVE_RAW_DECL_LOGL -_GL_WARN_ON_USE (logl, "logl is unportable - " - "use gnulib module logl for portability"); -# endif -#endif - - -#if @GNULIB_LOG10F@ -# if @REPLACE_LOG10F@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef log10f -# define log10f rpl_log10f -# endif -_GL_FUNCDECL_RPL (log10f, float, (float x)); -_GL_CXXALIAS_RPL (log10f, float, (float x)); -# else -# if !@HAVE_LOG10F@ -# undef log10f -_GL_FUNCDECL_SYS (log10f, float, (float x)); -# endif -_GL_CXXALIAS_SYS (log10f, float, (float x)); -# endif -_GL_CXXALIASWARN (log10f); -#elif defined GNULIB_POSIXCHECK -# undef log10f -# if HAVE_RAW_DECL_LOG10F -_GL_WARN_ON_USE (log10f, "log10f is unportable - " - "use gnulib module log10f for portability"); -# endif -#endif - -#if @GNULIB_LOG10@ -# if @REPLACE_LOG10@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef log10 -# define log10 rpl_log10 -# endif -_GL_FUNCDECL_RPL (log10, double, (double x)); -_GL_CXXALIAS_RPL (log10, double, (double x)); -# else -_GL_CXXALIAS_SYS (log10, double, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (log10, double, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef log10 -# if HAVE_RAW_DECL_LOG10 -_GL_WARN_ON_USE (log10, "log10 has portability problems - " - "use gnulib module log10 for portability"); -# endif -#endif - -#if @GNULIB_LOG10L@ -# if @REPLACE_LOG10L@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef log10l -# define log10l rpl_log10l -# endif -_GL_FUNCDECL_RPL (log10l, long double, (long double x)); -_GL_CXXALIAS_RPL (log10l, long double, (long double x)); -# else -# if !@HAVE_LOG10L@ || !@HAVE_DECL_LOG10L@ -# undef log10l -_GL_FUNCDECL_SYS (log10l, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (log10l, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (log10l); -# endif -#elif defined GNULIB_POSIXCHECK -# undef log10l -# if HAVE_RAW_DECL_LOG10L -_GL_WARN_ON_USE (log10l, "log10l is unportable - " - "use gnulib module log10l for portability"); -# endif -#endif - - -#if @GNULIB_LOG1PF@ -# if @REPLACE_LOG1PF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef log1pf -# define log1pf rpl_log1pf -# endif -_GL_FUNCDECL_RPL (log1pf, float, (float x)); -_GL_CXXALIAS_RPL (log1pf, float, (float x)); -# else -# if !@HAVE_LOG1PF@ -_GL_FUNCDECL_SYS (log1pf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (log1pf, float, (float x)); -# endif -_GL_CXXALIASWARN (log1pf); -#elif defined GNULIB_POSIXCHECK -# undef log1pf -# if HAVE_RAW_DECL_LOG1PF -_GL_WARN_ON_USE (log1pf, "log1pf is unportable - " - "use gnulib module log1pf for portability"); -# endif -#endif - -#if @GNULIB_LOG1P@ -# if @REPLACE_LOG1P@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef log1p -# define log1p rpl_log1p -# endif -_GL_FUNCDECL_RPL (log1p, double, (double x)); -_GL_CXXALIAS_RPL (log1p, double, (double x)); -# else -# if !@HAVE_LOG1P@ -_GL_FUNCDECL_SYS (log1p, double, (double x)); -# endif -_GL_CXXALIAS_SYS (log1p, double, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (log1p, double, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef log1p -# if HAVE_RAW_DECL_LOG1P -_GL_WARN_ON_USE (log1p, "log1p has portability problems - " - "use gnulib module log1p for portability"); -# endif -#endif - -#if @GNULIB_LOG1PL@ -# if @REPLACE_LOG1PL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef log1pl -# define log1pl rpl_log1pl -# endif -_GL_FUNCDECL_RPL (log1pl, long double, (long double x)); -_GL_CXXALIAS_RPL (log1pl, long double, (long double x)); -# else -# if !@HAVE_LOG1PL@ -_GL_FUNCDECL_SYS (log1pl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (log1pl, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (log1pl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef log1pl -# if HAVE_RAW_DECL_LOG1PL -_GL_WARN_ON_USE (log1pl, "log1pl has portability problems - " - "use gnulib module log1pl for portability"); -# endif -#endif - - -#if @GNULIB_LOG2F@ -# if @REPLACE_LOG2F@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef log2f -# define log2f rpl_log2f -# endif -_GL_FUNCDECL_RPL (log2f, float, (float x)); -_GL_CXXALIAS_RPL (log2f, float, (float x)); -# else -# if !@HAVE_DECL_LOG2F@ -# undef log2f -_GL_FUNCDECL_SYS (log2f, float, (float x)); -# endif -_GL_CXXALIAS_SYS (log2f, float, (float x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (log2f); -# endif -#elif defined GNULIB_POSIXCHECK -# undef log2f -# if HAVE_RAW_DECL_LOG2F -_GL_WARN_ON_USE (log2f, "log2f is unportable - " - "use gnulib module log2f for portability"); -# endif -#endif - -#if @GNULIB_LOG2@ -# if @REPLACE_LOG2@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef log2 -# define log2 rpl_log2 -# endif -_GL_FUNCDECL_RPL (log2, double, (double x)); -_GL_CXXALIAS_RPL (log2, double, (double x)); -# else -# if !@HAVE_DECL_LOG2@ -# undef log2 -_GL_FUNCDECL_SYS (log2, double, (double x)); -# endif -_GL_CXXALIAS_SYS (log2, double, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (log2, double, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef log2 -# if HAVE_RAW_DECL_LOG2 -_GL_WARN_ON_USE (log2, "log2 is unportable - " - "use gnulib module log2 for portability"); -# endif -#endif - -#if @GNULIB_LOG2L@ -# if @REPLACE_LOG2L@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef log2l -# define log2l rpl_log2l -# endif -_GL_FUNCDECL_RPL (log2l, long double, (long double x)); -_GL_CXXALIAS_RPL (log2l, long double, (long double x)); -# else -# if !@HAVE_DECL_LOG2L@ -_GL_FUNCDECL_SYS (log2l, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (log2l, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (log2l); -# endif -#elif defined GNULIB_POSIXCHECK -# undef log2l -# if HAVE_RAW_DECL_LOG2L -_GL_WARN_ON_USE (log2l, "log2l is unportable - " - "use gnulib module log2l for portability"); -# endif -#endif - - -#if @GNULIB_LOGBF@ -# if @REPLACE_LOGBF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef logbf -# define logbf rpl_logbf -# endif -_GL_FUNCDECL_RPL (logbf, float, (float x)); -_GL_CXXALIAS_RPL (logbf, float, (float x)); -# else -# if !@HAVE_LOGBF@ -_GL_FUNCDECL_SYS (logbf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (logbf, float, (float x)); -# endif -_GL_CXXALIASWARN (logbf); -#elif defined GNULIB_POSIXCHECK -# undef logbf -# if HAVE_RAW_DECL_LOGBF -_GL_WARN_ON_USE (logbf, "logbf is unportable - " - "use gnulib module logbf for portability"); -# endif -#endif - -#if @GNULIB_LOGB@ -# if @REPLACE_LOGB@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef logb -# define logb rpl_logb -# endif -_GL_FUNCDECL_RPL (logb, double, (double x)); -_GL_CXXALIAS_RPL (logb, double, (double x)); -# else -# if !@HAVE_DECL_LOGB@ -_GL_FUNCDECL_SYS (logb, double, (double x)); -# endif -_GL_CXXALIAS_SYS (logb, double, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (logb, double, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef logb -# if HAVE_RAW_DECL_LOGB -_GL_WARN_ON_USE (logb, "logb is unportable - " - "use gnulib module logb for portability"); -# endif -#endif - -#if @GNULIB_LOGBL@ -# if @REPLACE_LOGBL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef logbl -# define logbl rpl_logbl -# endif -_GL_FUNCDECL_RPL (logbl, long double, (long double x)); -_GL_CXXALIAS_RPL (logbl, long double, (long double x)); -# else -# if !@HAVE_LOGBL@ -_GL_FUNCDECL_SYS (logbl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (logbl, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (logbl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef logbl -# if HAVE_RAW_DECL_LOGBL -_GL_WARN_ON_USE (logbl, "logbl is unportable - " - "use gnulib module logbl for portability"); -# endif -#endif - - -#if @GNULIB_MODFF@ -# if @REPLACE_MODFF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef modff -# define modff rpl_modff -# endif -_GL_FUNCDECL_RPL (modff, float, (float x, float *iptr) _GL_ARG_NONNULL ((2))); -_GL_CXXALIAS_RPL (modff, float, (float x, float *iptr)); -# else -# if !@HAVE_MODFF@ -# undef modff -_GL_FUNCDECL_SYS (modff, float, (float x, float *iptr) _GL_ARG_NONNULL ((2))); -# endif -_GL_CXXALIAS_SYS (modff, float, (float x, float *iptr)); -# endif -_GL_CXXALIASWARN (modff); -#elif defined GNULIB_POSIXCHECK -# undef modff -# if HAVE_RAW_DECL_MODFF -_GL_WARN_ON_USE (modff, "modff is unportable - " - "use gnulib module modff for portability"); -# endif -#endif - -#if @GNULIB_MODF@ -# if @REPLACE_MODF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef modf -# define modf rpl_modf -# endif -_GL_FUNCDECL_RPL (modf, double, (double x, double *iptr) _GL_ARG_NONNULL ((2))); -_GL_CXXALIAS_RPL (modf, double, (double x, double *iptr)); -# else -_GL_CXXALIAS_SYS (modf, double, (double x, double *iptr)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (modf, double, (double x, double *iptr)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef modf -# if HAVE_RAW_DECL_MODF -_GL_WARN_ON_USE (modf, "modf has portability problems - " - "use gnulib module modf for portability"); -# endif -#endif - -#if @GNULIB_MODFL@ -# if @REPLACE_MODFL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef modfl -# define modfl rpl_modfl -# endif -_GL_FUNCDECL_RPL (modfl, long double, (long double x, long double *iptr) - _GL_ARG_NONNULL ((2))); -_GL_CXXALIAS_RPL (modfl, long double, (long double x, long double *iptr)); -# else -# if !@HAVE_MODFL@ -# undef modfl -_GL_FUNCDECL_SYS (modfl, long double, (long double x, long double *iptr) - _GL_ARG_NONNULL ((2))); -# endif -_GL_CXXALIAS_SYS (modfl, long double, (long double x, long double *iptr)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (modfl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef modfl -# if HAVE_RAW_DECL_MODFL -_GL_WARN_ON_USE (modfl, "modfl is unportable - " - "use gnulib module modfl for portability"); -# endif -#endif - - -#if @GNULIB_POWF@ -# if !@HAVE_POWF@ -# undef powf -_GL_FUNCDECL_SYS (powf, float, (float x, float y)); -# endif -_GL_CXXALIAS_SYS (powf, float, (float x, float y)); -_GL_CXXALIASWARN (powf); -#elif defined GNULIB_POSIXCHECK -# undef powf -# if HAVE_RAW_DECL_POWF -_GL_WARN_ON_USE (powf, "powf is unportable - " - "use gnulib module powf for portability"); -# endif -#endif - - -#if @GNULIB_REMAINDERF@ -# if @REPLACE_REMAINDERF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef remainderf -# define remainderf rpl_remainderf -# endif -_GL_FUNCDECL_RPL (remainderf, float, (float x, float y)); -_GL_CXXALIAS_RPL (remainderf, float, (float x, float y)); -# else -# if !@HAVE_REMAINDERF@ -_GL_FUNCDECL_SYS (remainderf, float, (float x, float y)); -# endif -_GL_CXXALIAS_SYS (remainderf, float, (float x, float y)); -# endif -_GL_CXXALIASWARN (remainderf); -#elif defined GNULIB_POSIXCHECK -# undef remainderf -# if HAVE_RAW_DECL_REMAINDERF -_GL_WARN_ON_USE (remainderf, "remainderf is unportable - " - "use gnulib module remainderf for portability"); -# endif -#endif - -#if @GNULIB_REMAINDER@ -# if @REPLACE_REMAINDER@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef remainder -# define remainder rpl_remainder -# endif -_GL_FUNCDECL_RPL (remainder, double, (double x, double y)); -_GL_CXXALIAS_RPL (remainder, double, (double x, double y)); -# else -# if !@HAVE_REMAINDER@ || !@HAVE_DECL_REMAINDER@ -_GL_FUNCDECL_SYS (remainder, double, (double x, double y)); -# endif -_GL_CXXALIAS_SYS (remainder, double, (double x, double y)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (remainder, double, (double x, double y)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef remainder -# if HAVE_RAW_DECL_REMAINDER -_GL_WARN_ON_USE (remainder, "remainder is unportable - " - "use gnulib module remainder for portability"); -# endif -#endif - -#if @GNULIB_REMAINDERL@ -# if @REPLACE_REMAINDERL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef remainderl -# define remainderl rpl_remainderl -# endif -_GL_FUNCDECL_RPL (remainderl, long double, (long double x, long double y)); -_GL_CXXALIAS_RPL (remainderl, long double, (long double x, long double y)); -# else -# if !@HAVE_DECL_REMAINDERL@ -# undef remainderl -# if !(defined __cplusplus && defined _AIX) -_GL_FUNCDECL_SYS (remainderl, long double, (long double x, long double y)); -# endif -# endif -_GL_CXXALIAS_SYS (remainderl, long double, (long double x, long double y)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (remainderl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef remainderl -# if HAVE_RAW_DECL_REMAINDERL -_GL_WARN_ON_USE (remainderl, "remainderl is unportable - " - "use gnulib module remainderl for portability"); -# endif -#endif - - -#if @GNULIB_RINTF@ -# if !@HAVE_DECL_RINTF@ -_GL_FUNCDECL_SYS (rintf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (rintf, float, (float x)); -_GL_CXXALIASWARN (rintf); -#elif defined GNULIB_POSIXCHECK -# undef rintf -# if HAVE_RAW_DECL_RINTF -_GL_WARN_ON_USE (rintf, "rintf is unportable - " - "use gnulib module rintf for portability"); -# endif -#endif - -#if @GNULIB_RINT@ -# if !@HAVE_RINT@ -_GL_FUNCDECL_SYS (rint, double, (double x)); -# endif -_GL_CXXALIAS_SYS (rint, double, (double x)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (rint, double, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef rint -# if HAVE_RAW_DECL_RINT -_GL_WARN_ON_USE (rint, "rint is unportable - " - "use gnulib module rint for portability"); -# endif -#endif - -#if @GNULIB_RINTL@ -# if @REPLACE_RINTL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef rintl -# define rintl rpl_rintl -# endif -_GL_FUNCDECL_RPL (rintl, long double, (long double x)); -_GL_CXXALIAS_RPL (rintl, long double, (long double x)); -# else -# if !@HAVE_RINTL@ -_GL_FUNCDECL_SYS (rintl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (rintl, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (rintl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef rintl -# if HAVE_RAW_DECL_RINTL -_GL_WARN_ON_USE (rintl, "rintl is unportable - " - "use gnulib module rintl for portability"); -# endif -#endif - - -#if @GNULIB_ROUNDF@ -# if @REPLACE_ROUNDF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef roundf -# define roundf rpl_roundf -# endif -_GL_FUNCDECL_RPL (roundf, float, (float x)); -_GL_CXXALIAS_RPL (roundf, float, (float x)); -# else -# if !@HAVE_DECL_ROUNDF@ -_GL_FUNCDECL_SYS (roundf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (roundf, float, (float x)); -# endif -_GL_CXXALIASWARN (roundf); -#elif defined GNULIB_POSIXCHECK -# undef roundf -# if HAVE_RAW_DECL_ROUNDF -_GL_WARN_ON_USE (roundf, "roundf is unportable - " - "use gnulib module roundf for portability"); -# endif -#endif - -#if @GNULIB_ROUND@ -# if @REPLACE_ROUND@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef round -# define round rpl_round -# endif -_GL_FUNCDECL_RPL (round, double, (double x)); -_GL_CXXALIAS_RPL (round, double, (double x)); -# else -# if !@HAVE_DECL_ROUND@ -_GL_FUNCDECL_SYS (round, double, (double x)); -# endif -_GL_CXXALIAS_SYS (round, double, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (round, double, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef round -# if HAVE_RAW_DECL_ROUND -_GL_WARN_ON_USE (round, "round is unportable - " - "use gnulib module round for portability"); -# endif -#endif - -#if @GNULIB_ROUNDL@ -# if @REPLACE_ROUNDL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef roundl -# define roundl rpl_roundl -# endif -_GL_FUNCDECL_RPL (roundl, long double, (long double x)); -_GL_CXXALIAS_RPL (roundl, long double, (long double x)); -# else -# if !@HAVE_DECL_ROUNDL@ -# undef roundl -# if !(defined __cplusplus && defined _AIX) -_GL_FUNCDECL_SYS (roundl, long double, (long double x)); -# endif -# endif -_GL_CXXALIAS_SYS (roundl, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (roundl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef roundl -# if HAVE_RAW_DECL_ROUNDL -_GL_WARN_ON_USE (roundl, "roundl is unportable - " - "use gnulib module roundl for portability"); -# endif -#endif - - -#if @GNULIB_SINF@ -# if @REPLACE_SINF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef sinf -# define sinf rpl_sinf -# endif -_GL_FUNCDECL_RPL (sinf, float, (float x)); -_GL_CXXALIAS_RPL (sinf, float, (float x)); -# else -# if !@HAVE_SINF@ -# undef sinf -_GL_FUNCDECL_SYS (sinf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (sinf, float, (float x)); -# endif -_GL_CXXALIASWARN (sinf); -#elif defined GNULIB_POSIXCHECK -# undef sinf -# if HAVE_RAW_DECL_SINF -_GL_WARN_ON_USE (sinf, "sinf is unportable - " - "use gnulib module sinf for portability"); -# endif -#endif - -#if @GNULIB_SINL@ -# if !@HAVE_SINL@ || !@HAVE_DECL_SINL@ -# undef sinl -_GL_FUNCDECL_SYS (sinl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (sinl, long double, (long double x)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (sinl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef sinl -# if HAVE_RAW_DECL_SINL -_GL_WARN_ON_USE (sinl, "sinl is unportable - " - "use gnulib module sinl for portability"); -# endif -#endif - - -#if @GNULIB_SINHF@ -# if @REPLACE_SINHF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef sinhf -# define sinhf rpl_sinhf -# endif -_GL_FUNCDECL_RPL (sinhf, float, (float x)); -_GL_CXXALIAS_RPL (sinhf, float, (float x)); -# else -# if !@HAVE_SINHF@ -# undef sinhf -_GL_FUNCDECL_SYS (sinhf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (sinhf, float, (float x)); -# endif -_GL_CXXALIASWARN (sinhf); -#elif defined GNULIB_POSIXCHECK -# undef sinhf -# if HAVE_RAW_DECL_SINHF -_GL_WARN_ON_USE (sinhf, "sinhf is unportable - " - "use gnulib module sinhf for portability"); -# endif -#endif - - -#if @GNULIB_SQRTF@ -# if @REPLACE_SQRTF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef sqrtf -# define sqrtf rpl_sqrtf -# endif -_GL_FUNCDECL_RPL (sqrtf, float, (float x)); -_GL_CXXALIAS_RPL (sqrtf, float, (float x)); -# else -# if !@HAVE_SQRTF@ -# undef sqrtf -_GL_FUNCDECL_SYS (sqrtf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (sqrtf, float, (float x)); -# endif -_GL_CXXALIASWARN (sqrtf); -#elif defined GNULIB_POSIXCHECK -# undef sqrtf -# if HAVE_RAW_DECL_SQRTF -_GL_WARN_ON_USE (sqrtf, "sqrtf is unportable - " - "use gnulib module sqrtf for portability"); -# endif -#endif - -#if @GNULIB_SQRTL@ -# if @REPLACE_SQRTL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef sqrtl -# define sqrtl rpl_sqrtl -# endif -_GL_FUNCDECL_RPL (sqrtl, long double, (long double x)); -_GL_CXXALIAS_RPL (sqrtl, long double, (long double x)); -# else -# if !@HAVE_SQRTL@ || !@HAVE_DECL_SQRTL@ -# undef sqrtl -_GL_FUNCDECL_SYS (sqrtl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (sqrtl, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (sqrtl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef sqrtl -# if HAVE_RAW_DECL_SQRTL -_GL_WARN_ON_USE (sqrtl, "sqrtl is unportable - " - "use gnulib module sqrtl for portability"); -# endif -#endif - - -#if @GNULIB_TANF@ -# if @REPLACE_TANF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef tanf -# define tanf rpl_tanf -# endif -_GL_FUNCDECL_RPL (tanf, float, (float x)); -_GL_CXXALIAS_RPL (tanf, float, (float x)); -# else -# if !@HAVE_TANF@ -# undef tanf -_GL_FUNCDECL_SYS (tanf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (tanf, float, (float x)); -# endif -_GL_CXXALIASWARN (tanf); -#elif defined GNULIB_POSIXCHECK -# undef tanf -# if HAVE_RAW_DECL_TANF -_GL_WARN_ON_USE (tanf, "tanf is unportable - " - "use gnulib module tanf for portability"); -# endif -#endif - -#if @GNULIB_TANL@ -# if !@HAVE_TANL@ || !@HAVE_DECL_TANL@ -# undef tanl -_GL_FUNCDECL_SYS (tanl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (tanl, long double, (long double x)); -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (tanl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef tanl -# if HAVE_RAW_DECL_TANL -_GL_WARN_ON_USE (tanl, "tanl is unportable - " - "use gnulib module tanl for portability"); -# endif -#endif - - -#if @GNULIB_TANHF@ -# if @REPLACE_TANHF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef tanhf -# define tanhf rpl_tanhf -# endif -_GL_FUNCDECL_RPL (tanhf, float, (float x)); -_GL_CXXALIAS_RPL (tanhf, float, (float x)); -# else -# if !@HAVE_TANHF@ -# undef tanhf -_GL_FUNCDECL_SYS (tanhf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (tanhf, float, (float x)); -# endif -_GL_CXXALIASWARN (tanhf); -#elif defined GNULIB_POSIXCHECK -# undef tanhf -# if HAVE_RAW_DECL_TANHF -_GL_WARN_ON_USE (tanhf, "tanhf is unportable - " - "use gnulib module tanhf for portability"); -# endif -#endif - - -#if @GNULIB_TRUNCF@ -# if @REPLACE_TRUNCF@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef truncf -# define truncf rpl_truncf -# endif -_GL_FUNCDECL_RPL (truncf, float, (float x)); -_GL_CXXALIAS_RPL (truncf, float, (float x)); -# else -# if !@HAVE_DECL_TRUNCF@ -_GL_FUNCDECL_SYS (truncf, float, (float x)); -# endif -_GL_CXXALIAS_SYS (truncf, float, (float x)); -# endif -_GL_CXXALIASWARN (truncf); -#elif defined GNULIB_POSIXCHECK -# undef truncf -# if HAVE_RAW_DECL_TRUNCF -_GL_WARN_ON_USE (truncf, "truncf is unportable - " - "use gnulib module truncf for portability"); -# endif -#endif - -#if @GNULIB_TRUNC@ -# if @REPLACE_TRUNC@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef trunc -# define trunc rpl_trunc -# endif -_GL_FUNCDECL_RPL (trunc, double, (double x)); -_GL_CXXALIAS_RPL (trunc, double, (double x)); -# else -# if !@HAVE_DECL_TRUNC@ -_GL_FUNCDECL_SYS (trunc, double, (double x)); -# endif -_GL_CXXALIAS_SYS (trunc, double, (double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN1 (trunc, double, (double x)); -# endif -#elif defined GNULIB_POSIXCHECK -# undef trunc -# if HAVE_RAW_DECL_TRUNC -_GL_WARN_ON_USE (trunc, "trunc is unportable - " - "use gnulib module trunc for portability"); -# endif -#endif - -#if @GNULIB_TRUNCL@ -# if @REPLACE_TRUNCL@ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef truncl -# define truncl rpl_truncl -# endif -_GL_FUNCDECL_RPL (truncl, long double, (long double x)); -_GL_CXXALIAS_RPL (truncl, long double, (long double x)); -# else -# if !@HAVE_DECL_TRUNCL@ -_GL_FUNCDECL_SYS (truncl, long double, (long double x)); -# endif -_GL_CXXALIAS_SYS (truncl, long double, (long double x)); -# endif -# if __GLIBC__ >= 2 -_GL_CXXALIASWARN (truncl); -# endif -#elif defined GNULIB_POSIXCHECK -# undef truncl -# if HAVE_RAW_DECL_TRUNCL -_GL_WARN_ON_USE (truncl, "truncl is unportable - " - "use gnulib module truncl for portability"); -# endif -#endif - - -#if @GNULIB_MDA_Y0@ -/* On native Windows, map 'y0' to '_y0', so that -loldnames is not - required. In C++ with GNULIB_NAMESPACE, avoid differences between - platforms by defining GNULIB_NAMESPACE::y0 always. */ -# if defined _WIN32 && !defined __CYGWIN__ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef y0 -# define y0 _y0 -# endif -_GL_CXXALIAS_MDA (y0, double, (double x)); -# else -_GL_CXXALIAS_SYS (y0, double, (double x)); -# endif -_GL_CXXALIASWARN (y0); -#endif - -#if @GNULIB_MDA_Y1@ -/* On native Windows, map 'y1' to '_y1', so that -loldnames is not - required. In C++ with GNULIB_NAMESPACE, avoid differences between - platforms by defining GNULIB_NAMESPACE::y1 always. */ -# if defined _WIN32 && !defined __CYGWIN__ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef y1 -# define y1 _y1 -# endif -_GL_CXXALIAS_MDA (y1, double, (double x)); -# else -_GL_CXXALIAS_SYS (y1, double, (double x)); -# endif -_GL_CXXALIASWARN (y1); -#endif - -#if @GNULIB_MDA_YN@ -/* On native Windows, map 'yn' to '_yn', so that -loldnames is not - required. In C++ with GNULIB_NAMESPACE, avoid differences between - platforms by defining GNULIB_NAMESPACE::yn always. */ -# if defined _WIN32 && !defined __CYGWIN__ -# if !(defined __cplusplus && defined GNULIB_NAMESPACE) -# undef yn -# define yn _yn -# endif -_GL_CXXALIAS_MDA (yn, double, (int n, double x)); -# else -_GL_CXXALIAS_SYS (yn, double, (int n, double x)); -# endif -_GL_CXXALIASWARN (yn); -#endif - - -/* Definitions of function-like macros come here, after the function - declarations. */ - - -#if @GNULIB_ISFINITE@ -# if @REPLACE_ISFINITE@ -_GL_EXTERN_C int gl_isfinitef (float x); -_GL_EXTERN_C int gl_isfinited (double x); -_GL_EXTERN_C int gl_isfinitel (long double x); -# undef isfinite -# define isfinite(x) \ - (sizeof (x) == sizeof (long double) ? gl_isfinitel (x) : \ - sizeof (x) == sizeof (double) ? gl_isfinited (x) : \ - gl_isfinitef (x)) -# endif -# ifdef __cplusplus -# if defined isfinite || defined GNULIB_NAMESPACE -_GL_MATH_CXX_REAL_FLOATING_DECL_1 (isfinite) -# undef isfinite -# if __GNUC__ >= 6 || (defined __clang__ && !((defined __APPLE__ && defined __MACH__) || defined __FreeBSD__ || defined __OpenBSD__ || defined _AIX || (defined _WIN32 && !defined __CYGWIN__))) - /* This platform's possibly defines isfinite through a set of inline - functions. */ -_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isfinite, rpl_isfinite, bool) -# define isfinite rpl_isfinite -# define GNULIB_NAMESPACE_LACKS_ISFINITE 1 -# else -_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isfinite, isfinite, bool) -# endif -# endif -# endif -#elif defined GNULIB_POSIXCHECK -# if defined isfinite -_GL_WARN_REAL_FLOATING_DECL (isfinite); -# undef isfinite -# define isfinite(x) _GL_WARN_REAL_FLOATING_IMPL (isfinite, x) -# endif -#endif - - -#if @GNULIB_ISINF@ -# if @REPLACE_ISINF@ -_GL_EXTERN_C int gl_isinff (float x); -_GL_EXTERN_C int gl_isinfd (double x); -_GL_EXTERN_C int gl_isinfl (long double x); -# undef isinf -# define isinf(x) \ - (sizeof (x) == sizeof (long double) ? gl_isinfl (x) : \ - sizeof (x) == sizeof (double) ? gl_isinfd (x) : \ - gl_isinff (x)) -# endif -# ifdef __cplusplus -# if defined isinf || defined GNULIB_NAMESPACE -_GL_MATH_CXX_REAL_FLOATING_DECL_1 (isinf) -# undef isinf -# if __GNUC__ >= 6 || (defined __clang__ && !((defined __APPLE__ && defined __MACH__) || defined __FreeBSD__ || defined __OpenBSD__ || (defined _WIN32 && !defined __CYGWIN__))) - /* This platform's possibly defines isinf through a set of inline - functions. */ -_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isinf, rpl_isinf, bool) -# define isinf rpl_isinf -# define GNULIB_NAMESPACE_LACKS_ISINF 1 -# else -_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isinf, isinf, bool) -# endif -# endif -# endif -#elif defined GNULIB_POSIXCHECK -# if defined isinf -_GL_WARN_REAL_FLOATING_DECL (isinf); -# undef isinf -# define isinf(x) _GL_WARN_REAL_FLOATING_IMPL (isinf, x) -# endif -#endif - - -#if @GNULIB_ISNANF@ -/* Test for NaN for 'float' numbers. */ -# if @HAVE_ISNANF@ -/* The original included above provides a declaration of isnan macro - or (older) isnanf function. */ -# if (__GNUC__ >= 4) || (__clang_major__ >= 4) - /* GCC >= 4.0 and clang provide a type-generic built-in for isnan. - GCC >= 4.0 also provides __builtin_isnanf, but clang doesn't. */ -# undef isnanf -# define isnanf(x) __builtin_isnan ((float)(x)) -# elif defined isnan -# undef isnanf -# define isnanf(x) isnan ((float)(x)) -# endif -# else -/* Test whether X is a NaN. */ -# undef isnanf -# define isnanf rpl_isnanf -_GL_EXTERN_C int isnanf (float x); -# endif -#endif - -#if @GNULIB_ISNAND@ -/* Test for NaN for 'double' numbers. - This function is a gnulib extension, unlike isnan() which applied only - to 'double' numbers earlier but now is a type-generic macro. */ -# if @HAVE_ISNAND@ -/* The original included above provides a declaration of isnan - macro. */ -# if (__GNUC__ >= 4) || (__clang_major__ >= 4) - /* GCC >= 4.0 and clang provide a type-generic built-in for isnan. */ -# undef isnand -# define isnand(x) __builtin_isnan ((double)(x)) -# else -# undef isnand -# define isnand(x) isnan ((double)(x)) -# endif -# else -/* Test whether X is a NaN. */ -# undef isnand -# define isnand rpl_isnand -_GL_EXTERN_C int isnand (double x); -# endif -#endif - -#if @GNULIB_ISNANL@ -/* Test for NaN for 'long double' numbers. */ -# if @HAVE_ISNANL@ -/* The original included above provides a declaration of isnan - macro or (older) isnanl function. */ -# if (__GNUC__ >= 4) || (__clang_major__ >= 4) - /* GCC >= 4.0 and clang provide a type-generic built-in for isnan. - GCC >= 4.0 also provides __builtin_isnanl, but clang doesn't. */ -# undef isnanl -# define isnanl(x) __builtin_isnan ((long double)(x)) -# elif defined isnan -# undef isnanl -# define isnanl(x) isnan ((long double)(x)) -# endif -# else -/* Test whether X is a NaN. */ -# undef isnanl -# define isnanl rpl_isnanl -_GL_EXTERN_C int isnanl (long double x) _GL_ATTRIBUTE_CONST; -# endif -#endif - -/* This must come *after* the snippets for GNULIB_ISNANF and GNULIB_ISNANL! */ -#if @GNULIB_ISNAN@ -# if @REPLACE_ISNAN@ -/* We can't just use the isnanf macro (e.g.) as exposed by - isnanf.h (e.g.) here, because those may end up being macros - that recursively expand back to isnan. So use the gnulib - replacements for them directly. */ -# if @HAVE_ISNANF@ && (__GNUC__ >= 4) || (__clang_major__ >= 4) -# define gl_isnan_f(x) __builtin_isnan ((float)(x)) -# else -_GL_EXTERN_C int rpl_isnanf (float x); -# define gl_isnan_f(x) rpl_isnanf (x) -# endif -# if @HAVE_ISNAND@ && (__GNUC__ >= 4) || (__clang_major__ >= 4) -# define gl_isnan_d(x) __builtin_isnan ((double)(x)) -# else -_GL_EXTERN_C int rpl_isnand (double x); -# define gl_isnan_d(x) rpl_isnand (x) -# endif -# if @HAVE_ISNANL@ && (__GNUC__ >= 4) || (__clang_major__ >= 4) -# define gl_isnan_l(x) __builtin_isnan ((long double)(x)) -# else -_GL_EXTERN_C int rpl_isnanl (long double x) _GL_ATTRIBUTE_CONST; -# define gl_isnan_l(x) rpl_isnanl (x) -# endif -# undef isnan -# define isnan(x) \ - (sizeof (x) == sizeof (long double) ? gl_isnan_l (x) : \ - sizeof (x) == sizeof (double) ? gl_isnan_d (x) : \ - gl_isnan_f (x)) -# elif (__GNUC__ >= 4) || (__clang_major__ >= 4) -# undef isnan -# define isnan(x) \ - (sizeof (x) == sizeof (long double) ? __builtin_isnan ((long double)(x)) : \ - sizeof (x) == sizeof (double) ? __builtin_isnan ((double)(x)) : \ - __builtin_isnan ((float)(x))) -# endif -# ifdef __cplusplus -# if defined isnan || defined GNULIB_NAMESPACE -_GL_MATH_CXX_REAL_FLOATING_DECL_1 (isnan) -# undef isnan -# if __GNUC__ >= 6 || (defined __clang__ && !((defined __APPLE__ && defined __MACH__ && __clang_major__ != 12) || (defined __FreeBSD__ && (__clang_major__ < 7 || __clang_major__ >= 11)) || defined __OpenBSD__ || (defined _WIN32 && !defined __CYGWIN__))) - /* This platform's possibly defines isnan through a set of inline - functions. */ -_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isnan, rpl_isnan, bool) -# define isnan rpl_isnan -# define GNULIB_NAMESPACE_LACKS_ISNAN 1 -# elif (defined __FreeBSD__ && __clang_major__ >= 14) - /* Neither of the two possible _GL_MATH_CXX_REAL_FLOATING_DECL_2 invocations - works. Inline functions are already present in /usr/include/c++/v1/math.h, - which comes from LLVM. */ -# define GNULIB_NAMESPACE_LACKS_ISNAN 1 -# else -_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isnan, isnan, bool) -# endif -# endif -# else -/* Ensure isnan is a macro. */ -# ifndef isnan -# define isnan isnan -# endif -# endif -#elif defined GNULIB_POSIXCHECK -# if defined isnan -_GL_WARN_REAL_FLOATING_DECL (isnan); -# undef isnan -# define isnan(x) _GL_WARN_REAL_FLOATING_IMPL (isnan, x) -# endif -#endif - - -#if @GNULIB_SIGNBIT@ -# if (@REPLACE_SIGNBIT_USING_BUILTINS@ \ - && (!defined __cplusplus || __cplusplus < 201103)) -# undef signbit - /* GCC >= 4.0 and clang provide three built-ins for signbit. */ -# define signbit(x) \ - (sizeof (x) == sizeof (long double) ? __builtin_signbitl (x) : \ - sizeof (x) == sizeof (double) ? __builtin_signbit (x) : \ - __builtin_signbitf (x)) -# endif -# if @REPLACE_SIGNBIT@ && !GNULIB_defined_signbit -# undef signbit -_GL_EXTERN_C int gl_signbitf (float arg); -_GL_EXTERN_C int gl_signbitd (double arg); -_GL_EXTERN_C int gl_signbitl (long double arg); -# if (__GNUC__ >= 2 || defined __clang__) && !defined __STRICT_ANSI__ -# define _GL_NUM_UINT_WORDS(type) \ - ((sizeof (type) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) -# if defined FLT_SIGNBIT_WORD && defined FLT_SIGNBIT_BIT && !defined gl_signbitf -# define gl_signbitf_OPTIMIZED_MACRO -# define gl_signbitf(arg) \ - ({ union { float _value; \ - unsigned int _word[_GL_NUM_UINT_WORDS (float)]; \ - } _m; \ - _m._value = (arg); \ - (_m._word[FLT_SIGNBIT_WORD] >> FLT_SIGNBIT_BIT) & 1; \ - }) -# endif -# if defined DBL_SIGNBIT_WORD && defined DBL_SIGNBIT_BIT && !defined gl_signbitd -# define gl_signbitd_OPTIMIZED_MACRO -# define gl_signbitd(arg) \ - ({ union { double _value; \ - unsigned int _word[_GL_NUM_UINT_WORDS (double)]; \ - } _m; \ - _m._value = (arg); \ - (_m._word[DBL_SIGNBIT_WORD] >> DBL_SIGNBIT_BIT) & 1; \ - }) -# endif -# if defined LDBL_SIGNBIT_WORD && defined LDBL_SIGNBIT_BIT && !defined gl_signbitl -# define gl_signbitl_OPTIMIZED_MACRO -# define gl_signbitl(arg) \ - ({ union { long double _value; \ - unsigned int _word[_GL_NUM_UINT_WORDS (long double)]; \ - } _m; \ - _m._value = (arg); \ - (_m._word[LDBL_SIGNBIT_WORD] >> LDBL_SIGNBIT_BIT) & 1; \ - }) -# endif -# endif -# define signbit(x) \ - (sizeof (x) == sizeof (long double) ? gl_signbitl (x) : \ - sizeof (x) == sizeof (double) ? gl_signbitd (x) : \ - gl_signbitf (x)) -# define GNULIB_defined_signbit 1 -# endif -# ifdef __cplusplus -# if defined signbit || defined GNULIB_NAMESPACE -_GL_MATH_CXX_REAL_FLOATING_DECL_1 (signbit) -# undef signbit -# if __GNUC__ >= 6 || (defined __clang__ && !((defined __APPLE__ && defined __MACH__) || defined __FreeBSD__ || defined __OpenBSD__ || defined _AIX || (defined _WIN32 && !defined __CYGWIN__))) - /* This platform's possibly defines signbit through a set of inline - functions. */ -_GL_MATH_CXX_REAL_FLOATING_DECL_2 (signbit, rpl_signbit, bool) -# define signbit rpl_signbit -# define GNULIB_NAMESPACE_LACKS_SIGNBIT 1 -# else -_GL_MATH_CXX_REAL_FLOATING_DECL_2 (signbit, signbit, bool) -# endif -# endif -# endif -#elif defined GNULIB_POSIXCHECK -# if defined signbit -_GL_WARN_REAL_FLOATING_DECL (signbit); -# undef signbit -# define signbit(x) _GL_WARN_REAL_FLOATING_IMPL (signbit, x) -# endif -#endif - -_GL_INLINE_HEADER_END - -#endif /* _@GUARD_PREFIX@_MATH_H */ -#endif /* _GL_INCLUDING_MATH_H */ -#endif /* _@GUARD_PREFIX@_MATH_H */ -#endif diff --git a/lib/mini-gmp.c b/lib/mini-gmp.c index ea037b801dc..69a72bfd460 100644 --- a/lib/mini-gmp.c +++ b/lib/mini-gmp.c @@ -172,12 +172,19 @@ #define gmp_umul_ppmm(w1, w0, u, v) \ } \ } while (0) +/* If mp_limb_t is of size smaller than int, plain u*v implies + automatic promotion to *signed* int, and then multiply may overflow + and cause undefined behavior. Explicitly cast to unsigned int for + that case. */ +#define gmp_umullo_limb(u, v) \ + ((sizeof(mp_limb_t) >= sizeof(int)) ? (u)*(v) : (unsigned int)(u) * (v)) + #define gmp_udiv_qrnnd_preinv(q, r, nh, nl, d, di) \ do { \ mp_limb_t _qh, _ql, _r, _mask; \ gmp_umul_ppmm (_qh, _ql, (nh), (di)); \ gmp_add_ssaaaa (_qh, _ql, _qh, _ql, (nh) + 1, (nl)); \ - _r = (nl) - _qh * (d); \ + _r = (nl) - gmp_umullo_limb (_qh, (d)); \ _mask = -(mp_limb_t) (_r > _ql); /* both > and >= are OK */ \ _qh += _mask; \ _r += _mask & (d); \ @@ -198,7 +205,7 @@ #define gmp_udiv_qr_3by2(q, r1, r0, n2, n1, n0, d1, d0, dinv) \ gmp_add_ssaaaa ((q), _q0, (q), _q0, (n2), (n1)); \ \ /* Compute the two most significant limbs of n - q'd */ \ - (r1) = (n1) - (d1) * (q); \ + (r1) = (n1) - gmp_umullo_limb ((d1), (q)); \ gmp_sub_ddmmss ((r1), (r0), (r1), (n0), (d1), (d0)); \ gmp_umul_ppmm (_t1, _t0, (d0), (q)); \ gmp_sub_ddmmss ((r1), (r0), (r1), (r0), _t1, _t0); \ diff --git a/lib/printf-args.c b/lib/printf-args.c deleted file mode 100644 index b2b21aeec18..00000000000 --- a/lib/printf-args.c +++ /dev/null @@ -1,306 +0,0 @@ -/* Decomposed printf argument list. - Copyright (C) 1999, 2002-2003, 2005-2007, 2009-2023 Free Software - Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* This file can be parametrized with the following macros: - ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. - PRINTF_FETCHARGS Name of the function to be defined. - STATIC Set to 'static' to declare the function static. */ - -#ifndef PRINTF_FETCHARGS -# include -#endif - -/* Specification. */ -#ifndef PRINTF_FETCHARGS -# include "printf-args.h" -#endif - -/* Get INT_WIDTH. */ -#include - -#ifdef STATIC -STATIC -#endif -int -PRINTF_FETCHARGS (va_list args, arguments *a) -{ - size_t i; - argument *ap; - - for (i = 0, ap = &a->arg[0]; i < a->count; i++, ap++) - switch (ap->type) - { - case TYPE_SCHAR: - ap->a.a_schar = va_arg (args, /*signed char*/ int); - break; - case TYPE_UCHAR: - ap->a.a_uchar = va_arg (args, /*unsigned char*/ int); - break; - case TYPE_SHORT: - ap->a.a_short = va_arg (args, /*short*/ int); - break; - case TYPE_USHORT: - ap->a.a_ushort = va_arg (args, /*unsigned short*/ int); - break; - case TYPE_INT: - ap->a.a_int = va_arg (args, int); - break; - case TYPE_UINT: - ap->a.a_uint = va_arg (args, unsigned int); - break; - case TYPE_LONGINT: - ap->a.a_longint = va_arg (args, long int); - break; - case TYPE_ULONGINT: - ap->a.a_ulongint = va_arg (args, unsigned long int); - break; - case TYPE_LONGLONGINT: - ap->a.a_longlongint = va_arg (args, long long int); - break; - case TYPE_ULONGLONGINT: - ap->a.a_ulonglongint = va_arg (args, unsigned long long int); - break; - case TYPE_INT8_T: - #if INT8_WIDTH < INT_WIDTH - ap->a.a_int8_t = va_arg (args, /* int8_t */ int); - #else - ap->a.a_int8_t = va_arg (args, int8_t); - #endif - break; - case TYPE_UINT8_T: - #if UINT8_WIDTH < INT_WIDTH - ap->a.a_uint8_t = va_arg (args, /* uint8_t */ int); - #else - ap->a.a_uint8_t = va_arg (args, uint8_t); - #endif - break; - case TYPE_INT16_T: - #if INT16_WIDTH < INT_WIDTH - ap->a.a_int16_t = va_arg (args, /* int16_t */ int); - #else - ap->a.a_int16_t = va_arg (args, int16_t); - #endif - break; - case TYPE_UINT16_T: - #if UINT16_WIDTH < INT_WIDTH - ap->a.a_uint16_t = va_arg (args, /* uint16_t */ int); - #else - ap->a.a_uint16_t = va_arg (args, uint16_t); - #endif - break; - case TYPE_INT32_T: - #if INT32_WIDTH < INT_WIDTH - ap->a.a_int32_t = va_arg (args, /* int32_t */ int); - #else - ap->a.a_int32_t = va_arg (args, int32_t); - #endif - break; - case TYPE_UINT32_T: - #if UINT32_WIDTH < INT_WIDTH - ap->a.a_uint32_t = va_arg (args, /* uint32_t */ int); - #else - ap->a.a_uint32_t = va_arg (args, uint32_t); - #endif - break; - case TYPE_INT64_T: - ap->a.a_int64_t = va_arg (args, int64_t); - break; - case TYPE_UINT64_T: - ap->a.a_uint64_t = va_arg (args, uint64_t); - break; - case TYPE_INT_FAST8_T: - #if INT_FAST8_WIDTH < INT_WIDTH - ap->a.a_int_fast8_t = va_arg (args, /* int_fast8_t */ int); - #else - ap->a.a_int_fast8_t = va_arg (args, int_fast8_t); - #endif - break; - case TYPE_UINT_FAST8_T: - #if UINT_FAST8_WIDTH < INT_WIDTH - ap->a.a_uint_fast8_t = va_arg (args, /* uint_fast8_t */ int); - #else - ap->a.a_uint_fast8_t = va_arg (args, uint_fast8_t); - #endif - break; - case TYPE_INT_FAST16_T: - #if INT_FAST16_WIDTH < INT_WIDTH - ap->a.a_int_fast16_t = va_arg (args, /* int_fast16_t */ int); - #else - ap->a.a_int_fast16_t = va_arg (args, int_fast16_t); - #endif - break; - case TYPE_UINT_FAST16_T: - #if UINT_FAST16_WIDTH < INT_WIDTH - ap->a.a_uint_fast16_t = va_arg (args, /* uint_fast16_t */ int); - #else - ap->a.a_uint_fast16_t = va_arg (args, uint_fast16_t); - #endif - break; - case TYPE_INT_FAST32_T: - #if INT_FAST32_WIDTH < INT_WIDTH - ap->a.a_int_fast32_t = va_arg (args, /* int_fast32_t */ int); - #else - ap->a.a_int_fast32_t = va_arg (args, int_fast32_t); - #endif - break; - case TYPE_UINT_FAST32_T: - #if UINT_FAST32_WIDTH < INT_WIDTH - ap->a.a_uint_fast32_t = va_arg (args, /* uint_fast32_t */ int); - #else - ap->a.a_uint_fast32_t = va_arg (args, uint_fast32_t); - #endif - break; - case TYPE_INT_FAST64_T: - ap->a.a_int_fast64_t = va_arg (args, int_fast64_t); - break; - case TYPE_UINT_FAST64_T: - ap->a.a_uint_fast64_t = va_arg (args, uint_fast64_t); - break; - case TYPE_DOUBLE: - ap->a.a_double = va_arg (args, double); - break; - case TYPE_LONGDOUBLE: - ap->a.a_longdouble = va_arg (args, long double); - break; - case TYPE_CHAR: - ap->a.a_char = va_arg (args, int); - break; -#if HAVE_WINT_T - case TYPE_WIDE_CHAR: - /* Although ISO C 99 7.24.1.(2) says that wint_t is "unchanged by - default argument promotions", this is not the case in mingw32, - where wint_t is 'unsigned short'. */ - ap->a.a_wide_char = - (sizeof (wint_t) < sizeof (int) - ? (wint_t) va_arg (args, int) - : va_arg (args, wint_t)); - break; -#endif - case TYPE_STRING: - ap->a.a_string = va_arg (args, const char *); - /* A null pointer is an invalid argument for "%s", but in practice - it occurs quite frequently in printf statements that produce - debug output. Use a fallback in this case. */ - if (ap->a.a_string == NULL) - ap->a.a_string = "(NULL)"; - break; -#if HAVE_WCHAR_T - case TYPE_WIDE_STRING: - ap->a.a_wide_string = va_arg (args, const wchar_t *); - /* A null pointer is an invalid argument for "%ls", but in practice - it occurs quite frequently in printf statements that produce - debug output. Use a fallback in this case. */ - if (ap->a.a_wide_string == NULL) - { - static const wchar_t wide_null_string[] = - { - (wchar_t)'(', - (wchar_t)'N', (wchar_t)'U', (wchar_t)'L', (wchar_t)'L', - (wchar_t)')', - (wchar_t)0 - }; - ap->a.a_wide_string = wide_null_string; - } - break; -#endif - case TYPE_POINTER: - ap->a.a_pointer = va_arg (args, void *); - break; - case TYPE_COUNT_SCHAR_POINTER: - ap->a.a_count_schar_pointer = va_arg (args, signed char *); - break; - case TYPE_COUNT_SHORT_POINTER: - ap->a.a_count_short_pointer = va_arg (args, short *); - break; - case TYPE_COUNT_INT_POINTER: - ap->a.a_count_int_pointer = va_arg (args, int *); - break; - case TYPE_COUNT_LONGINT_POINTER: - ap->a.a_count_longint_pointer = va_arg (args, long int *); - break; - case TYPE_COUNT_LONGLONGINT_POINTER: - ap->a.a_count_longlongint_pointer = va_arg (args, long long int *); - break; - case TYPE_COUNT_INT8_T_POINTER: - ap->a.a_count_int8_t_pointer = va_arg (args, int8_t *); - break; - case TYPE_COUNT_INT16_T_POINTER: - ap->a.a_count_int16_t_pointer = va_arg (args, int16_t *); - break; - case TYPE_COUNT_INT32_T_POINTER: - ap->a.a_count_int32_t_pointer = va_arg (args, int32_t *); - break; - case TYPE_COUNT_INT64_T_POINTER: - ap->a.a_count_int64_t_pointer = va_arg (args, int64_t *); - break; - case TYPE_COUNT_INT_FAST8_T_POINTER: - ap->a.a_count_int_fast8_t_pointer = va_arg (args, int_fast8_t *); - break; - case TYPE_COUNT_INT_FAST16_T_POINTER: - ap->a.a_count_int_fast16_t_pointer = va_arg (args, int_fast16_t *); - break; - case TYPE_COUNT_INT_FAST32_T_POINTER: - ap->a.a_count_int_fast32_t_pointer = va_arg (args, int_fast32_t *); - break; - case TYPE_COUNT_INT_FAST64_T_POINTER: - ap->a.a_count_int_fast64_t_pointer = va_arg (args, int_fast64_t *); - break; -#if ENABLE_UNISTDIO - /* The unistdio extensions. */ - case TYPE_U8_STRING: - ap->a.a_u8_string = va_arg (args, const uint8_t *); - /* A null pointer is an invalid argument for "%U", but in practice - it occurs quite frequently in printf statements that produce - debug output. Use a fallback in this case. */ - if (ap->a.a_u8_string == NULL) - { - static const uint8_t u8_null_string[] = - { '(', 'N', 'U', 'L', 'L', ')', 0 }; - ap->a.a_u8_string = u8_null_string; - } - break; - case TYPE_U16_STRING: - ap->a.a_u16_string = va_arg (args, const uint16_t *); - /* A null pointer is an invalid argument for "%lU", but in practice - it occurs quite frequently in printf statements that produce - debug output. Use a fallback in this case. */ - if (ap->a.a_u16_string == NULL) - { - static const uint16_t u16_null_string[] = - { '(', 'N', 'U', 'L', 'L', ')', 0 }; - ap->a.a_u16_string = u16_null_string; - } - break; - case TYPE_U32_STRING: - ap->a.a_u32_string = va_arg (args, const uint32_t *); - /* A null pointer is an invalid argument for "%llU", but in practice - it occurs quite frequently in printf statements that produce - debug output. Use a fallback in this case. */ - if (ap->a.a_u32_string == NULL) - { - static const uint32_t u32_null_string[] = - { '(', 'N', 'U', 'L', 'L', ')', 0 }; - ap->a.a_u32_string = u32_null_string; - } - break; -#endif - default: - /* Unknown type. */ - return -1; - } - return 0; -} diff --git a/lib/printf-args.h b/lib/printf-args.h deleted file mode 100644 index 11016102828..00000000000 --- a/lib/printf-args.h +++ /dev/null @@ -1,205 +0,0 @@ -/* Decomposed printf argument list. - Copyright (C) 1999, 2002-2003, 2006-2007, 2011-2023 Free Software - Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#ifndef _PRINTF_ARGS_H -#define _PRINTF_ARGS_H - -/* This file can be parametrized with the following macros: - ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. - PRINTF_FETCHARGS Name of the function to be declared. - STATIC Set to 'static' to declare the function static. */ - -/* Default parameters. */ -#ifndef PRINTF_FETCHARGS -# define PRINTF_FETCHARGS printf_fetchargs -#endif - -/* Get size_t. */ -#include - -/* Get wchar_t. */ -#if HAVE_WCHAR_T -# include -#endif - -/* Get wint_t. */ -#if HAVE_WINT_T -# include -#endif - -/* Get intN_t, uintN_t, intN_fast_t, uintN_fast_t. */ -#include - -/* Get va_list. */ -#include - - -/* Argument types */ -typedef enum -{ - TYPE_NONE, - TYPE_SCHAR, - TYPE_UCHAR, - TYPE_SHORT, - TYPE_USHORT, - TYPE_INT, - TYPE_UINT, - TYPE_LONGINT, - TYPE_ULONGINT, - TYPE_LONGLONGINT, - TYPE_ULONGLONGINT, - /* According to ISO C 23 § 7.23.6.1, "all exact-width integer types", - "all minimum-width integer types", and "all fastest minimum-width integer - types" defined in should be supported. But for portability - between platforms, we support only those with N = 8, 16, 32, 64. */ - TYPE_INT8_T, - TYPE_UINT8_T, - TYPE_INT16_T, - TYPE_UINT16_T, - TYPE_INT32_T, - TYPE_UINT32_T, - TYPE_INT64_T, - TYPE_UINT64_T, - TYPE_INT_FAST8_T, - TYPE_UINT_FAST8_T, - TYPE_INT_FAST16_T, - TYPE_UINT_FAST16_T, - TYPE_INT_FAST32_T, - TYPE_UINT_FAST32_T, - TYPE_INT_FAST64_T, - TYPE_UINT_FAST64_T, - TYPE_DOUBLE, - TYPE_LONGDOUBLE, - TYPE_CHAR, -#if HAVE_WINT_T - TYPE_WIDE_CHAR, -#endif - TYPE_STRING, -#if HAVE_WCHAR_T - TYPE_WIDE_STRING, -#endif - TYPE_POINTER, - TYPE_COUNT_SCHAR_POINTER, - TYPE_COUNT_SHORT_POINTER, - TYPE_COUNT_INT_POINTER, - TYPE_COUNT_LONGINT_POINTER, - TYPE_COUNT_LONGLONGINT_POINTER, - TYPE_COUNT_INT8_T_POINTER, - TYPE_COUNT_INT16_T_POINTER, - TYPE_COUNT_INT32_T_POINTER, - TYPE_COUNT_INT64_T_POINTER, - TYPE_COUNT_INT_FAST8_T_POINTER, - TYPE_COUNT_INT_FAST16_T_POINTER, - TYPE_COUNT_INT_FAST32_T_POINTER, - TYPE_COUNT_INT_FAST64_T_POINTER -#if ENABLE_UNISTDIO - /* The unistdio extensions. */ -, TYPE_U8_STRING -, TYPE_U16_STRING -, TYPE_U32_STRING -#endif -} arg_type; - -/* Polymorphic argument */ -typedef struct -{ - arg_type type; - union - { - signed char a_schar; - unsigned char a_uchar; - short a_short; - unsigned short a_ushort; - int a_int; - unsigned int a_uint; - long int a_longint; - unsigned long int a_ulongint; - long long int a_longlongint; - unsigned long long int a_ulonglongint; - int8_t a_int8_t; - uint8_t a_uint8_t; - int16_t a_int16_t; - uint16_t a_uint16_t; - int32_t a_int32_t; - uint32_t a_uint32_t; - int64_t a_int64_t; - uint64_t a_uint64_t; - int_fast8_t a_int_fast8_t; - uint_fast8_t a_uint_fast8_t; - int_fast16_t a_int_fast16_t; - uint_fast16_t a_uint_fast16_t; - int_fast32_t a_int_fast32_t; - uint_fast32_t a_uint_fast32_t; - int_fast64_t a_int_fast64_t; - uint_fast64_t a_uint_fast64_t; - float a_float; /* unused */ - double a_double; - long double a_longdouble; - int a_char; -#if HAVE_WINT_T - wint_t a_wide_char; -#endif - const char* a_string; -#if HAVE_WCHAR_T - const wchar_t* a_wide_string; -#endif - void* a_pointer; - signed char * a_count_schar_pointer; - short * a_count_short_pointer; - int * a_count_int_pointer; - long int * a_count_longint_pointer; - long long int * a_count_longlongint_pointer; - int8_t * a_count_int8_t_pointer; - int16_t * a_count_int16_t_pointer; - int32_t * a_count_int32_t_pointer; - int64_t * a_count_int64_t_pointer; - int_fast8_t * a_count_int_fast8_t_pointer; - int_fast16_t * a_count_int_fast16_t_pointer; - int_fast32_t * a_count_int_fast32_t_pointer; - int_fast64_t * a_count_int_fast64_t_pointer; -#if ENABLE_UNISTDIO - /* The unistdio extensions. */ - const uint8_t * a_u8_string; - const uint16_t * a_u16_string; - const uint32_t * a_u32_string; -#endif - } - a; -} -argument; - -/* Number of directly allocated arguments (no malloc() needed). */ -#define N_DIRECT_ALLOC_ARGUMENTS 7 - -typedef struct -{ - size_t count; - argument *arg; - argument direct_alloc_arg[N_DIRECT_ALLOC_ARGUMENTS]; -} -arguments; - - -/* Fetch the arguments, putting them into a. */ -#ifdef STATIC -STATIC -#else -extern -#endif -int PRINTF_FETCHARGS (va_list args, arguments *a); - -#endif /* _PRINTF_ARGS_H */ diff --git a/lib/printf-frexp.c b/lib/printf-frexp.c deleted file mode 100644 index 8252b0656cb..00000000000 --- a/lib/printf-frexp.c +++ /dev/null @@ -1,190 +0,0 @@ -/* Split a double into fraction and mantissa, for hexadecimal printf. - Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#if ! defined USE_LONG_DOUBLE -# include -#endif - -/* Specification. */ -#ifdef USE_LONG_DOUBLE -# include "printf-frexpl.h" -#else -# include "printf-frexp.h" -#endif - -#include -#include -#ifdef USE_LONG_DOUBLE -# include "fpucw.h" -#endif - -/* This file assumes FLT_RADIX = 2. If FLT_RADIX is a power of 2 greater - than 2, or not even a power of 2, some rounding errors can occur, so that - then the returned mantissa is only guaranteed to be <= 2.0, not < 2.0. */ - -#ifdef USE_LONG_DOUBLE -# define FUNC printf_frexpl -# define DOUBLE long double -# define MIN_EXP LDBL_MIN_EXP -# if HAVE_FREXPL_IN_LIBC && HAVE_LDEXPL_IN_LIBC -# define USE_FREXP_LDEXP -# define FREXP frexpl -# define LDEXP ldexpl -# endif -# define DECL_ROUNDING DECL_LONG_DOUBLE_ROUNDING -# define BEGIN_ROUNDING() BEGIN_LONG_DOUBLE_ROUNDING () -# define END_ROUNDING() END_LONG_DOUBLE_ROUNDING () -# define L_(literal) literal##L -#else -# define FUNC printf_frexp -# define DOUBLE double -# define MIN_EXP DBL_MIN_EXP -# if HAVE_FREXP_IN_LIBC && HAVE_LDEXP_IN_LIBC -# define USE_FREXP_LDEXP -# define FREXP frexp -# define LDEXP ldexp -# endif -# define DECL_ROUNDING -# define BEGIN_ROUNDING() -# define END_ROUNDING() -# define L_(literal) literal -#endif - -DOUBLE -FUNC (DOUBLE x, int *expptr) -{ - int exponent; - DECL_ROUNDING - - BEGIN_ROUNDING (); - -#ifdef USE_FREXP_LDEXP - /* frexp and ldexp are usually faster than the loop below. */ - x = FREXP (x, &exponent); - - x = x + x; - exponent -= 1; - - if (exponent < MIN_EXP - 1) - { - x = LDEXP (x, exponent - (MIN_EXP - 1)); - exponent = MIN_EXP - 1; - } -#else - { - /* Since the exponent is an 'int', it fits in 64 bits. Therefore the - loops are executed no more than 64 times. */ - DOUBLE pow2[64]; /* pow2[i] = 2^2^i */ - DOUBLE powh[64]; /* powh[i] = 2^-2^i */ - int i; - - exponent = 0; - if (x >= L_(1.0)) - { - /* A nonnegative exponent. */ - { - DOUBLE pow2_i; /* = pow2[i] */ - DOUBLE powh_i; /* = powh[i] */ - - /* Invariants: pow2_i = 2^2^i, powh_i = 2^-2^i, - x * 2^exponent = argument, x >= 1.0. */ - for (i = 0, pow2_i = L_(2.0), powh_i = L_(0.5); - ; - i++, pow2_i = pow2_i * pow2_i, powh_i = powh_i * powh_i) - { - if (x >= pow2_i) - { - exponent += (1 << i); - x *= powh_i; - } - else - break; - - pow2[i] = pow2_i; - powh[i] = powh_i; - } - } - /* Here 1.0 <= x < 2^2^i. */ - } - else - { - /* A negative exponent. */ - { - DOUBLE pow2_i; /* = pow2[i] */ - DOUBLE powh_i; /* = powh[i] */ - - /* Invariants: pow2_i = 2^2^i, powh_i = 2^-2^i, - x * 2^exponent = argument, x < 1.0, exponent >= MIN_EXP - 1. */ - for (i = 0, pow2_i = L_(2.0), powh_i = L_(0.5); - ; - i++, pow2_i = pow2_i * pow2_i, powh_i = powh_i * powh_i) - { - if (exponent - (1 << i) < MIN_EXP - 1) - break; - - exponent -= (1 << i); - x *= pow2_i; - if (x >= L_(1.0)) - break; - - pow2[i] = pow2_i; - powh[i] = powh_i; - } - } - /* Here either x < 1.0 and exponent - 2^i < MIN_EXP - 1 <= exponent, - or 1.0 <= x < 2^2^i and exponent >= MIN_EXP - 1. */ - - if (x < L_(1.0)) - /* Invariants: x * 2^exponent = argument, x < 1.0 and - exponent - 2^i < MIN_EXP - 1 <= exponent. */ - while (i > 0) - { - i--; - if (exponent - (1 << i) >= MIN_EXP - 1) - { - exponent -= (1 << i); - x *= pow2[i]; - if (x >= L_(1.0)) - break; - } - } - - /* Here either x < 1.0 and exponent = MIN_EXP - 1, - or 1.0 <= x < 2^2^i and exponent >= MIN_EXP - 1. */ - } - - /* Invariants: x * 2^exponent = argument, and - either x < 1.0 and exponent = MIN_EXP - 1, - or 1.0 <= x < 2^2^i and exponent >= MIN_EXP - 1. */ - while (i > 0) - { - i--; - if (x >= pow2[i]) - { - exponent += (1 << i); - x *= powh[i]; - } - } - /* Here either x < 1.0 and exponent = MIN_EXP - 1, - or 1.0 <= x < 2.0 and exponent >= MIN_EXP - 1. */ - } -#endif - - END_ROUNDING (); - - *expptr = exponent; - return x; -} diff --git a/lib/printf-frexp.h b/lib/printf-frexp.h deleted file mode 100644 index 7c3f9e585db..00000000000 --- a/lib/printf-frexp.h +++ /dev/null @@ -1,23 +0,0 @@ -/* Split a double into fraction and mantissa, for hexadecimal printf. - Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Write a finite, positive number x as - x = mantissa * 2^exp - where exp >= DBL_MIN_EXP - 1, - mantissa < 2.0, - if x is not a denormalized number then mantissa >= 1.0. - Store exp in *EXPPTR and return mantissa. */ -extern double printf_frexp (double x, int *expptr); diff --git a/lib/printf-frexpl.c b/lib/printf-frexpl.c deleted file mode 100644 index ba7b579b987..00000000000 --- a/lib/printf-frexpl.c +++ /dev/null @@ -1,37 +0,0 @@ -/* Split a 'long double' into fraction and mantissa, for hexadecimal printf. - Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -#if HAVE_SAME_LONG_DOUBLE_AS_DOUBLE - -/* Specification. */ -# include "printf-frexpl.h" - -# include "printf-frexp.h" - -long double -printf_frexpl (long double x, int *expptr) -{ - return printf_frexp (x, expptr); -} - -#else - -# define USE_LONG_DOUBLE -# include "printf-frexp.c" - -#endif diff --git a/lib/printf-frexpl.h b/lib/printf-frexpl.h deleted file mode 100644 index 827443ec579..00000000000 --- a/lib/printf-frexpl.h +++ /dev/null @@ -1,23 +0,0 @@ -/* Split a 'long double' into fraction and mantissa, for hexadecimal printf. - Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* Write a finite, positive number x as - x = mantissa * 2^exp - where exp >= LDBL_MIN_EXP - 1, - mantissa < 2.0, - if x is not a denormalized number then mantissa >= 1.0. - Store exp in *EXPPTR and return mantissa. */ -extern long double printf_frexpl (long double x, int *expptr); diff --git a/lib/printf-parse.c b/lib/printf-parse.c deleted file mode 100644 index d3f2c3cb5d1..00000000000 --- a/lib/printf-parse.c +++ /dev/null @@ -1,714 +0,0 @@ -/* Formatted output to strings. - Copyright (C) 1999-2000, 2002-2003, 2006-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* This file can be parametrized with the following macros: - CHAR_T The element type of the format string. - CHAR_T_ONLY_ASCII Set to 1 to enable verification that all characters - in the format string are ASCII. - DIRECTIVE Structure denoting a format directive. - Depends on CHAR_T. - DIRECTIVES Structure denoting the set of format directives of a - format string. Depends on CHAR_T. - PRINTF_PARSE Function that parses a format string. - Depends on CHAR_T. - STATIC Set to 'static' to declare the function static. - ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. */ - -#ifndef PRINTF_PARSE -# include -#endif - -/* Specification. */ -#ifndef PRINTF_PARSE -# include "printf-parse.h" -#endif - -/* Default parameters. */ -#ifndef PRINTF_PARSE -# define PRINTF_PARSE printf_parse -# define CHAR_T char -# define DIRECTIVE char_directive -# define DIRECTIVES char_directives -#endif - -/* Get size_t, NULL. */ -#include - -/* Get intmax_t. */ -#include - -/* malloc(), realloc(), free(). */ -#include - -/* memcpy(). */ -#include - -/* errno. */ -#include - -/* Checked size_t computations. */ -#include "xsize.h" - -#if CHAR_T_ONLY_ASCII -/* c_isascii(). */ -# include "c-ctype.h" -#endif - -#ifdef STATIC -STATIC -#endif -int -PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a) -{ - const CHAR_T *cp = format; /* pointer into format */ - size_t arg_posn = 0; /* number of regular arguments consumed */ - size_t d_allocated; /* allocated elements of d->dir */ - size_t a_allocated; /* allocated elements of a->arg */ - size_t max_width_length = 0; - size_t max_precision_length = 0; - - d->count = 0; - d_allocated = N_DIRECT_ALLOC_DIRECTIVES; - d->dir = d->direct_alloc_dir; - - a->count = 0; - a_allocated = N_DIRECT_ALLOC_ARGUMENTS; - a->arg = a->direct_alloc_arg; - -#define REGISTER_ARG(_index_,_type_) \ - { \ - size_t n = (_index_); \ - if (n >= a_allocated) \ - { \ - size_t memory_size; \ - argument *memory; \ - \ - a_allocated = xtimes (a_allocated, 2); \ - if (a_allocated <= n) \ - a_allocated = xsum (n, 1); \ - memory_size = xtimes (a_allocated, sizeof (argument)); \ - if (size_overflow_p (memory_size)) \ - /* Overflow, would lead to out of memory. */ \ - goto out_of_memory; \ - memory = (argument *) (a->arg != a->direct_alloc_arg \ - ? realloc (a->arg, memory_size) \ - : malloc (memory_size)); \ - if (memory == NULL) \ - /* Out of memory. */ \ - goto out_of_memory; \ - if (a->arg == a->direct_alloc_arg) \ - memcpy (memory, a->arg, a->count * sizeof (argument)); \ - a->arg = memory; \ - } \ - while (a->count <= n) \ - a->arg[a->count++].type = TYPE_NONE; \ - if (a->arg[n].type == TYPE_NONE) \ - a->arg[n].type = (_type_); \ - else if (a->arg[n].type != (_type_)) \ - /* Ambiguous type for positional argument. */ \ - goto error; \ - } - - while (*cp != '\0') - { - CHAR_T c = *cp++; - if (c == '%') - { - size_t arg_index = ARG_NONE; - DIRECTIVE *dp = &d->dir[d->count]; /* pointer to next directive */ - - /* Initialize the next directive. */ - dp->dir_start = cp - 1; - dp->flags = 0; - dp->width_start = NULL; - dp->width_end = NULL; - dp->width_arg_index = ARG_NONE; - dp->precision_start = NULL; - dp->precision_end = NULL; - dp->precision_arg_index = ARG_NONE; - dp->arg_index = ARG_NONE; - - /* Test for positional argument. */ - if (*cp >= '0' && *cp <= '9') - { - const CHAR_T *np; - - for (np = cp; *np >= '0' && *np <= '9'; np++) - ; - if (*np == '$') - { - size_t n = 0; - - for (np = cp; *np >= '0' && *np <= '9'; np++) - n = xsum (xtimes (n, 10), *np - '0'); - if (n == 0) - /* Positional argument 0. */ - goto error; - if (size_overflow_p (n)) - /* n too large, would lead to out of memory later. */ - goto error; - arg_index = n - 1; - cp = np + 1; - } - } - - /* Read the flags. */ - for (;;) - { - if (*cp == '\'') - { - dp->flags |= FLAG_GROUP; - cp++; - } - else if (*cp == '-') - { - dp->flags |= FLAG_LEFT; - cp++; - } - else if (*cp == '+') - { - dp->flags |= FLAG_SHOWSIGN; - cp++; - } - else if (*cp == ' ') - { - dp->flags |= FLAG_SPACE; - cp++; - } - else if (*cp == '#') - { - dp->flags |= FLAG_ALT; - cp++; - } - else if (*cp == '0') - { - dp->flags |= FLAG_ZERO; - cp++; - } -#if __GLIBC__ >= 2 && !defined __UCLIBC__ - else if (*cp == 'I') - { - dp->flags |= FLAG_LOCALIZED; - cp++; - } -#endif - else - break; - } - - /* Parse the field width. */ - if (*cp == '*') - { - dp->width_start = cp; - cp++; - dp->width_end = cp; - if (max_width_length < 1) - max_width_length = 1; - - /* Test for positional argument. */ - if (*cp >= '0' && *cp <= '9') - { - const CHAR_T *np; - - for (np = cp; *np >= '0' && *np <= '9'; np++) - ; - if (*np == '$') - { - size_t n = 0; - - for (np = cp; *np >= '0' && *np <= '9'; np++) - n = xsum (xtimes (n, 10), *np - '0'); - if (n == 0) - /* Positional argument 0. */ - goto error; - if (size_overflow_p (n)) - /* n too large, would lead to out of memory later. */ - goto error; - dp->width_arg_index = n - 1; - cp = np + 1; - } - } - if (dp->width_arg_index == ARG_NONE) - { - dp->width_arg_index = arg_posn++; - if (dp->width_arg_index == ARG_NONE) - /* arg_posn wrapped around. */ - goto error; - } - REGISTER_ARG (dp->width_arg_index, TYPE_INT); - } - else if (*cp >= '0' && *cp <= '9') - { - size_t width_length; - - dp->width_start = cp; - for (; *cp >= '0' && *cp <= '9'; cp++) - ; - dp->width_end = cp; - width_length = dp->width_end - dp->width_start; - if (max_width_length < width_length) - max_width_length = width_length; - } - - /* Parse the precision. */ - if (*cp == '.') - { - cp++; - if (*cp == '*') - { - dp->precision_start = cp - 1; - cp++; - dp->precision_end = cp; - if (max_precision_length < 2) - max_precision_length = 2; - - /* Test for positional argument. */ - if (*cp >= '0' && *cp <= '9') - { - const CHAR_T *np; - - for (np = cp; *np >= '0' && *np <= '9'; np++) - ; - if (*np == '$') - { - size_t n = 0; - - for (np = cp; *np >= '0' && *np <= '9'; np++) - n = xsum (xtimes (n, 10), *np - '0'); - if (n == 0) - /* Positional argument 0. */ - goto error; - if (size_overflow_p (n)) - /* n too large, would lead to out of memory - later. */ - goto error; - dp->precision_arg_index = n - 1; - cp = np + 1; - } - } - if (dp->precision_arg_index == ARG_NONE) - { - dp->precision_arg_index = arg_posn++; - if (dp->precision_arg_index == ARG_NONE) - /* arg_posn wrapped around. */ - goto error; - } - REGISTER_ARG (dp->precision_arg_index, TYPE_INT); - } - else - { - size_t precision_length; - - dp->precision_start = cp - 1; - for (; *cp >= '0' && *cp <= '9'; cp++) - ; - dp->precision_end = cp; - precision_length = dp->precision_end - dp->precision_start; - if (max_precision_length < precision_length) - max_precision_length = precision_length; - } - } - - { - arg_type type; - - /* Parse argument type/size specifiers. */ - /* Relevant for the conversion characters d, i. */ - arg_type signed_type = TYPE_INT; - /* Relevant for the conversion characters b, o, u, x, X. */ - arg_type unsigned_type = TYPE_UINT; - /* Relevant for the conversion characters n. */ - arg_type pointer_type = TYPE_COUNT_INT_POINTER; - /* Relevant for the conversion characters a, A, e, E, f, F, g, G. */ - arg_type floatingpoint_type = TYPE_DOUBLE; - - if (*cp == 'h') - { - if (cp[1] == 'h') - { - signed_type = TYPE_SCHAR; - unsigned_type = TYPE_UCHAR; - pointer_type = TYPE_COUNT_SCHAR_POINTER; - cp += 2; - } - else - { - signed_type = TYPE_SHORT; - unsigned_type = TYPE_USHORT; - pointer_type = TYPE_COUNT_SHORT_POINTER; - cp++; - } - } - else if (*cp == 'l') - { - if (cp[1] == 'l') - { - signed_type = TYPE_LONGLONGINT; - unsigned_type = TYPE_ULONGLONGINT; - pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; - /* For backward compatibility only. */ - floatingpoint_type = TYPE_LONGDOUBLE; - cp += 2; - } - else - { - signed_type = TYPE_LONGINT; - unsigned_type = TYPE_ULONGINT; - pointer_type = TYPE_COUNT_LONGINT_POINTER; - cp++; - } - } - else if (*cp == 'j') - { - if (sizeof (intmax_t) > sizeof (long)) - { - /* intmax_t = long long */ - signed_type = TYPE_LONGLONGINT; - unsigned_type = TYPE_ULONGLONGINT; - pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; - /* For backward compatibility only. */ - floatingpoint_type = TYPE_LONGDOUBLE; - } - else if (sizeof (intmax_t) > sizeof (int)) - { - /* intmax_t = long */ - signed_type = TYPE_LONGINT; - unsigned_type = TYPE_ULONGINT; - pointer_type = TYPE_COUNT_LONGINT_POINTER; - } - cp++; - } - else if (*cp == 'z' || *cp == 'Z') - { - /* 'z' is standardized in ISO C 99, but glibc uses 'Z' - because the warning facility in gcc-2.95.2 understands - only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */ - if (sizeof (size_t) > sizeof (long)) - { - /* size_t = unsigned long long */ - signed_type = TYPE_LONGLONGINT; - unsigned_type = TYPE_ULONGLONGINT; - pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; - /* For backward compatibility only. */ - floatingpoint_type = TYPE_LONGDOUBLE; - } - else if (sizeof (size_t) > sizeof (int)) - { - /* size_t = unsigned long */ - signed_type = TYPE_LONGINT; - unsigned_type = TYPE_ULONGINT; - pointer_type = TYPE_COUNT_LONGINT_POINTER; - } - cp++; - } - else if (*cp == 't') - { - if (sizeof (ptrdiff_t) > sizeof (long)) - { - /* ptrdiff_t = long long */ - signed_type = TYPE_LONGLONGINT; - unsigned_type = TYPE_ULONGLONGINT; - pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; - /* For backward compatibility only. */ - floatingpoint_type = TYPE_LONGDOUBLE; - } - else if (sizeof (ptrdiff_t) > sizeof (int)) - { - /* ptrdiff_t = long */ - signed_type = TYPE_LONGINT; - unsigned_type = TYPE_ULONGINT; - pointer_type = TYPE_COUNT_LONGINT_POINTER; - } - cp++; - } - else if (*cp == 'w') - { - /* wN and wfN are standardized in ISO C 23. */ - if (cp[1] == 'f') - { - if (cp[2] == '8') - { - signed_type = TYPE_INT_FAST8_T; - unsigned_type = TYPE_UINT_FAST8_T; - pointer_type = TYPE_COUNT_INT_FAST8_T_POINTER; - cp += 3; - } - else if (cp[2] == '1' && cp[3] == '6') - { - signed_type = TYPE_INT_FAST16_T; - unsigned_type = TYPE_UINT_FAST16_T; - pointer_type = TYPE_COUNT_INT_FAST16_T_POINTER; - cp += 4; - } - else if (cp[2] == '3' && cp[3] == '2') - { - signed_type = TYPE_INT_FAST32_T; - unsigned_type = TYPE_UINT_FAST32_T; - pointer_type = TYPE_COUNT_INT_FAST32_T_POINTER; - cp += 4; - } - else if (cp[2] == '6' && cp[3] == '4') - { - signed_type = TYPE_INT_FAST64_T; - unsigned_type = TYPE_UINT_FAST64_T; - pointer_type = TYPE_COUNT_INT_FAST64_T_POINTER; - cp += 4; - } - } - else - { - if (cp[1] == '8') - { - signed_type = TYPE_INT8_T; - unsigned_type = TYPE_UINT8_T; - pointer_type = TYPE_COUNT_INT8_T_POINTER; - cp += 2; - } - else if (cp[1] == '1' && cp[2] == '6') - { - signed_type = TYPE_INT16_T; - unsigned_type = TYPE_UINT16_T; - pointer_type = TYPE_COUNT_INT16_T_POINTER; - cp += 3; - } - else if (cp[1] == '3' && cp[2] == '2') - { - signed_type = TYPE_INT32_T; - unsigned_type = TYPE_UINT32_T; - pointer_type = TYPE_COUNT_INT32_T_POINTER; - cp += 3; - } - else if (cp[1] == '6' && cp[2] == '4') - { - signed_type = TYPE_INT64_T; - unsigned_type = TYPE_UINT64_T; - pointer_type = TYPE_COUNT_INT64_T_POINTER; - cp += 3; - } - } - } - else if (*cp == 'L') - { - signed_type = TYPE_LONGLONGINT; - unsigned_type = TYPE_ULONGLONGINT; - pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; - floatingpoint_type = TYPE_LONGDOUBLE; - cp++; - } -#if defined __APPLE__ && defined __MACH__ - /* On Mac OS X 10.3, PRIdMAX is defined as "qd". - We cannot change it to "lld" because PRIdMAX must also - be understood by the system's printf routines. */ - else if (*cp == 'q') - { - if (64 / 8 > sizeof (long)) - { - /* int64_t = long long */ - signed_type = TYPE_LONGLONGINT; - unsigned_type = TYPE_ULONGLONGINT; - pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; - /* For backward compatibility only. */ - floatingpoint_type = TYPE_LONGDOUBLE; - } - else - { - /* int64_t = long */ - signed_type = TYPE_LONGINT; - unsigned_type = TYPE_ULONGINT; - pointer_type = TYPE_COUNT_LONGINT_POINTER; - } - cp++; - } -#endif -#if defined _WIN32 && ! defined __CYGWIN__ - /* On native Windows, PRIdMAX is defined as "I64d". - We cannot change it to "lld" because PRIdMAX must also - be understood by the system's printf routines. */ - else if (*cp == 'I' && cp[1] == '6' && cp[2] == '4') - { - if (64 / 8 > sizeof (long)) - { - /* __int64_t = long long */ - signed_type = TYPE_LONGLONGINT; - unsigned_type = TYPE_ULONGLONGINT; - pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; - /* For backward compatibility only. */ - floatingpoint_type = TYPE_LONGDOUBLE; - } - else - { - /* __int64_t = long */ - signed_type = TYPE_LONGINT; - unsigned_type = TYPE_ULONGINT; - pointer_type = TYPE_COUNT_LONGINT_POINTER; - } - cp++; - } -#endif - - /* Read the conversion character. */ - c = *cp++; - switch (c) - { - case 'd': case 'i': - type = signed_type; - break; - case 'b': case 'o': case 'u': case 'x': case 'X': - #if SUPPORT_GNU_PRINTF_DIRECTIVES \ - || (__GLIBC__ + (__GLIBC_MINOR__ >= 35) > 2) - case 'B': - #endif - type = unsigned_type; - break; - case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': - case 'a': case 'A': - type = floatingpoint_type; - break; - case 'c': - if (signed_type == TYPE_LONGINT - /* For backward compatibility only. */ - || signed_type == TYPE_LONGLONGINT) -#if HAVE_WINT_T - type = TYPE_WIDE_CHAR; -#else - goto error; -#endif - else - type = TYPE_CHAR; - break; -#if HAVE_WINT_T - case 'C': - type = TYPE_WIDE_CHAR; - c = 'c'; - break; -#endif - case 's': - if (signed_type == TYPE_LONGINT - /* For backward compatibility only. */ - || signed_type == TYPE_LONGLONGINT) -#if HAVE_WCHAR_T - type = TYPE_WIDE_STRING; -#else - goto error; -#endif - else - type = TYPE_STRING; - break; -#if HAVE_WCHAR_T - case 'S': - type = TYPE_WIDE_STRING; - c = 's'; - break; -#endif - case 'p': - type = TYPE_POINTER; - break; - case 'n': - type = pointer_type; - break; -#if ENABLE_UNISTDIO - /* The unistdio extensions. */ - case 'U': - if (signed_type == TYPE_LONGLONGINT) - type = TYPE_U32_STRING; - else if (signed_type == TYPE_LONGINT) - type = TYPE_U16_STRING; - else - type = TYPE_U8_STRING; - break; -#endif - case '%': - type = TYPE_NONE; - break; - default: - /* Unknown conversion character. */ - goto error; - } - - if (type != TYPE_NONE) - { - dp->arg_index = arg_index; - if (dp->arg_index == ARG_NONE) - { - dp->arg_index = arg_posn++; - if (dp->arg_index == ARG_NONE) - /* arg_posn wrapped around. */ - goto error; - } - REGISTER_ARG (dp->arg_index, type); - } - dp->conversion = c; - dp->dir_end = cp; - } - - d->count++; - if (d->count >= d_allocated) - { - size_t memory_size; - DIRECTIVE *memory; - - d_allocated = xtimes (d_allocated, 2); - memory_size = xtimes (d_allocated, sizeof (DIRECTIVE)); - if (size_overflow_p (memory_size)) - /* Overflow, would lead to out of memory. */ - goto out_of_memory; - memory = (DIRECTIVE *) (d->dir != d->direct_alloc_dir - ? realloc (d->dir, memory_size) - : malloc (memory_size)); - if (memory == NULL) - /* Out of memory. */ - goto out_of_memory; - if (d->dir == d->direct_alloc_dir) - memcpy (memory, d->dir, d->count * sizeof (DIRECTIVE)); - d->dir = memory; - } - } -#if CHAR_T_ONLY_ASCII - else if (!c_isascii (c)) - { - /* Non-ASCII character. Not supported. */ - goto error; - } -#endif - } - d->dir[d->count].dir_start = cp; - - d->max_width_length = max_width_length; - d->max_precision_length = max_precision_length; - return 0; - -error: - if (a->arg != a->direct_alloc_arg) - free (a->arg); - if (d->dir != d->direct_alloc_dir) - free (d->dir); - errno = EINVAL; - return -1; - -out_of_memory: - if (a->arg != a->direct_alloc_arg) - free (a->arg); - if (d->dir != d->direct_alloc_dir) - free (d->dir); - errno = ENOMEM; - return -1; -} - -#undef PRINTF_PARSE -#undef DIRECTIVES -#undef DIRECTIVE -#undef CHAR_T_ONLY_ASCII -#undef CHAR_T diff --git a/lib/printf-parse.h b/lib/printf-parse.h deleted file mode 100644 index 45febac1f04..00000000000 --- a/lib/printf-parse.h +++ /dev/null @@ -1,193 +0,0 @@ -/* Parse printf format string. - Copyright (C) 1999, 2002-2003, 2005, 2007, 2010-2023 Free Software - Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#ifndef _PRINTF_PARSE_H -#define _PRINTF_PARSE_H - -/* This file can be parametrized with the following macros: - ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. - STATIC Set to 'static' to declare the function static. */ - -#if HAVE_FEATURES_H -# include /* for __GLIBC__, __UCLIBC__ */ -#endif - -#include "printf-args.h" - - -/* Flags */ -#define FLAG_GROUP 1 /* ' flag */ -#define FLAG_LEFT 2 /* - flag */ -#define FLAG_SHOWSIGN 4 /* + flag */ -#define FLAG_SPACE 8 /* space flag */ -#define FLAG_ALT 16 /* # flag */ -#define FLAG_ZERO 32 -#if __GLIBC__ >= 2 && !defined __UCLIBC__ -# define FLAG_LOCALIZED 64 /* I flag, uses localized digits */ -#endif - -/* arg_index value indicating that no argument is consumed. */ -#define ARG_NONE (~(size_t)0) - -/* xxx_directive: A parsed directive. - xxx_directives: A parsed format string. */ - -/* Number of directly allocated directives (no malloc() needed). */ -#define N_DIRECT_ALLOC_DIRECTIVES 7 - -/* A parsed directive. */ -typedef struct -{ - const char* dir_start; - const char* dir_end; - int flags; - const char* width_start; - const char* width_end; - size_t width_arg_index; - const char* precision_start; - const char* precision_end; - size_t precision_arg_index; - char conversion; /* d i b B o u x X f F e E g G a A c s p n U % but not C S */ - size_t arg_index; -} -char_directive; - -/* A parsed format string. */ -typedef struct -{ - size_t count; - char_directive *dir; - size_t max_width_length; - size_t max_precision_length; - char_directive direct_alloc_dir[N_DIRECT_ALLOC_DIRECTIVES]; -} -char_directives; - -#if ENABLE_UNISTDIO - -/* A parsed directive. */ -typedef struct -{ - const uint8_t* dir_start; - const uint8_t* dir_end; - int flags; - const uint8_t* width_start; - const uint8_t* width_end; - size_t width_arg_index; - const uint8_t* precision_start; - const uint8_t* precision_end; - size_t precision_arg_index; - uint8_t conversion; /* d i b B o u x X f F e E g G a A c s p n U % but not C S */ - size_t arg_index; -} -u8_directive; - -/* A parsed format string. */ -typedef struct -{ - size_t count; - u8_directive *dir; - size_t max_width_length; - size_t max_precision_length; - u8_directive direct_alloc_dir[N_DIRECT_ALLOC_DIRECTIVES]; -} -u8_directives; - -/* A parsed directive. */ -typedef struct -{ - const uint16_t* dir_start; - const uint16_t* dir_end; - int flags; - const uint16_t* width_start; - const uint16_t* width_end; - size_t width_arg_index; - const uint16_t* precision_start; - const uint16_t* precision_end; - size_t precision_arg_index; - uint16_t conversion; /* d i b B o u x X f F e E g G a A c s p n U % but not C S */ - size_t arg_index; -} -u16_directive; - -/* A parsed format string. */ -typedef struct -{ - size_t count; - u16_directive *dir; - size_t max_width_length; - size_t max_precision_length; - u16_directive direct_alloc_dir[N_DIRECT_ALLOC_DIRECTIVES]; -} -u16_directives; - -/* A parsed directive. */ -typedef struct -{ - const uint32_t* dir_start; - const uint32_t* dir_end; - int flags; - const uint32_t* width_start; - const uint32_t* width_end; - size_t width_arg_index; - const uint32_t* precision_start; - const uint32_t* precision_end; - size_t precision_arg_index; - uint32_t conversion; /* d i b B o u x X f F e E g G a A c s p n U % but not C S */ - size_t arg_index; -} -u32_directive; - -/* A parsed format string. */ -typedef struct -{ - size_t count; - u32_directive *dir; - size_t max_width_length; - size_t max_precision_length; - u32_directive direct_alloc_dir[N_DIRECT_ALLOC_DIRECTIVES]; -} -u32_directives; - -#endif - - -/* Parses the format string. Fills in the number N of directives, and fills - in directives[0], ..., directives[N-1], and sets directives[N].dir_start - to the end of the format string. Also fills in the arg_type fields of the - arguments and the needed count of arguments. */ -#if ENABLE_UNISTDIO -extern int - ulc_printf_parse (const char *format, char_directives *d, arguments *a); -extern int - u8_printf_parse (const uint8_t *format, u8_directives *d, arguments *a); -extern int - u16_printf_parse (const uint16_t *format, u16_directives *d, - arguments *a); -extern int - u32_printf_parse (const uint32_t *format, u32_directives *d, - arguments *a); -#else -# ifdef STATIC -STATIC -# else -extern -# endif -int printf_parse (const char *format, char_directives *d, arguments *a); -#endif - -#endif /* _PRINTF_PARSE_H */ diff --git a/lib/printf.c b/lib/printf.c deleted file mode 100644 index 00483271292..00000000000 --- a/lib/printf.c +++ /dev/null @@ -1,40 +0,0 @@ -/* Formatted output to a stream. - Copyright (C) 2007, 2010-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 3 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -/* Specification. */ -#include - -#include - -/* Print formatted output to standard output. - Return string length of formatted string. On error, return a negative - value. */ -int -printf (const char *format, ...) -{ - int retval; - va_list args; - - va_start (args, format); - retval = vfprintf (stdout, format, args); - va_end (args); - - return retval; -} diff --git a/lib/signbitd.c b/lib/signbitd.c deleted file mode 100644 index 706ea394414..00000000000 --- a/lib/signbitd.c +++ /dev/null @@ -1,64 +0,0 @@ -/* signbit() macro: Determine the sign bit of a floating-point number. - Copyright (C) 2007-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -/* Specification. */ -#include - -#include -#include "isnand-nolibm.h" -#include "float+.h" - -#ifdef gl_signbitd_OPTIMIZED_MACRO -# undef gl_signbitd -#endif - -int -gl_signbitd (double arg) -{ -#if defined DBL_SIGNBIT_WORD && defined DBL_SIGNBIT_BIT - /* The use of a union to extract the bits of the representation of a - 'long double' is safe in practice, despite of the "aliasing rules" of - C99, because the GCC docs say - "Even with '-fstrict-aliasing', type-punning is allowed, provided the - memory is accessed through the union type." - and similarly for other compilers. */ -# define NWORDS \ - ((sizeof (double) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) - union { double value; unsigned int word[NWORDS]; } m; - m.value = arg; - return (m.word[DBL_SIGNBIT_WORD] >> DBL_SIGNBIT_BIT) & 1; -#elif HAVE_COPYSIGN_IN_LIBC - return copysign (1.0, arg) < 0; -#else - /* This does not do the right thing for NaN, but this is irrelevant for - most use cases. */ - if (isnand (arg)) - return 0; - if (arg < 0.0) - return 1; - else if (arg == 0.0) - { - /* Distinguish 0.0 and -0.0. */ - static double plus_zero = 0.0; - double arg_mem = arg; - return (memcmp (&plus_zero, &arg_mem, SIZEOF_DBL) != 0); - } - else - return 0; -#endif -} diff --git a/lib/signbitf.c b/lib/signbitf.c deleted file mode 100644 index c25bb64a0d2..00000000000 --- a/lib/signbitf.c +++ /dev/null @@ -1,64 +0,0 @@ -/* signbit() macro: Determine the sign bit of a floating-point number. - Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -/* Specification. */ -#include - -#include -#include "isnanf-nolibm.h" -#include "float+.h" - -#ifdef gl_signbitf_OPTIMIZED_MACRO -# undef gl_signbitf -#endif - -int -gl_signbitf (float arg) -{ -#if defined FLT_SIGNBIT_WORD && defined FLT_SIGNBIT_BIT - /* The use of a union to extract the bits of the representation of a - 'long double' is safe in practice, despite of the "aliasing rules" of - C99, because the GCC docs say - "Even with '-fstrict-aliasing', type-punning is allowed, provided the - memory is accessed through the union type." - and similarly for other compilers. */ -# define NWORDS \ - ((sizeof (float) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) - union { float value; unsigned int word[NWORDS]; } m; - m.value = arg; - return (m.word[FLT_SIGNBIT_WORD] >> FLT_SIGNBIT_BIT) & 1; -#elif HAVE_COPYSIGNF_IN_LIBC - return copysignf (1.0f, arg) < 0; -#else - /* This does not do the right thing for NaN, but this is irrelevant for - most use cases. */ - if (isnanf (arg)) - return 0; - if (arg < 0.0f) - return 1; - else if (arg == 0.0f) - { - /* Distinguish 0.0f and -0.0f. */ - static float plus_zero = 0.0f; - float arg_mem = arg; - return (memcmp (&plus_zero, &arg_mem, SIZEOF_FLT) != 0); - } - else - return 0; -#endif -} diff --git a/lib/signbitl.c b/lib/signbitl.c deleted file mode 100644 index 9d459caf879..00000000000 --- a/lib/signbitl.c +++ /dev/null @@ -1,64 +0,0 @@ -/* signbit() macro: Determine the sign bit of a floating-point number. - Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -/* Specification. */ -#include - -#include -#include "isnanl-nolibm.h" -#include "float+.h" - -#ifdef gl_signbitl_OPTIMIZED_MACRO -# undef gl_signbitl -#endif - -int -gl_signbitl (long double arg) -{ -#if defined LDBL_SIGNBIT_WORD && defined LDBL_SIGNBIT_BIT - /* The use of a union to extract the bits of the representation of a - 'long double' is safe in practice, despite of the "aliasing rules" of - C99, because the GCC docs say - "Even with '-fstrict-aliasing', type-punning is allowed, provided the - memory is accessed through the union type." - and similarly for other compilers. */ -# define NWORDS \ - ((sizeof (long double) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) - union { long double value; unsigned int word[NWORDS]; } m; - m.value = arg; - return (m.word[LDBL_SIGNBIT_WORD] >> LDBL_SIGNBIT_BIT) & 1; -#elif HAVE_COPYSIGNL_IN_LIBC - return copysignl (1.0L, arg) < 0; -#else - /* This does not do the right thing for NaN, but this is irrelevant for - most use cases. */ - if (isnanl (arg)) - return 0; - if (arg < 0.0L) - return 1; - else if (arg == 0.0L) - { - /* Distinguish 0.0L and -0.0L. */ - static long double plus_zero = 0.0L; - long double arg_mem = arg; - return (memcmp (&plus_zero, &arg_mem, SIZEOF_LDBL) != 0); - } - else - return 0; -#endif -} diff --git a/lib/size_max.h b/lib/size_max.h deleted file mode 100644 index 2cfd31a59b8..00000000000 --- a/lib/size_max.h +++ /dev/null @@ -1,35 +0,0 @@ -/* size_max.h -- declare SIZE_MAX through system headers - Copyright (C) 2005-2006, 2009-2023 Free Software Foundation, Inc. - Written by Simon Josefsson. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#ifndef GNULIB_SIZE_MAX_H -#define GNULIB_SIZE_MAX_H - -/* This file uses HAVE_STDINT_H. */ -#if !_GL_CONFIG_H_INCLUDED - #error "Please include config.h first." -#endif - -/* Get SIZE_MAX declaration on systems like Solaris 7/8/9. */ -# include -/* Get SIZE_MAX declaration on systems like glibc 2. */ -# if HAVE_STDINT_H -# include -# endif -/* On systems where these include files don't define it, SIZE_MAX is defined - in config.h. */ - -#endif /* GNULIB_SIZE_MAX_H */ diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c deleted file mode 100644 index 9ad31b2a084..00000000000 --- a/lib/vasnprintf.c +++ /dev/null @@ -1,6963 +0,0 @@ -/* vsprintf with automatic memory allocation. - Copyright (C) 1999, 2002-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* This file can be parametrized with the following macros: - VASNPRINTF The name of the function being defined. - FCHAR_T The element type of the format string. - DCHAR_T The element type of the destination (result) string. - FCHAR_T_ONLY_ASCII Set to 1 to enable verification that all characters - in the format string are ASCII. MUST be set if - FCHAR_T and DCHAR_T are not the same type. - DIRECTIVE Structure denoting a format directive. - Depends on FCHAR_T. - DIRECTIVES Structure denoting the set of format directives of a - format string. Depends on FCHAR_T. - PRINTF_PARSE Function that parses a format string. - Depends on FCHAR_T. - DCHAR_CPY memcpy like function for DCHAR_T[] arrays. - DCHAR_SET memset like function for DCHAR_T[] arrays. - DCHAR_MBSNLEN mbsnlen like function for DCHAR_T[] arrays. - SNPRINTF The system's snprintf (or similar) function. - This may be either snprintf or swprintf. - TCHAR_T The element type of the argument and result string - of the said SNPRINTF function. This may be either - char or wchar_t. The code exploits that - sizeof (TCHAR_T) | sizeof (DCHAR_T) and - alignof (TCHAR_T) <= alignof (DCHAR_T). - DCHAR_IS_TCHAR Set to 1 if DCHAR_T and TCHAR_T are the same type. - DCHAR_CONV_FROM_ENCODING A function to convert from char[] to DCHAR[]. - DCHAR_IS_UINT8_T Set to 1 if DCHAR_T is uint8_t. - DCHAR_IS_UINT16_T Set to 1 if DCHAR_T is uint16_t. - DCHAR_IS_UINT32_T Set to 1 if DCHAR_T is uint32_t. - ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. - ENABLE_WCHAR_FALLBACK Set to 1 to avoid EILSEQ during conversion of wide - characters (wchar_t) and wide character strings - (wchar_t[]) to multibyte sequences. The fallback is the - hexadecimal escape syntax (\unnnn or \Unnnnnnnn) or, - if wchar_t is not Unicode encoded, \wnnnn or \Wnnnnnnnn. - */ - -/* Tell glibc's to provide a prototype for snprintf(). - This must come before because may include - , and once has been included, it's too late. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE 1 -#endif - -#ifndef VASNPRINTF -# include -#endif - -/* As of GCC 11.2.1, gcc -Wanalyzer-too-complex reports that main's - use of CHECK macros expands to code that is too complicated for gcc - -fanalyzer. Suppress the resulting bogus warnings. */ -#if 10 <= __GNUC__ -# pragma GCC diagnostic ignored "-Wanalyzer-null-argument" -#endif - -#include - -/* Specification. */ -#ifndef VASNPRINTF -# if WIDE_CHAR_VERSION -# include "vasnwprintf.h" -# else -# include "vasnprintf.h" -# endif -#endif - -#include /* localeconv() */ -#include /* snprintf(), sprintf() */ -#include /* abort(), malloc(), realloc(), free() */ -#include /* memcpy(), strlen() */ -#include /* mbstate_t, mbrtowc(), mbrlen(), wcrtomb() */ -#include /* errno */ -#include /* CHAR_BIT, INT_WIDTH, LONG_WIDTH */ -#include /* DBL_MAX_EXP, LDBL_MAX_EXP */ -#if HAVE_NL_LANGINFO -# include -#endif -#ifndef VASNPRINTF -# if WIDE_CHAR_VERSION -# include "wprintf-parse.h" -# else -# include "printf-parse.h" -# endif -#endif - -/* Checked size_t computations. */ -#include "xsize.h" - -#include "attribute.h" - -#if NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) -# include -# include "float+.h" -#endif - -#if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE -# include -# include "isnand-nolibm.h" -#endif - -#if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) -# include -# include "isnanl-nolibm.h" -# include "fpucw.h" -#endif - -#if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE -# include -# include "isnand-nolibm.h" -# include "printf-frexp.h" -#endif - -#if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) -# include -# include "isnanl-nolibm.h" -# include "printf-frexpl.h" -# include "fpucw.h" -#endif - -/* Default parameters. */ -#ifndef VASNPRINTF -# if WIDE_CHAR_VERSION -# define VASNPRINTF vasnwprintf -# define FCHAR_T wchar_t -# define DCHAR_T wchar_t -# define DIRECTIVE wchar_t_directive -# define DIRECTIVES wchar_t_directives -# define PRINTF_PARSE wprintf_parse -# define DCHAR_CPY wmemcpy -# define DCHAR_SET wmemset -# else -# define VASNPRINTF vasnprintf -# define FCHAR_T char -# define DCHAR_T char -# define TCHAR_T char -# define DCHAR_IS_TCHAR 1 -# define DIRECTIVE char_directive -# define DIRECTIVES char_directives -# define PRINTF_PARSE printf_parse -# define DCHAR_CPY memcpy -# define DCHAR_SET memset -# endif -#endif -#if WIDE_CHAR_VERSION - /* DCHAR_T is wchar_t. */ -# if HAVE_DECL__SNWPRINTF || (HAVE_SWPRINTF && HAVE_WORKING_SWPRINTF) -# define TCHAR_T wchar_t -# define DCHAR_IS_TCHAR 1 -# define USE_SNPRINTF 1 -# if HAVE_DECL__SNWPRINTF - /* On Windows, the function swprintf() has a different signature than - on Unix; we use the function _snwprintf() or - on mingw - snwprintf() - instead. The mingw function snwprintf() has fewer bugs than the - MSVCRT function _snwprintf(), so prefer that. */ -# if defined __MINGW32__ -# define SNPRINTF snwprintf -# else -# define SNPRINTF _snwprintf -# define USE_MSVC__SNPRINTF 1 -# endif -# else - /* Unix. */ -# define SNPRINTF swprintf -# endif -# else - /* Old platforms such as NetBSD 3.0, OpenBSD 3.8, HP-UX 11.00, IRIX 6.5. */ -# define TCHAR_T char -# endif -#endif -#if !WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR - /* TCHAR_T is char. */ - /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'. - But don't use it on BeOS, since BeOS snprintf produces no output if the - size argument is >= 0x3000000. - Also don't use it on Linux libc5, since there snprintf with size = 1 - writes any output without bounds, like sprintf. */ -# if (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) && !defined __BEOS__ && !(__GNU_LIBRARY__ == 1) -# define USE_SNPRINTF 1 -# else -# define USE_SNPRINTF 0 -# endif -# if HAVE_DECL__SNPRINTF - /* Windows. The mingw function snprintf() has fewer bugs than the MSVCRT - function _snprintf(), so prefer that. */ -# if defined __MINGW32__ -# define SNPRINTF snprintf - /* Here we need to call the native snprintf, not rpl_snprintf. */ -# undef snprintf -# else - /* MSVC versions < 14 did not have snprintf, only _snprintf. */ -# define SNPRINTF _snprintf -# define USE_MSVC__SNPRINTF 1 -# endif -# else - /* Unix. */ -# define SNPRINTF snprintf - /* Here we need to call the native snprintf, not rpl_snprintf. */ -# undef snprintf -# endif -#endif -/* Here we need to call the native sprintf, not rpl_sprintf. */ -#undef sprintf - -/* GCC >= 4.0 with -Wall emits unjustified "... may be used uninitialized" - warnings in this file. Use -Dlint to suppress them. */ -#if defined GCC_LINT || defined lint -# define IF_LINT(Code) Code -#else -# define IF_LINT(Code) /* empty */ -#endif - -/* Avoid some warnings from "gcc -Wshadow". - This file doesn't use the exp() and remainder() functions. */ -#undef exp -#define exp expo -#undef remainder -#define remainder rem - -#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && !WIDE_CHAR_VERSION -# if (HAVE_STRNLEN && !defined _AIX) -# define local_strnlen strnlen -# else -# ifndef local_strnlen_defined -# define local_strnlen_defined 1 -static size_t -local_strnlen (const char *string, size_t maxlen) -{ - const char *end = memchr (string, '\0', maxlen); - return end ? (size_t) (end - string) : maxlen; -} -# endif -# endif -#endif - -#if (((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_WPRINTF_DIRECTIVE_LC) && WIDE_CHAR_VERSION) || ((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS) && !WIDE_CHAR_VERSION && DCHAR_IS_TCHAR)) && HAVE_WCHAR_T -# if HAVE_WCSLEN -# define local_wcslen wcslen -# else - /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid - a dependency towards this library, here is a local substitute. - Define this substitute only once, even if this file is included - twice in the same compilation unit. */ -# ifndef local_wcslen_defined -# define local_wcslen_defined 1 -static size_t -local_wcslen (const wchar_t *s) -{ - const wchar_t *ptr; - - for (ptr = s; *ptr != (wchar_t) 0; ptr++) - ; - return ptr - s; -} -# endif -# endif -#endif - -#if (!USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && HAVE_WCHAR_T && WIDE_CHAR_VERSION -# if HAVE_WCSNLEN && HAVE_DECL_WCSNLEN -# define local_wcsnlen wcsnlen -# else -# ifndef local_wcsnlen_defined -# define local_wcsnlen_defined 1 -static size_t -local_wcsnlen (const wchar_t *s, size_t maxlen) -{ - const wchar_t *ptr; - - for (ptr = s; maxlen > 0 && *ptr != (wchar_t) 0; ptr++, maxlen--) - ; - return ptr - s; -} -# endif -# endif -#endif - -#if (((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T)) && !WIDE_CHAR_VERSION -# if ENABLE_WCHAR_FALLBACK -static size_t -wctomb_fallback (char *s, wchar_t wc) -{ - static char hex[16] = "0123456789ABCDEF"; - - s[0] = '\\'; - if (sizeof (wchar_t) > 2 && wc > 0xffff) - { -# if __STDC_ISO_10646__ || (__GLIBC__ >= 2) || (defined _WIN32 || defined __CYGWIN__) - s[1] = 'U'; -# else - s[1] = 'W'; -# endif - s[2] = hex[(wc & 0xf0000000U) >> 28]; - s[3] = hex[(wc & 0xf000000U) >> 24]; - s[4] = hex[(wc & 0xf00000U) >> 20]; - s[5] = hex[(wc & 0xf0000U) >> 16]; - s[6] = hex[(wc & 0xf000U) >> 12]; - s[7] = hex[(wc & 0xf00U) >> 8]; - s[8] = hex[(wc & 0xf0U) >> 4]; - s[9] = hex[wc & 0xfU]; - return 10; - } - else - { -# if __STDC_ISO_10646__ || (__GLIBC__ >= 2) || (defined _WIN32 || defined __CYGWIN__) - s[1] = 'u'; -# else - s[1] = 'w'; -# endif - s[2] = hex[(wc & 0xf000U) >> 12]; - s[3] = hex[(wc & 0xf00U) >> 8]; - s[4] = hex[(wc & 0xf0U) >> 4]; - s[5] = hex[wc & 0xfU]; - return 6; - } -} -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t -static size_t -local_wcrtomb (char *s, wchar_t wc, mbstate_t *ps) -{ - size_t count = wcrtomb (s, wc, ps); - if (count == (size_t)(-1)) - count = wctomb_fallback (s, wc); - return count; -} -# else -static int -local_wctomb (char *s, wchar_t wc) -{ - int count = wctomb (s, wc); - if (count < 0) - count = wctomb_fallback (s, wc); - return count; -} -# define local_wcrtomb(S, WC, PS) local_wctomb ((S), (WC)) -# endif -# else -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t -# define local_wcrtomb(S, WC, PS) wcrtomb ((S), (WC), (PS)) -# else -# define local_wcrtomb(S, WC, PS) wctomb ((S), (WC)) -# endif -# endif -#endif - -#if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) -/* Determine the decimal-point character according to the current locale. */ -# ifndef decimal_point_char_defined -# define decimal_point_char_defined 1 -static char -decimal_point_char (void) -{ - const char *point; - /* Determine it in a multithread-safe way. We know nl_langinfo is - multithread-safe on glibc systems and Mac OS X systems, but is not required - to be multithread-safe by POSIX. sprintf(), however, is multithread-safe. - localeconv() is rarely multithread-safe. */ -# if HAVE_NL_LANGINFO && (__GLIBC__ || defined __UCLIBC__ || (defined __APPLE__ && defined __MACH__)) - point = nl_langinfo (RADIXCHAR); -# elif 1 - char pointbuf[5]; - sprintf (pointbuf, "%#.0f", 1.0); - point = &pointbuf[1]; -# else - point = localeconv () -> decimal_point; -# endif - /* The decimal point is always a single byte: either '.' or ','. */ - return (point[0] != '\0' ? point[0] : '.'); -} -# endif -#endif - -#if NEED_PRINTF_INFINITE_DOUBLE && !NEED_PRINTF_DOUBLE - -/* Equivalent to !isfinite(x) || x == 0, but does not require libm. */ -static int -is_infinite_or_zero (double x) -{ - return isnand (x) || x + x == x; -} - -#endif - -#if NEED_PRINTF_INFINITE_LONG_DOUBLE && !NEED_PRINTF_LONG_DOUBLE - -/* Equivalent to !isfinite(x) || x == 0, but does not require libm. */ -static int -is_infinite_or_zerol (long double x) -{ - return isnanl (x) || x + x == x; -} - -#endif - -#if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE - -/* Converting 'long double' to decimal without rare rounding bugs requires - real bignums. We use the naming conventions of GNU gmp, but vastly simpler - (and slower) algorithms. */ - -typedef unsigned int mp_limb_t; -# define GMP_LIMB_BITS 32 -static_assert (sizeof (mp_limb_t) * CHAR_BIT == GMP_LIMB_BITS); - -typedef unsigned long long mp_twolimb_t; -# define GMP_TWOLIMB_BITS 64 -static_assert (sizeof (mp_twolimb_t) * CHAR_BIT == GMP_TWOLIMB_BITS); - -/* Representation of a bignum >= 0. */ -typedef struct -{ - size_t nlimbs; - mp_limb_t *limbs; /* Bits in little-endian order, allocated with malloc(). */ -} mpn_t; - -/* Compute the product of two bignums >= 0. - Return the allocated memory in case of success, NULL in case of memory - allocation failure. */ -static void * -multiply (mpn_t src1, mpn_t src2, mpn_t *dest) -{ - const mp_limb_t *p1; - const mp_limb_t *p2; - size_t len1; - size_t len2; - - if (src1.nlimbs <= src2.nlimbs) - { - len1 = src1.nlimbs; - p1 = src1.limbs; - len2 = src2.nlimbs; - p2 = src2.limbs; - } - else - { - len1 = src2.nlimbs; - p1 = src2.limbs; - len2 = src1.nlimbs; - p2 = src1.limbs; - } - /* Now 0 <= len1 <= len2. */ - if (len1 == 0) - { - /* src1 or src2 is zero. */ - dest->nlimbs = 0; - dest->limbs = (mp_limb_t *) malloc (1); - } - else - { - /* Here 1 <= len1 <= len2. */ - size_t dlen; - mp_limb_t *dp; - size_t k, i, j; - - dlen = len1 + len2; - dp = (mp_limb_t *) malloc (dlen * sizeof (mp_limb_t)); - if (dp == NULL) - return NULL; - for (k = len2; k > 0; ) - dp[--k] = 0; - for (i = 0; i < len1; i++) - { - mp_limb_t digit1 = p1[i]; - mp_twolimb_t carry = 0; - for (j = 0; j < len2; j++) - { - mp_limb_t digit2 = p2[j]; - carry += (mp_twolimb_t) digit1 * (mp_twolimb_t) digit2; - carry += dp[i + j]; - dp[i + j] = (mp_limb_t) carry; - carry = carry >> GMP_LIMB_BITS; - } - dp[i + len2] = (mp_limb_t) carry; - } - /* Normalise. */ - while (dlen > 0 && dp[dlen - 1] == 0) - dlen--; - dest->nlimbs = dlen; - dest->limbs = dp; - } - return dest->limbs; -} - -/* Compute the quotient of a bignum a >= 0 and a bignum b > 0. - a is written as a = q * b + r with 0 <= r < b. q is the quotient, r - the remainder. - Finally, round-to-even is performed: If r > b/2 or if r = b/2 and q is odd, - q is incremented. - Return the allocated memory in case of success, NULL in case of memory - allocation failure. */ -static void * -divide (mpn_t a, mpn_t b, mpn_t *q) -{ - /* Algorithm: - First normalise a and b: a=[a[m-1],...,a[0]], b=[b[n-1],...,b[0]] - with m>=0 and n>0 (in base beta = 2^GMP_LIMB_BITS). - If m=n=1, perform a single-precision division: - r:=0, j:=m, - while j>0 do - {Here (q[m-1]*beta^(m-1)+...+q[j]*beta^j) * b[0] + r*beta^j = - = a[m-1]*beta^(m-1)+...+a[j]*beta^j und 0<=r=n>1, perform a multiple-precision division: - We have a/b < beta^(m-n+1). - s:=intDsize-1-(highest bit in b[n-1]), 0<=s=beta/2. - For j=m-n,...,0: {Here 0 <= r < b*beta^(j+1).} - Compute q* : - q* := floor((r[j+n]*beta+r[j+n-1])/b[n-1]). - In case of overflow (q* >= beta) set q* := beta-1. - Compute c2 := ((r[j+n]*beta+r[j+n-1]) - q* * b[n-1])*beta + r[j+n-2] - and c3 := b[n-2] * q*. - {We have 0 <= c2 < 2*beta^2, even 0 <= c2 < beta^2 if no overflow - occurred. Furthermore 0 <= c3 < beta^2. - If there was overflow and - r[j+n]*beta+r[j+n-1] - q* * b[n-1] >= beta, i.e. c2 >= beta^2, - the next test can be skipped.} - While c3 > c2, {Here 0 <= c2 < c3 < beta^2} - Put q* := q* - 1, c2 := c2 + b[n-1]*beta, c3 := c3 - b[n-2]. - If q* > 0: - Put r := r - b * q* * beta^j. In detail: - [r[n+j],...,r[j]] := [r[n+j],...,r[j]] - q* * [b[n-1],...,b[0]]. - hence: u:=0, for i:=0 to n-1 do - u := u + q* * b[i], - r[j+i]:=r[j+i]-(u mod beta) (+ beta, if carry), - u:=u div beta (+ 1, if carry in subtraction) - r[n+j]:=r[n+j]-u. - {Since always u = (q* * [b[i-1],...,b[0]] div beta^i) + 1 - < q* + 1 <= beta, - the carry u does not overflow.} - If a negative carry occurs, put q* := q* - 1 - and [r[n+j],...,r[j]] := [r[n+j],...,r[j]] + [0,b[n-1],...,b[0]]. - Set q[j] := q*. - Normalise [q[m-n],..,q[0]]; this yields the quotient q. - Shift [r[n-1],...,r[0]] right by s bits and normalise; this yields the - rest r. - The room for q[j] can be allocated at the memory location of r[n+j]. - Finally, round-to-even: - Shift r left by 1 bit. - If r > b or if r = b and q[0] is odd, q := q+1. - */ - const mp_limb_t *a_ptr = a.limbs; - size_t a_len = a.nlimbs; - const mp_limb_t *b_ptr = b.limbs; - size_t b_len = b.nlimbs; - mp_limb_t *roomptr; - mp_limb_t *tmp_roomptr = NULL; - mp_limb_t *q_ptr; - size_t q_len; - mp_limb_t *r_ptr; - size_t r_len; - - /* Allocate room for a_len+2 digits. - (Need a_len+1 digits for the real division and 1 more digit for the - final rounding of q.) */ - roomptr = (mp_limb_t *) malloc ((a_len + 2) * sizeof (mp_limb_t)); - if (roomptr == NULL) - return NULL; - - /* Normalise a. */ - while (a_len > 0 && a_ptr[a_len - 1] == 0) - a_len--; - - /* Normalise b. */ - for (;;) - { - if (b_len == 0) - /* Division by zero. */ - abort (); - if (b_ptr[b_len - 1] == 0) - b_len--; - else - break; - } - - /* Here m = a_len >= 0 and n = b_len > 0. */ - - if (a_len < b_len) - { - /* m beta^(m-2) <= a/b < beta^m */ - r_ptr = roomptr; - q_ptr = roomptr + 1; - { - mp_limb_t den = b_ptr[0]; - mp_limb_t remainder = 0; - const mp_limb_t *sourceptr = a_ptr + a_len; - mp_limb_t *destptr = q_ptr + a_len; - size_t count; - for (count = a_len; count > 0; count--) - { - mp_twolimb_t num = - ((mp_twolimb_t) remainder << GMP_LIMB_BITS) | *--sourceptr; - *--destptr = num / den; - remainder = num % den; - } - /* Normalise and store r. */ - if (remainder > 0) - { - r_ptr[0] = remainder; - r_len = 1; - } - else - r_len = 0; - /* Normalise q. */ - q_len = a_len; - if (q_ptr[q_len - 1] == 0) - q_len--; - } - } - else - { - /* n>1: multiple precision division. - beta^(m-1) <= a < beta^m, beta^(n-1) <= b < beta^n ==> - beta^(m-n-1) <= a/b < beta^(m-n+1). */ - /* Determine s. */ - size_t s; - { - mp_limb_t msd = b_ptr[b_len - 1]; /* = b[n-1], > 0 */ - /* Determine s = GMP_LIMB_BITS - integer_length (msd). - Code copied from gnulib's integer_length.c. */ -# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) \ - || (__clang_major__ >= 4) - s = __builtin_clz (msd); -# else -# if defined DBL_EXPBIT0_WORD && defined DBL_EXPBIT0_BIT - if (GMP_LIMB_BITS <= DBL_MANT_BIT) - { - /* Use 'double' operations. - Assumes an IEEE 754 'double' implementation. */ -# define DBL_EXP_MASK ((DBL_MAX_EXP - DBL_MIN_EXP) | 7) -# define DBL_EXP_BIAS (DBL_EXP_MASK / 2 - 1) -# define NWORDS \ - ((sizeof (double) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) - union { double value; unsigned int word[NWORDS]; } m; - - /* Use a single integer to floating-point conversion. */ - m.value = msd; - - s = GMP_LIMB_BITS - - (((m.word[DBL_EXPBIT0_WORD] >> DBL_EXPBIT0_BIT) & DBL_EXP_MASK) - - DBL_EXP_BIAS); - } - else -# undef NWORDS -# endif - { - s = 31; - if (msd >= 0x10000) - { - msd = msd >> 16; - s -= 16; - } - if (msd >= 0x100) - { - msd = msd >> 8; - s -= 8; - } - if (msd >= 0x10) - { - msd = msd >> 4; - s -= 4; - } - if (msd >= 0x4) - { - msd = msd >> 2; - s -= 2; - } - if (msd >= 0x2) - { - msd = msd >> 1; - s -= 1; - } - } -# endif - } - /* 0 <= s < GMP_LIMB_BITS. - Copy b, shifting it left by s bits. */ - if (s > 0) - { - tmp_roomptr = (mp_limb_t *) malloc (b_len * sizeof (mp_limb_t)); - if (tmp_roomptr == NULL) - { - free (roomptr); - return NULL; - } - { - const mp_limb_t *sourceptr = b_ptr; - mp_limb_t *destptr = tmp_roomptr; - mp_twolimb_t accu = 0; - size_t count; - for (count = b_len; count > 0; count--) - { - accu += (mp_twolimb_t) *sourceptr++ << s; - *destptr++ = (mp_limb_t) accu; - accu = accu >> GMP_LIMB_BITS; - } - /* accu must be zero, since that was how s was determined. */ - if (accu != 0) - abort (); - } - b_ptr = tmp_roomptr; - } - /* Copy a, shifting it left by s bits, yields r. - Memory layout: - At the beginning: r = roomptr[0..a_len], - at the end: r = roomptr[0..b_len-1], q = roomptr[b_len..a_len] */ - r_ptr = roomptr; - if (s == 0) - { - memcpy (r_ptr, a_ptr, a_len * sizeof (mp_limb_t)); - r_ptr[a_len] = 0; - } - else - { - const mp_limb_t *sourceptr = a_ptr; - mp_limb_t *destptr = r_ptr; - mp_twolimb_t accu = 0; - size_t count; - for (count = a_len; count > 0; count--) - { - accu += (mp_twolimb_t) *sourceptr++ << s; - *destptr++ = (mp_limb_t) accu; - accu = accu >> GMP_LIMB_BITS; - } - *destptr++ = (mp_limb_t) accu; - } - q_ptr = roomptr + b_len; - q_len = a_len - b_len + 1; /* q will have m-n+1 limbs */ - { - size_t j = a_len - b_len; /* m-n */ - mp_limb_t b_msd = b_ptr[b_len - 1]; /* b[n-1] */ - mp_limb_t b_2msd = b_ptr[b_len - 2]; /* b[n-2] */ - mp_twolimb_t b_msdd = /* b[n-1]*beta+b[n-2] */ - ((mp_twolimb_t) b_msd << GMP_LIMB_BITS) | b_2msd; - /* Division loop, traversed m-n+1 times. - j counts down, b is unchanged, beta/2 <= b[n-1] < beta. */ - for (;;) - { - mp_limb_t q_star; - mp_limb_t c1; - if (r_ptr[j + b_len] < b_msd) /* r[j+n] < b[n-1] ? */ - { - /* Divide r[j+n]*beta+r[j+n-1] by b[n-1], no overflow. */ - mp_twolimb_t num = - ((mp_twolimb_t) r_ptr[j + b_len] << GMP_LIMB_BITS) - | r_ptr[j + b_len - 1]; - q_star = num / b_msd; - c1 = num % b_msd; - } - else - { - /* Overflow, hence r[j+n]*beta+r[j+n-1] >= beta*b[n-1]. */ - q_star = (mp_limb_t)~(mp_limb_t)0; /* q* = beta-1 */ - /* Test whether r[j+n]*beta+r[j+n-1] - (beta-1)*b[n-1] >= beta - <==> r[j+n]*beta+r[j+n-1] + b[n-1] >= beta*b[n-1]+beta - <==> b[n-1] < floor((r[j+n]*beta+r[j+n-1]+b[n-1])/beta) - {<= beta !}. - If yes, jump directly to the subtraction loop. - (Otherwise, r[j+n]*beta+r[j+n-1] - (beta-1)*b[n-1] < beta - <==> floor((r[j+n]*beta+r[j+n-1]+b[n-1])/beta) = b[n-1] ) */ - if (r_ptr[j + b_len] > b_msd - || (c1 = r_ptr[j + b_len - 1] + b_msd) < b_msd) - /* r[j+n] >= b[n-1]+1 or - r[j+n] = b[n-1] and the addition r[j+n-1]+b[n-1] gives a - carry. */ - goto subtract; - } - /* q_star = q*, - c1 = (r[j+n]*beta+r[j+n-1]) - q* * b[n-1] (>=0, 0, decrease it by - b[n-1]*beta+b[n-2]. Because of b[n-1]*beta+b[n-2] >= beta^2/2 - this can happen only twice. */ - if (c3 > c2) - { - q_star = q_star - 1; /* q* := q* - 1 */ - if (c3 - c2 > b_msdd) - q_star = q_star - 1; /* q* := q* - 1 */ - } - } - if (q_star > 0) - subtract: - { - /* Subtract r := r - b * q* * beta^j. */ - mp_limb_t cr; - { - const mp_limb_t *sourceptr = b_ptr; - mp_limb_t *destptr = r_ptr + j; - mp_twolimb_t carry = 0; - size_t count; - for (count = b_len; count > 0; count--) - { - /* Here 0 <= carry <= q*. */ - carry = - carry - + (mp_twolimb_t) q_star * (mp_twolimb_t) *sourceptr++ - + (mp_limb_t) ~(*destptr); - /* Here 0 <= carry <= beta*q* + beta-1. */ - *destptr++ = ~(mp_limb_t) carry; - carry = carry >> GMP_LIMB_BITS; /* <= q* */ - } - cr = (mp_limb_t) carry; - } - /* Subtract cr from r_ptr[j + b_len], then forget about - r_ptr[j + b_len]. */ - if (cr > r_ptr[j + b_len]) - { - /* Subtraction gave a carry. */ - q_star = q_star - 1; /* q* := q* - 1 */ - /* Add b back. */ - { - const mp_limb_t *sourceptr = b_ptr; - mp_limb_t *destptr = r_ptr + j; - mp_limb_t carry = 0; - size_t count; - for (count = b_len; count > 0; count--) - { - mp_limb_t source1 = *sourceptr++; - mp_limb_t source2 = *destptr; - *destptr++ = source1 + source2 + carry; - carry = - (carry - ? source1 >= (mp_limb_t) ~source2 - : source1 > (mp_limb_t) ~source2); - } - } - /* Forget about the carry and about r[j+n]. */ - } - } - /* q* is determined. Store it as q[j]. */ - q_ptr[j] = q_star; - if (j == 0) - break; - j--; - } - } - r_len = b_len; - /* Normalise q. */ - if (q_ptr[q_len - 1] == 0) - q_len--; -# if 0 /* Not needed here, since we need r only to compare it with b/2, and - b is shifted left by s bits. */ - /* Shift r right by s bits. */ - if (s > 0) - { - mp_limb_t ptr = r_ptr + r_len; - mp_twolimb_t accu = 0; - size_t count; - for (count = r_len; count > 0; count--) - { - accu = (mp_twolimb_t) (mp_limb_t) accu << GMP_LIMB_BITS; - accu += (mp_twolimb_t) *--ptr << (GMP_LIMB_BITS - s); - *ptr = (mp_limb_t) (accu >> GMP_LIMB_BITS); - } - } -# endif - /* Normalise r. */ - while (r_len > 0 && r_ptr[r_len - 1] == 0) - r_len--; - } - /* Compare r << 1 with b. */ - if (r_len > b_len) - goto increment_q; - { - size_t i; - for (i = b_len;;) - { - mp_limb_t r_i = - (i <= r_len && i > 0 ? r_ptr[i - 1] >> (GMP_LIMB_BITS - 1) : 0) - | (i < r_len ? r_ptr[i] << 1 : 0); - mp_limb_t b_i = (i < b_len ? b_ptr[i] : 0); - if (r_i > b_i) - goto increment_q; - if (r_i < b_i) - goto keep_q; - if (i == 0) - break; - i--; - } - } - if (q_len > 0 && ((q_ptr[0] & 1) != 0)) - /* q is odd. */ - increment_q: - { - size_t i; - for (i = 0; i < q_len; i++) - if (++(q_ptr[i]) != 0) - goto keep_q; - q_ptr[q_len++] = 1; - } - keep_q: - free (tmp_roomptr); - q->limbs = q_ptr; - q->nlimbs = q_len; - return roomptr; -} - -/* Avoid pointless GCC warning "argument 1 value '18446744073709551615' exceeds - maximum object size 9223372036854775807", triggered by the use of xsum as - argument of malloc. */ -# if __GNUC__ >= 7 -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Walloc-size-larger-than=" -# endif - -/* Convert a bignum a >= 0, multiplied with 10^extra_zeroes, to decimal - representation. - Destroys the contents of a. - Return the allocated memory - containing the decimal digits in low-to-high - order, terminated with a NUL character - in case of success, NULL in case - of memory allocation failure. */ -static char * -convert_to_decimal (mpn_t a, size_t extra_zeroes) -{ - mp_limb_t *a_ptr = a.limbs; - size_t a_len = a.nlimbs; - /* 0.03345 is slightly larger than log(2)/(9*log(10)). */ - size_t c_len = 9 * ((size_t)(a_len * (GMP_LIMB_BITS * 0.03345f)) + 1); - /* We need extra_zeroes bytes for zeroes, followed by c_len bytes for the - digits of a, followed by 1 byte for the terminating NUL. */ - char *c_ptr = (char *) malloc (xsum (xsum (extra_zeroes, c_len), 1)); - if (c_ptr != NULL) - { - char *d_ptr = c_ptr; - for (; extra_zeroes > 0; extra_zeroes--) - *d_ptr++ = '0'; - while (a_len > 0) - { - /* Divide a by 10^9, in-place. */ - mp_limb_t remainder = 0; - mp_limb_t *ptr = a_ptr + a_len; - size_t count; - for (count = a_len; count > 0; count--) - { - mp_twolimb_t num = - ((mp_twolimb_t) remainder << GMP_LIMB_BITS) | *--ptr; - *ptr = num / 1000000000; - remainder = num % 1000000000; - } - /* Store the remainder as 9 decimal digits. */ - for (count = 9; count > 0; count--) - { - *d_ptr++ = '0' + (remainder % 10); - remainder = remainder / 10; - } - /* Normalize a. */ - if (a_ptr[a_len - 1] == 0) - a_len--; - } - /* Remove leading zeroes. */ - while (d_ptr > c_ptr && d_ptr[-1] == '0') - d_ptr--; - /* But keep at least one zero. */ - if (d_ptr == c_ptr) - *d_ptr++ = '0'; - /* Terminate the string. */ - *d_ptr = '\0'; - } - return c_ptr; -} - -# if __GNUC__ >= 7 -# pragma GCC diagnostic pop -# endif - -# if NEED_PRINTF_LONG_DOUBLE - -/* Assuming x is finite and >= 0: - write x as x = 2^e * m, where m is a bignum. - Return the allocated memory in case of success, NULL in case of memory - allocation failure. */ -static void * -decode_long_double (long double x, int *ep, mpn_t *mp) -{ - mpn_t m; - int exp; - long double y; - size_t i; - - /* Allocate memory for result. */ - m.nlimbs = (LDBL_MANT_BIT + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; - m.limbs = (mp_limb_t *) malloc (m.nlimbs * sizeof (mp_limb_t)); - if (m.limbs == NULL) - return NULL; - /* Split into exponential part and mantissa. */ - y = frexpl (x, &exp); - if (!(y >= 0.0L && y < 1.0L)) - abort (); - /* x = 2^exp * y = 2^(exp - LDBL_MANT_BIT) * (y * 2^LDBL_MANT_BIT), and the - latter is an integer. */ - /* Convert the mantissa (y * 2^LDBL_MANT_BIT) to a sequence of limbs. - I'm not sure whether it's safe to cast a 'long double' value between - 2^31 and 2^32 to 'unsigned int', therefore play safe and cast only - 'long double' values between 0 and 2^16 (to 'unsigned int' or 'int', - doesn't matter). */ -# if (LDBL_MANT_BIT % GMP_LIMB_BITS) != 0 -# if (LDBL_MANT_BIT % GMP_LIMB_BITS) > GMP_LIMB_BITS / 2 - { - mp_limb_t hi, lo; - y *= (mp_limb_t) 1 << (LDBL_MANT_BIT % (GMP_LIMB_BITS / 2)); - hi = (int) y; - y -= hi; - if (!(y >= 0.0L && y < 1.0L)) - abort (); - y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); - lo = (int) y; - y -= lo; - if (!(y >= 0.0L && y < 1.0L)) - abort (); - m.limbs[LDBL_MANT_BIT / GMP_LIMB_BITS] = (hi << (GMP_LIMB_BITS / 2)) | lo; - } -# else - { - mp_limb_t d; - y *= (mp_limb_t) 1 << (LDBL_MANT_BIT % GMP_LIMB_BITS); - d = (int) y; - y -= d; - if (!(y >= 0.0L && y < 1.0L)) - abort (); - m.limbs[LDBL_MANT_BIT / GMP_LIMB_BITS] = d; - } -# endif -# endif - for (i = LDBL_MANT_BIT / GMP_LIMB_BITS; i > 0; ) - { - mp_limb_t hi, lo; - y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); - hi = (int) y; - y -= hi; - if (!(y >= 0.0L && y < 1.0L)) - abort (); - y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); - lo = (int) y; - y -= lo; - if (!(y >= 0.0L && y < 1.0L)) - abort (); - m.limbs[--i] = (hi << (GMP_LIMB_BITS / 2)) | lo; - } -# if 0 /* On FreeBSD 6.1/x86, 'long double' numbers sometimes have excess - precision. */ - if (!(y == 0.0L)) - abort (); -# endif - /* Normalise. */ - while (m.nlimbs > 0 && m.limbs[m.nlimbs - 1] == 0) - m.nlimbs--; - *mp = m; - *ep = exp - LDBL_MANT_BIT; - return m.limbs; -} - -# endif - -# if NEED_PRINTF_DOUBLE - -/* Assuming x is finite and >= 0: - write x as x = 2^e * m, where m is a bignum. - Return the allocated memory in case of success, NULL in case of memory - allocation failure. */ -static void * -decode_double (double x, int *ep, mpn_t *mp) -{ - mpn_t m; - int exp; - double y; - size_t i; - - /* Allocate memory for result. */ - m.nlimbs = (DBL_MANT_BIT + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS; - m.limbs = (mp_limb_t *) malloc (m.nlimbs * sizeof (mp_limb_t)); - if (m.limbs == NULL) - return NULL; - /* Split into exponential part and mantissa. */ - y = frexp (x, &exp); - if (!(y >= 0.0 && y < 1.0)) - abort (); - /* x = 2^exp * y = 2^(exp - DBL_MANT_BIT) * (y * 2^DBL_MANT_BIT), and the - latter is an integer. */ - /* Convert the mantissa (y * 2^DBL_MANT_BIT) to a sequence of limbs. - I'm not sure whether it's safe to cast a 'double' value between - 2^31 and 2^32 to 'unsigned int', therefore play safe and cast only - 'double' values between 0 and 2^16 (to 'unsigned int' or 'int', - doesn't matter). */ -# if (DBL_MANT_BIT % GMP_LIMB_BITS) != 0 -# if (DBL_MANT_BIT % GMP_LIMB_BITS) > GMP_LIMB_BITS / 2 - { - mp_limb_t hi, lo; - y *= (mp_limb_t) 1 << (DBL_MANT_BIT % (GMP_LIMB_BITS / 2)); - hi = (int) y; - y -= hi; - if (!(y >= 0.0 && y < 1.0)) - abort (); - y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); - lo = (int) y; - y -= lo; - if (!(y >= 0.0 && y < 1.0)) - abort (); - m.limbs[DBL_MANT_BIT / GMP_LIMB_BITS] = (hi << (GMP_LIMB_BITS / 2)) | lo; - } -# else - { - mp_limb_t d; - y *= (mp_limb_t) 1 << (DBL_MANT_BIT % GMP_LIMB_BITS); - d = (int) y; - y -= d; - if (!(y >= 0.0 && y < 1.0)) - abort (); - m.limbs[DBL_MANT_BIT / GMP_LIMB_BITS] = d; - } -# endif -# endif - for (i = DBL_MANT_BIT / GMP_LIMB_BITS; i > 0; ) - { - mp_limb_t hi, lo; - y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); - hi = (int) y; - y -= hi; - if (!(y >= 0.0 && y < 1.0)) - abort (); - y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2); - lo = (int) y; - y -= lo; - if (!(y >= 0.0 && y < 1.0)) - abort (); - m.limbs[--i] = (hi << (GMP_LIMB_BITS / 2)) | lo; - } - if (!(y == 0.0)) - abort (); - /* Normalise. */ - while (m.nlimbs > 0 && m.limbs[m.nlimbs - 1] == 0) - m.nlimbs--; - *mp = m; - *ep = exp - DBL_MANT_BIT; - return m.limbs; -} - -# endif - -/* Assuming x = 2^e * m is finite and >= 0, and n is an integer: - Returns the decimal representation of round (x * 10^n). - Return the allocated memory - containing the decimal digits in low-to-high - order, terminated with a NUL character - in case of success, NULL in case - of memory allocation failure. */ -static char * -scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) -{ - int s; - size_t extra_zeroes; - unsigned int abs_n; - unsigned int abs_s; - mp_limb_t *pow5_ptr; - size_t pow5_len; - unsigned int s_limbs; - unsigned int s_bits; - mpn_t pow5; - mpn_t z; - void *z_memory; - char *digits; - - /* x = 2^e * m, hence - y = round (2^e * 10^n * m) = round (2^(e+n) * 5^n * m) - = round (2^s * 5^n * m). */ - s = e + n; - extra_zeroes = 0; - /* Factor out a common power of 10 if possible. */ - if (s > 0 && n > 0) - { - extra_zeroes = (s < n ? s : n); - s -= extra_zeroes; - n -= extra_zeroes; - } - /* Here y = round (2^s * 5^n * m) * 10^extra_zeroes. - Before converting to decimal, we need to compute - z = round (2^s * 5^n * m). */ - /* Compute 5^|n|, possibly shifted by |s| bits if n and s have the same - sign. 2.322 is slightly larger than log(5)/log(2). */ - abs_n = (n >= 0 ? n : -n); - abs_s = (s >= 0 ? s : -s); - pow5_ptr = (mp_limb_t *) malloc (((int)(abs_n * (2.322f / GMP_LIMB_BITS)) + 1 - + abs_s / GMP_LIMB_BITS + 1) - * sizeof (mp_limb_t)); - if (pow5_ptr == NULL) - { - free (memory); - return NULL; - } - /* Initialize with 1. */ - pow5_ptr[0] = 1; - pow5_len = 1; - /* Multiply with 5^|n|. */ - if (abs_n > 0) - { - static mp_limb_t const small_pow5[13 + 1] = - { - 1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625, - 48828125, 244140625, 1220703125 - }; - unsigned int n13; - for (n13 = 0; n13 <= abs_n; n13 += 13) - { - mp_limb_t digit1 = small_pow5[n13 + 13 <= abs_n ? 13 : abs_n - n13]; - size_t j; - mp_twolimb_t carry = 0; - for (j = 0; j < pow5_len; j++) - { - mp_limb_t digit2 = pow5_ptr[j]; - carry += (mp_twolimb_t) digit1 * (mp_twolimb_t) digit2; - pow5_ptr[j] = (mp_limb_t) carry; - carry = carry >> GMP_LIMB_BITS; - } - if (carry > 0) - pow5_ptr[pow5_len++] = (mp_limb_t) carry; - } - } - s_limbs = abs_s / GMP_LIMB_BITS; - s_bits = abs_s % GMP_LIMB_BITS; - if (n >= 0 ? s >= 0 : s <= 0) - { - /* Multiply with 2^|s|. */ - if (s_bits > 0) - { - mp_limb_t *ptr = pow5_ptr; - mp_twolimb_t accu = 0; - size_t count; - for (count = pow5_len; count > 0; count--) - { - accu += (mp_twolimb_t) *ptr << s_bits; - *ptr++ = (mp_limb_t) accu; - accu = accu >> GMP_LIMB_BITS; - } - if (accu > 0) - { - *ptr = (mp_limb_t) accu; - pow5_len++; - } - } - if (s_limbs > 0) - { - size_t count; - for (count = pow5_len; count > 0;) - { - count--; - pow5_ptr[s_limbs + count] = pow5_ptr[count]; - } - for (count = s_limbs; count > 0;) - { - count--; - pow5_ptr[count] = 0; - } - pow5_len += s_limbs; - } - pow5.limbs = pow5_ptr; - pow5.nlimbs = pow5_len; - if (n >= 0) - { - /* Multiply m with pow5. No division needed. */ - z_memory = multiply (m, pow5, &z); - } - else - { - /* Divide m by pow5 and round. */ - z_memory = divide (m, pow5, &z); - } - } - else - { - pow5.limbs = pow5_ptr; - pow5.nlimbs = pow5_len; - if (n >= 0) - { - /* n >= 0, s < 0. - Multiply m with pow5, then divide by 2^|s|. */ - mpn_t numerator; - mpn_t denominator; - void *tmp_memory; - tmp_memory = multiply (m, pow5, &numerator); - if (tmp_memory == NULL) - { - free (pow5_ptr); - free (memory); - return NULL; - } - /* Construct 2^|s|. */ - { - mp_limb_t *ptr = pow5_ptr + pow5_len; - size_t i; - for (i = 0; i < s_limbs; i++) - ptr[i] = 0; - ptr[s_limbs] = (mp_limb_t) 1 << s_bits; - denominator.limbs = ptr; - denominator.nlimbs = s_limbs + 1; - } - z_memory = divide (numerator, denominator, &z); - free (tmp_memory); - } - else - { - /* n < 0, s > 0. - Multiply m with 2^s, then divide by pow5. */ - mpn_t numerator; - mp_limb_t *num_ptr; - num_ptr = (mp_limb_t *) malloc ((m.nlimbs + s_limbs + 1) - * sizeof (mp_limb_t)); - if (num_ptr == NULL) - { - free (pow5_ptr); - free (memory); - return NULL; - } - { - mp_limb_t *destptr = num_ptr; - { - size_t i; - for (i = 0; i < s_limbs; i++) - *destptr++ = 0; - } - if (s_bits > 0) - { - const mp_limb_t *sourceptr = m.limbs; - mp_twolimb_t accu = 0; - size_t count; - for (count = m.nlimbs; count > 0; count--) - { - accu += (mp_twolimb_t) *sourceptr++ << s_bits; - *destptr++ = (mp_limb_t) accu; - accu = accu >> GMP_LIMB_BITS; - } - if (accu > 0) - *destptr++ = (mp_limb_t) accu; - } - else - { - const mp_limb_t *sourceptr = m.limbs; - size_t count; - for (count = m.nlimbs; count > 0; count--) - *destptr++ = *sourceptr++; - } - numerator.limbs = num_ptr; - numerator.nlimbs = destptr - num_ptr; - } - z_memory = divide (numerator, pow5, &z); - free (num_ptr); - } - } - free (pow5_ptr); - free (memory); - - /* Here y = round (x * 10^n) = z * 10^extra_zeroes. */ - - if (z_memory == NULL) - return NULL; - digits = convert_to_decimal (z, extra_zeroes); - free (z_memory); - return digits; -} - -# if NEED_PRINTF_LONG_DOUBLE - -/* Assuming x is finite and >= 0, and n is an integer: - Returns the decimal representation of round (x * 10^n). - Return the allocated memory - containing the decimal digits in low-to-high - order, terminated with a NUL character - in case of success, NULL in case - of memory allocation failure. */ -static char * -scale10_round_decimal_long_double (long double x, int n) -{ - int e; - mpn_t m; - void *memory = decode_long_double (x, &e, &m); - if (memory != NULL) - return scale10_round_decimal_decoded (e, m, memory, n); - else - return NULL; -} - -# endif - -# if NEED_PRINTF_DOUBLE - -/* Assuming x is finite and >= 0, and n is an integer: - Returns the decimal representation of round (x * 10^n). - Return the allocated memory - containing the decimal digits in low-to-high - order, terminated with a NUL character - in case of success, NULL in case - of memory allocation failure. */ -static char * -scale10_round_decimal_double (double x, int n) -{ - int e; - mpn_t m; - void *memory = decode_double (x, &e, &m); - if (memory != NULL) - return scale10_round_decimal_decoded (e, m, memory, n); - else - return NULL; -} - -# endif - -# if NEED_PRINTF_LONG_DOUBLE - -/* Assuming x is finite and > 0: - Return an approximation for n with 10^n <= x < 10^(n+1). - The approximation is usually the right n, but may be off by 1 sometimes. */ -static int -floorlog10l (long double x) -{ - int exp; - long double y; - double z; - double l; - - /* Split into exponential part and mantissa. */ - y = frexpl (x, &exp); - if (!(y >= 0.0L && y < 1.0L)) - abort (); - if (y == 0.0L) - return INT_MIN; - if (y < 0.5L) - { - while (y < (1.0L / (1 << (GMP_LIMB_BITS / 2)) / (1 << (GMP_LIMB_BITS / 2)))) - { - y *= 1.0L * (1 << (GMP_LIMB_BITS / 2)) * (1 << (GMP_LIMB_BITS / 2)); - exp -= GMP_LIMB_BITS; - } - if (y < (1.0L / (1 << 16))) - { - y *= 1.0L * (1 << 16); - exp -= 16; - } - if (y < (1.0L / (1 << 8))) - { - y *= 1.0L * (1 << 8); - exp -= 8; - } - if (y < (1.0L / (1 << 4))) - { - y *= 1.0L * (1 << 4); - exp -= 4; - } - if (y < (1.0L / (1 << 2))) - { - y *= 1.0L * (1 << 2); - exp -= 2; - } - if (y < (1.0L / (1 << 1))) - { - y *= 1.0L * (1 << 1); - exp -= 1; - } - } - if (!(y >= 0.5L && y < 1.0L)) - abort (); - /* Compute an approximation for l = log2(x) = exp + log2(y). */ - l = exp; - z = y; - if (z < 0.70710678118654752444) - { - z *= 1.4142135623730950488; - l -= 0.5; - } - if (z < 0.8408964152537145431) - { - z *= 1.1892071150027210667; - l -= 0.25; - } - if (z < 0.91700404320467123175) - { - z *= 1.0905077326652576592; - l -= 0.125; - } - if (z < 0.9576032806985736469) - { - z *= 1.0442737824274138403; - l -= 0.0625; - } - /* Now 0.95 <= z <= 1.01. */ - z = 1 - z; - /* log2(1-z) = 1/log(2) * (- z - z^2/2 - z^3/3 - z^4/4 - ...) - Four terms are enough to get an approximation with error < 10^-7. */ - l -= 1.4426950408889634074 * z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25))); - /* Finally multiply with log(2)/log(10), yields an approximation for - log10(x). */ - l *= 0.30102999566398119523; - /* Round down to the next integer. */ - return (int) l + (l < 0 ? -1 : 0); -} - -# endif - -# if NEED_PRINTF_DOUBLE - -/* Assuming x is finite and > 0: - Return an approximation for n with 10^n <= x < 10^(n+1). - The approximation is usually the right n, but may be off by 1 sometimes. */ -static int -floorlog10 (double x) -{ - int exp; - double y; - double z; - double l; - - /* Split into exponential part and mantissa. */ - y = frexp (x, &exp); - if (!(y >= 0.0 && y < 1.0)) - abort (); - if (y == 0.0) - return INT_MIN; - if (y < 0.5) - { - while (y < (1.0 / (1 << (GMP_LIMB_BITS / 2)) / (1 << (GMP_LIMB_BITS / 2)))) - { - y *= 1.0 * (1 << (GMP_LIMB_BITS / 2)) * (1 << (GMP_LIMB_BITS / 2)); - exp -= GMP_LIMB_BITS; - } - if (y < (1.0 / (1 << 16))) - { - y *= 1.0 * (1 << 16); - exp -= 16; - } - if (y < (1.0 / (1 << 8))) - { - y *= 1.0 * (1 << 8); - exp -= 8; - } - if (y < (1.0 / (1 << 4))) - { - y *= 1.0 * (1 << 4); - exp -= 4; - } - if (y < (1.0 / (1 << 2))) - { - y *= 1.0 * (1 << 2); - exp -= 2; - } - if (y < (1.0 / (1 << 1))) - { - y *= 1.0 * (1 << 1); - exp -= 1; - } - } - if (!(y >= 0.5 && y < 1.0)) - abort (); - /* Compute an approximation for l = log2(x) = exp + log2(y). */ - l = exp; - z = y; - if (z < 0.70710678118654752444) - { - z *= 1.4142135623730950488; - l -= 0.5; - } - if (z < 0.8408964152537145431) - { - z *= 1.1892071150027210667; - l -= 0.25; - } - if (z < 0.91700404320467123175) - { - z *= 1.0905077326652576592; - l -= 0.125; - } - if (z < 0.9576032806985736469) - { - z *= 1.0442737824274138403; - l -= 0.0625; - } - /* Now 0.95 <= z <= 1.01. */ - z = 1 - z; - /* log2(1-z) = 1/log(2) * (- z - z^2/2 - z^3/3 - z^4/4 - ...) - Four terms are enough to get an approximation with error < 10^-7. */ - l -= 1.4426950408889634074 * z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25))); - /* Finally multiply with log(2)/log(10), yields an approximation for - log10(x). */ - l *= 0.30102999566398119523; - /* Round down to the next integer. */ - return (int) l + (l < 0 ? -1 : 0); -} - -# endif - -/* Tests whether a string of digits consists of exactly PRECISION zeroes and - a single '1' digit. */ -static int -is_borderline (const char *digits, size_t precision) -{ - for (; precision > 0; precision--, digits++) - if (*digits != '0') - return 0; - if (*digits != '1') - return 0; - digits++; - return *digits == '\0'; -} - -#endif - -#if !USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF - -/* Use a different function name, to make it possible that the 'wchar_t' - parametrization and the 'char' parametrization get compiled in the same - translation unit. */ -# if WIDE_CHAR_VERSION -# define MAX_ROOM_NEEDED wmax_room_needed -# else -# define MAX_ROOM_NEEDED max_room_needed -# endif - -/* Returns the number of TCHAR_T units needed as temporary space for the result - of sprintf or SNPRINTF of a single conversion directive. */ -static size_t -MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, - arg_type type, int flags, size_t width, int has_precision, - size_t precision, int pad_ourselves) -{ - size_t tmp_length; - - switch (conversion) - { - case 'd': case 'i': case 'u': - switch (type) - { - default: - tmp_length = - (unsigned int) (sizeof (unsigned int) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_LONGINT: - tmp_length = - (unsigned int) (sizeof (long int) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_ULONGINT: - tmp_length = - (unsigned int) (sizeof (unsigned long int) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_LONGLONGINT: - tmp_length = - (unsigned int) (sizeof (long long int) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_ULONGLONGINT: - tmp_length = - (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_INT8_T: - tmp_length = - (unsigned int) (sizeof (int8_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT8_T: - tmp_length = - (unsigned int) (sizeof (uint8_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_INT16_T: - tmp_length = - (unsigned int) (sizeof (int16_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT16_T: - tmp_length = - (unsigned int) (sizeof (uint16_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_INT32_T: - tmp_length = - (unsigned int) (sizeof (int32_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT32_T: - tmp_length = - (unsigned int) (sizeof (uint32_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_INT64_T: - tmp_length = - (unsigned int) (sizeof (int64_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT64_T: - tmp_length = - (unsigned int) (sizeof (uint64_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_INT_FAST8_T: - tmp_length = - (unsigned int) (sizeof (int_fast8_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST8_T: - tmp_length = - (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_INT_FAST16_T: - tmp_length = - (unsigned int) (sizeof (int_fast16_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST16_T: - tmp_length = - (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_INT_FAST32_T: - tmp_length = - (unsigned int) (sizeof (int_fast32_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST32_T: - tmp_length = - (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_INT_FAST64_T: - tmp_length = - (unsigned int) (sizeof (int_fast64_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST64_T: - tmp_length = - (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - break; - } - if (tmp_length < precision) - tmp_length = precision; - /* Multiply by 2, as an estimate for FLAG_GROUP. */ - tmp_length = xsum (tmp_length, tmp_length); - /* Add 1, to account for a leading sign. */ - tmp_length = xsum (tmp_length, 1); - break; - - case 'b': - #if SUPPORT_GNU_PRINTF_DIRECTIVES \ - || (__GLIBC__ + (__GLIBC_MINOR__ >= 35) > 2) - case 'B': - #endif - switch (type) - { - default: - tmp_length = - (unsigned int) (sizeof (unsigned int) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_ULONGINT: - tmp_length = - (unsigned int) (sizeof (unsigned long int) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_ULONGLONGINT: - tmp_length = - (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT8_T: - tmp_length = - (unsigned int) (sizeof (uint8_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT16_T: - tmp_length = - (unsigned int) (sizeof (uint16_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT32_T: - tmp_length = - (unsigned int) (sizeof (uint32_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT64_T: - tmp_length = - (unsigned int) (sizeof (uint64_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST8_T: - tmp_length = - (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST16_T: - tmp_length = - (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST32_T: - tmp_length = - (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST64_T: - tmp_length = - (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - } - if (tmp_length < precision) - tmp_length = precision; - /* Add 2, to account for a prefix from the alternate form. */ - tmp_length = xsum (tmp_length, 2); - break; - - case 'o': - switch (type) - { - default: - tmp_length = - (unsigned int) (sizeof (unsigned int) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_ULONGINT: - tmp_length = - (unsigned int) (sizeof (unsigned long int) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_ULONGLONGINT: - tmp_length = - (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT8_T: - tmp_length = - (unsigned int) (sizeof (uint8_t) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT16_T: - tmp_length = - (unsigned int) (sizeof (uint16_t) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT32_T: - tmp_length = - (unsigned int) (sizeof (uint32_t) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT64_T: - tmp_length = - (unsigned int) (sizeof (uint64_t) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST8_T: - tmp_length = - (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST16_T: - tmp_length = - (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST32_T: - tmp_length = - (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST64_T: - tmp_length = - (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - break; - } - if (tmp_length < precision) - tmp_length = precision; - /* Add 1, to account for a leading sign. */ - tmp_length = xsum (tmp_length, 1); - break; - - case 'x': case 'X': - switch (type) - { - default: - tmp_length = - (unsigned int) (sizeof (unsigned int) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_ULONGINT: - tmp_length = - (unsigned int) (sizeof (unsigned long int) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_ULONGLONGINT: - tmp_length = - (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT8_T: - tmp_length = - (unsigned int) (sizeof (uint8_t) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT16_T: - tmp_length = - (unsigned int) (sizeof (uint16_t) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT32_T: - tmp_length = - (unsigned int) (sizeof (uint32_t) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT64_T: - tmp_length = - (unsigned int) (sizeof (uint64_t) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST8_T: - tmp_length = - (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST16_T: - tmp_length = - (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST32_T: - tmp_length = - (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST64_T: - tmp_length = - (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - break; - } - if (tmp_length < precision) - tmp_length = precision; - /* Add 2, to account for a prefix from the alternate form. */ - tmp_length = xsum (tmp_length, 2); - break; - - case 'f': case 'F': - if (type == TYPE_LONGDOUBLE) - tmp_length = - (unsigned int) (LDBL_MAX_EXP - * 0.30103 /* binary -> decimal */ - * 2 /* estimate for FLAG_GROUP */ - ) - + 1 /* turn floor into ceil */ - + 10; /* sign, decimal point etc. */ - else - tmp_length = - (unsigned int) (DBL_MAX_EXP - * 0.30103 /* binary -> decimal */ - * 2 /* estimate for FLAG_GROUP */ - ) - + 1 /* turn floor into ceil */ - + 10; /* sign, decimal point etc. */ - tmp_length = xsum (tmp_length, precision); - break; - - case 'e': case 'E': case 'g': case 'G': - tmp_length = - 12; /* sign, decimal point, exponent etc. */ - tmp_length = xsum (tmp_length, precision); - break; - - case 'a': case 'A': - if (type == TYPE_LONGDOUBLE) - tmp_length = - (unsigned int) (LDBL_DIG - * 0.831 /* decimal -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - else - tmp_length = - (unsigned int) (DBL_DIG - * 0.831 /* decimal -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - if (tmp_length < precision) - tmp_length = precision; - /* Account for sign, decimal point etc. */ - tmp_length = xsum (tmp_length, 12); - break; - - case 'c': -# if HAVE_WINT_T && !WIDE_CHAR_VERSION - if (type == TYPE_WIDE_CHAR) - { - tmp_length = MB_CUR_MAX; -# if ENABLE_WCHAR_FALLBACK - if (tmp_length < (sizeof (wchar_t) > 2 ? 10 : 6)) - tmp_length = (sizeof (wchar_t) > 2 ? 10 : 6); -# endif - } - else -# endif - tmp_length = 1; - break; - - case 's': -# if HAVE_WCHAR_T - if (type == TYPE_WIDE_STRING) - { -# if WIDE_CHAR_VERSION - /* ISO C says about %ls in fwprintf: - "If the precision is not specified or is greater than the size - of the array, the array shall contain a null wide character." - So if there is a precision, we must not use wcslen. */ - const wchar_t *arg = ap->arg[arg_index].a.a_wide_string; - - if (has_precision) - tmp_length = local_wcsnlen (arg, precision); - else - tmp_length = local_wcslen (arg); -# else - /* ISO C says about %ls in fprintf: - "If a precision is specified, no more than that many bytes are - written (including shift sequences, if any), and the array - shall contain a null wide character if, to equal the multibyte - character sequence length given by the precision, the function - would need to access a wide character one past the end of the - array." - So if there is a precision, we must not use wcslen. */ - /* This case has already been handled separately in VASNPRINTF. */ - abort (); -# endif - } - else -# endif - { -# if WIDE_CHAR_VERSION - /* ISO C says about %s in fwprintf: - "If the precision is not specified or is greater than the size - of the converted array, the converted array shall contain a - null wide character." - So if there is a precision, we must not use strlen. */ - /* This case has already been handled separately in VASNPRINTF. */ - abort (); -# else - /* ISO C says about %s in fprintf: - "If the precision is not specified or greater than the size of - the array, the array shall contain a null character." - So if there is a precision, we must not use strlen. */ - const char *arg = ap->arg[arg_index].a.a_string; - - if (has_precision) - tmp_length = local_strnlen (arg, precision); - else - tmp_length = strlen (arg); -# endif - } - break; - - case 'p': - tmp_length = - (unsigned int) (sizeof (void *) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1 /* turn floor into ceil */ - + 2; /* account for leading 0x */ - break; - - default: - abort (); - } - - if (!pad_ourselves) - { -# if ENABLE_UNISTDIO - /* Padding considers the number of characters, therefore the number of - elements after padding may be - > max (tmp_length, width) - but is certainly - <= tmp_length + width. */ - tmp_length = xsum (tmp_length, width); -# else - /* Padding considers the number of elements, says POSIX. */ - if (tmp_length < width) - tmp_length = width; -# endif - } - - tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ - - return tmp_length; -} - -#endif - -DCHAR_T * -VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp, - const FCHAR_T *format, va_list args) -{ - DIRECTIVES d; - arguments a; - - if (PRINTF_PARSE (format, &d, &a) < 0) - /* errno is already set. */ - return NULL; - - /* Frees the memory allocated by this function. Preserves errno. */ -#define CLEANUP() \ - if (d.dir != d.direct_alloc_dir) \ - free (d.dir); \ - if (a.arg != a.direct_alloc_arg) \ - free (a.arg); - - if (PRINTF_FETCHARGS (args, &a) < 0) - goto fail_1_with_EINVAL; - - { - size_t buf_neededlength; - TCHAR_T *buf; - TCHAR_T *buf_malloced; - const FCHAR_T *cp; - size_t i; - DIRECTIVE *dp; - /* Output string accumulator. */ - DCHAR_T *result; - size_t allocated; - size_t length; - - /* Allocate a small buffer that will hold a directive passed to - sprintf or snprintf. */ - buf_neededlength = - xsum4 (7, d.max_width_length, d.max_precision_length, 6); -#if HAVE_ALLOCA - if (buf_neededlength < 4000 / sizeof (TCHAR_T)) - { - buf = (TCHAR_T *) alloca (buf_neededlength * sizeof (TCHAR_T)); - buf_malloced = NULL; - } - else -#endif - { - size_t buf_memsize = xtimes (buf_neededlength, sizeof (TCHAR_T)); - if (size_overflow_p (buf_memsize)) - goto out_of_memory_1; - buf = (TCHAR_T *) malloc (buf_memsize); - if (buf == NULL) - goto out_of_memory_1; - buf_malloced = buf; - } - - result = resultbuf; - allocated = (resultbuf != NULL ? *lengthp : 0); - length = 0; - /* Invariants: - result is either == resultbuf or malloc-allocated. - If result == NULL, resultbuf is == NULL as well. - If length > 0, then result != NULL. */ - - /* Ensures that allocated >= needed. Aborts through a jump to - out_of_memory if needed is SIZE_MAX or otherwise too big. */ -#define ENSURE_ALLOCATION_ELSE(needed, oom_statement) \ - if ((needed) > allocated) \ - { \ - size_t memory_size; \ - DCHAR_T *memory; \ - \ - allocated = (allocated > 0 ? xtimes (allocated, 2) : 12); \ - if ((needed) > allocated) \ - allocated = (needed); \ - memory_size = xtimes (allocated, sizeof (DCHAR_T)); \ - if (size_overflow_p (memory_size)) \ - oom_statement \ - if (result == resultbuf) \ - memory = (DCHAR_T *) malloc (memory_size); \ - else \ - memory = (DCHAR_T *) realloc (result, memory_size); \ - if (memory == NULL) \ - oom_statement \ - if (result == resultbuf && length > 0) \ - DCHAR_CPY (memory, result, length); \ - result = memory; \ - } -#define ENSURE_ALLOCATION(needed) \ - ENSURE_ALLOCATION_ELSE((needed), goto out_of_memory; ) - - for (cp = format, i = 0, dp = &d.dir[0]; ; cp = dp->dir_end, i++, dp++) - { - if (cp != dp->dir_start) - { - size_t n = dp->dir_start - cp; - size_t augmented_length = xsum (length, n); - - ENSURE_ALLOCATION (augmented_length); - /* This copies a piece of FCHAR_T[] into a DCHAR_T[]. Here we - need that the format string contains only ASCII characters - if FCHAR_T and DCHAR_T are not the same type. */ - if (sizeof (FCHAR_T) == sizeof (DCHAR_T)) - { - DCHAR_CPY (result + length, (const DCHAR_T *) cp, n); - length = augmented_length; - } - else - { - do - result[length++] = *cp++; - while (--n > 0); - } - } - if (i == d.count) - break; - - /* Execute a single directive. */ - if (dp->conversion == '%') - { - size_t augmented_length; - - if (!(dp->arg_index == ARG_NONE)) - abort (); - augmented_length = xsum (length, 1); - ENSURE_ALLOCATION (augmented_length); - result[length] = '%'; - length = augmented_length; - } - else - { - if (!(dp->arg_index != ARG_NONE)) - abort (); - - if (dp->conversion == 'n') - { - switch (a.arg[dp->arg_index].type) - { - case TYPE_COUNT_SCHAR_POINTER: - *a.arg[dp->arg_index].a.a_count_schar_pointer = length; - break; - case TYPE_COUNT_SHORT_POINTER: - *a.arg[dp->arg_index].a.a_count_short_pointer = length; - break; - case TYPE_COUNT_INT_POINTER: - *a.arg[dp->arg_index].a.a_count_int_pointer = length; - break; - case TYPE_COUNT_LONGINT_POINTER: - *a.arg[dp->arg_index].a.a_count_longint_pointer = length; - break; - case TYPE_COUNT_LONGLONGINT_POINTER: - *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length; - break; - case TYPE_COUNT_INT8_T_POINTER: - *a.arg[dp->arg_index].a.a_count_int8_t_pointer = length; - break; - case TYPE_COUNT_INT16_T_POINTER: - *a.arg[dp->arg_index].a.a_count_int16_t_pointer = length; - break; - case TYPE_COUNT_INT32_T_POINTER: - *a.arg[dp->arg_index].a.a_count_int32_t_pointer = length; - break; - case TYPE_COUNT_INT64_T_POINTER: - *a.arg[dp->arg_index].a.a_count_int64_t_pointer = length; - break; - case TYPE_COUNT_INT_FAST8_T_POINTER: - *a.arg[dp->arg_index].a.a_count_int_fast8_t_pointer = length; - break; - case TYPE_COUNT_INT_FAST16_T_POINTER: - *a.arg[dp->arg_index].a.a_count_int_fast16_t_pointer = length; - break; - case TYPE_COUNT_INT_FAST32_T_POINTER: - *a.arg[dp->arg_index].a.a_count_int_fast32_t_pointer = length; - break; - case TYPE_COUNT_INT_FAST64_T_POINTER: - *a.arg[dp->arg_index].a.a_count_int_fast64_t_pointer = length; - break; - default: - abort (); - } - } -#if ENABLE_UNISTDIO - /* The unistdio extensions. */ - else if (dp->conversion == 'U') - { - arg_type type = a.arg[dp->arg_index].type; - int flags = dp->flags; - int has_width; - size_t width; - int has_precision; - size_t precision; - - has_width = 0; - width = 0; - if (dp->width_start != dp->width_end) - { - if (dp->width_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->width_arg_index].a.a_int; - width = arg; - if (arg < 0) - { - /* "A negative field width is taken as a '-' flag - followed by a positive field width." */ - flags |= FLAG_LEFT; - width = -width; - } - } - else - { - const FCHAR_T *digitp = dp->width_start; - - do - width = xsum (xtimes (width, 10), *digitp++ - '0'); - while (digitp != dp->width_end); - } - has_width = 1; - } - - has_precision = 0; - precision = 0; - if (dp->precision_start != dp->precision_end) - { - if (dp->precision_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; - /* "A negative precision is taken as if the precision - were omitted." */ - if (arg >= 0) - { - precision = arg; - has_precision = 1; - } - } - else - { - const FCHAR_T *digitp = dp->precision_start + 1; - - precision = 0; - while (digitp != dp->precision_end) - precision = xsum (xtimes (precision, 10), *digitp++ - '0'); - has_precision = 1; - } - } - - switch (type) - { - case TYPE_U8_STRING: - { - const uint8_t *arg = a.arg[dp->arg_index].a.a_u8_string; - const uint8_t *arg_end; - size_t characters; - - if (has_precision) - { - /* Use only PRECISION characters, from the left. */ - arg_end = arg; - characters = 0; - for (; precision > 0; precision--) - { - int count = u8_strmblen (arg_end); - if (count == 0) - break; - if (count < 0) - goto fail_with_EILSEQ; - arg_end += count; - characters++; - } - } - else if (has_width) - { - /* Use the entire string, and count the number of - characters. */ - arg_end = arg; - characters = 0; - for (;;) - { - int count = u8_strmblen (arg_end); - if (count == 0) - break; - if (count < 0) - goto fail_with_EILSEQ; - arg_end += count; - characters++; - } - } - else - { - /* Use the entire string. */ - arg_end = arg + u8_strlen (arg); - /* The number of characters doesn't matter. */ - characters = 0; - } - - if (characters < width && !(flags & FLAG_LEFT)) - { - size_t n = width - characters; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - -# if DCHAR_IS_UINT8_T - { - size_t n = arg_end - arg; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_CPY (result + length, arg, n); - length += n; - } -# else - { /* Convert. */ - DCHAR_T *converted = result + length; - size_t converted_len = allocated - length; -# if DCHAR_IS_TCHAR - /* Convert from UTF-8 to locale encoding. */ - converted = - u8_conv_to_encoding (locale_charset (), - iconveh_question_mark, - arg, arg_end - arg, NULL, - converted, &converted_len); -# else - /* Convert from UTF-8 to UTF-16/UTF-32. */ - converted = - U8_TO_DCHAR (arg, arg_end - arg, - converted, &converted_len); -# endif - if (converted == NULL) - goto fail_with_errno; - if (converted != result + length) - { - ENSURE_ALLOCATION_ELSE (xsum (length, converted_len), - { free (converted); goto out_of_memory; }); - DCHAR_CPY (result + length, converted, converted_len); - free (converted); - } - length += converted_len; - } -# endif - - if (characters < width && (flags & FLAG_LEFT)) - { - size_t n = width - characters; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - } - break; - - case TYPE_U16_STRING: - { - const uint16_t *arg = a.arg[dp->arg_index].a.a_u16_string; - const uint16_t *arg_end; - size_t characters; - - if (has_precision) - { - /* Use only PRECISION characters, from the left. */ - arg_end = arg; - characters = 0; - for (; precision > 0; precision--) - { - int count = u16_strmblen (arg_end); - if (count == 0) - break; - if (count < 0) - goto fail_with_EILSEQ; - arg_end += count; - characters++; - } - } - else if (has_width) - { - /* Use the entire string, and count the number of - characters. */ - arg_end = arg; - characters = 0; - for (;;) - { - int count = u16_strmblen (arg_end); - if (count == 0) - break; - if (count < 0) - goto fail_with_EILSEQ; - arg_end += count; - characters++; - } - } - else - { - /* Use the entire string. */ - arg_end = arg + u16_strlen (arg); - /* The number of characters doesn't matter. */ - characters = 0; - } - - if (characters < width && !(flags & FLAG_LEFT)) - { - size_t n = width - characters; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - -# if DCHAR_IS_UINT16_T - { - size_t n = arg_end - arg; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_CPY (result + length, arg, n); - length += n; - } -# else - { /* Convert. */ - DCHAR_T *converted = result + length; - size_t converted_len = allocated - length; -# if DCHAR_IS_TCHAR - /* Convert from UTF-16 to locale encoding. */ - converted = - u16_conv_to_encoding (locale_charset (), - iconveh_question_mark, - arg, arg_end - arg, NULL, - converted, &converted_len); -# else - /* Convert from UTF-16 to UTF-8/UTF-32. */ - converted = - U16_TO_DCHAR (arg, arg_end - arg, - converted, &converted_len); -# endif - if (converted == NULL) - goto fail_with_errno; - if (converted != result + length) - { - ENSURE_ALLOCATION_ELSE (xsum (length, converted_len), - { free (converted); goto out_of_memory; }); - DCHAR_CPY (result + length, converted, converted_len); - free (converted); - } - length += converted_len; - } -# endif - - if (characters < width && (flags & FLAG_LEFT)) - { - size_t n = width - characters; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - } - break; - - case TYPE_U32_STRING: - { - const uint32_t *arg = a.arg[dp->arg_index].a.a_u32_string; - const uint32_t *arg_end; - size_t characters; - - if (has_precision) - { - /* Use only PRECISION characters, from the left. */ - arg_end = arg; - characters = 0; - for (; precision > 0; precision--) - { - int count = u32_strmblen (arg_end); - if (count == 0) - break; - if (count < 0) - goto fail_with_EILSEQ; - arg_end += count; - characters++; - } - } - else if (has_width) - { - /* Use the entire string, and count the number of - characters. */ - arg_end = arg; - characters = 0; - for (;;) - { - int count = u32_strmblen (arg_end); - if (count == 0) - break; - if (count < 0) - goto fail_with_EILSEQ; - arg_end += count; - characters++; - } - } - else - { - /* Use the entire string. */ - arg_end = arg + u32_strlen (arg); - /* The number of characters doesn't matter. */ - characters = 0; - } - - if (characters < width && !(flags & FLAG_LEFT)) - { - size_t n = width - characters; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - -# if DCHAR_IS_UINT32_T - { - size_t n = arg_end - arg; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_CPY (result + length, arg, n); - length += n; - } -# else - { /* Convert. */ - DCHAR_T *converted = result + length; - size_t converted_len = allocated - length; -# if DCHAR_IS_TCHAR - /* Convert from UTF-32 to locale encoding. */ - converted = - u32_conv_to_encoding (locale_charset (), - iconveh_question_mark, - arg, arg_end - arg, NULL, - converted, &converted_len); -# else - /* Convert from UTF-32 to UTF-8/UTF-16. */ - converted = - U32_TO_DCHAR (arg, arg_end - arg, - converted, &converted_len); -# endif - if (converted == NULL) - goto fail_with_errno; - if (converted != result + length) - { - ENSURE_ALLOCATION_ELSE (xsum (length, converted_len), - { free (converted); goto out_of_memory; }); - DCHAR_CPY (result + length, converted, converted_len); - free (converted); - } - length += converted_len; - } -# endif - - if (characters < width && (flags & FLAG_LEFT)) - { - size_t n = width - characters; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - } - break; - - default: - abort (); - } - } -#endif -#if WIDE_CHAR_VERSION && (!DCHAR_IS_TCHAR || NEED_WPRINTF_DIRECTIVE_LC) - else if ((dp->conversion == 's' - && a.arg[dp->arg_index].type == TYPE_WIDE_STRING) - || (dp->conversion == 'c' - && a.arg[dp->arg_index].type == TYPE_WIDE_CHAR)) - { - /* %ls or %lc in vasnwprintf. See the specification of - fwprintf. */ - /* It would be silly to use snprintf ("%ls", ...) and then - convert back the result from a char[] to a wchar_t[]. - Instead, just copy the argument wchar_t[] to the result. */ - int flags = dp->flags; - size_t width; - - width = 0; - if (dp->width_start != dp->width_end) - { - if (dp->width_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->width_arg_index].a.a_int; - width = arg; - if (arg < 0) - { - /* "A negative field width is taken as a '-' flag - followed by a positive field width." */ - flags |= FLAG_LEFT; - width = -width; - } - } - else - { - const FCHAR_T *digitp = dp->width_start; - - do - width = xsum (xtimes (width, 10), *digitp++ - '0'); - while (digitp != dp->width_end); - } - } - - { - const wchar_t *ls_arg; - wchar_t lc_arg[1]; - size_t characters; - - if (dp->conversion == 's') - { - int has_precision; - size_t precision; - - has_precision = 0; - precision = 6; - if (dp->precision_start != dp->precision_end) - { - if (dp->precision_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; - /* "A negative precision is taken as if the precision - were omitted." */ - if (arg >= 0) - { - precision = arg; - has_precision = 1; - } - } - else - { - const FCHAR_T *digitp = dp->precision_start + 1; - - precision = 0; - while (digitp != dp->precision_end) - precision = xsum (xtimes (precision, 10), *digitp++ - '0'); - has_precision = 1; - } - } - - ls_arg = a.arg[dp->arg_index].a.a_wide_string; - - if (has_precision) - { - /* Use only at most PRECISION wide characters, from - the left. */ - const wchar_t *ls_arg_end; - - ls_arg_end = ls_arg; - characters = 0; - for (; precision > 0; precision--) - { - if (*ls_arg_end == 0) - /* Found the terminating null wide character. */ - break; - ls_arg_end++; - characters++; - } - } - else - { - /* Use the entire string, and count the number of wide - characters. */ - characters = local_wcslen (ls_arg); - } - } - else /* dp->conversion == 'c' */ - { - lc_arg[0] = (wchar_t) a.arg[dp->arg_index].a.a_wide_char; - ls_arg = lc_arg; - characters = 1; - } - - { - size_t total = (characters < width ? width : characters); - ENSURE_ALLOCATION (xsum (length, total)); - - if (characters < width && !(flags & FLAG_LEFT)) - { - size_t n = width - characters; - DCHAR_SET (result + length, ' ', n); - length += n; - } - - if (characters > 0) - { - DCHAR_CPY (result + length, ls_arg, characters); - length += characters; - } - - if (characters < width && (flags & FLAG_LEFT)) - { - size_t n = width - characters; - DCHAR_SET (result + length, ' ', n); - length += n; - } - } - } - } -#endif -#if (!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T - else if (dp->conversion == 's' -# if WIDE_CHAR_VERSION - && a.arg[dp->arg_index].type != TYPE_WIDE_STRING -# else - && a.arg[dp->arg_index].type == TYPE_WIDE_STRING -# endif - ) - { - /* The normal handling of the 's' directive below requires - allocating a temporary buffer. The determination of its - length (tmp_length), in the case when a precision is - specified, below requires a conversion between a char[] - string and a wchar_t[] wide string. It could be done, but - we have no guarantee that the implementation of sprintf will - use the exactly same algorithm. Without this guarantee, it - is possible to have buffer overrun bugs. In order to avoid - such bugs, we implement the entire processing of the 's' - directive ourselves. */ - int flags = dp->flags; - int has_width; - size_t width; - int has_precision; - size_t precision; - - has_width = 0; - width = 0; - if (dp->width_start != dp->width_end) - { - if (dp->width_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->width_arg_index].a.a_int; - width = arg; - if (arg < 0) - { - /* "A negative field width is taken as a '-' flag - followed by a positive field width." */ - flags |= FLAG_LEFT; - width = -width; - } - } - else - { - const FCHAR_T *digitp = dp->width_start; - - do - width = xsum (xtimes (width, 10), *digitp++ - '0'); - while (digitp != dp->width_end); - } - has_width = 1; - } - - has_precision = 0; - precision = 6; - if (dp->precision_start != dp->precision_end) - { - if (dp->precision_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; - /* "A negative precision is taken as if the precision - were omitted." */ - if (arg >= 0) - { - precision = arg; - has_precision = 1; - } - } - else - { - const FCHAR_T *digitp = dp->precision_start + 1; - - precision = 0; - while (digitp != dp->precision_end) - precision = xsum (xtimes (precision, 10), *digitp++ - '0'); - has_precision = 1; - } - } - -# if WIDE_CHAR_VERSION - /* %s in vasnwprintf. See the specification of fwprintf. */ - { - const char *arg = a.arg[dp->arg_index].a.a_string; - const char *arg_end; - size_t characters; - - if (has_precision) - { - /* Use only as many bytes as needed to produce PRECISION - wide characters, from the left. */ -# if HAVE_MBRTOWC - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - arg_end = arg; - characters = 0; - for (; precision > 0; precision--) - { - int count; -# if HAVE_MBRTOWC - count = mbrlen (arg_end, MB_CUR_MAX, &state); -# else - count = mblen (arg_end, MB_CUR_MAX); -# endif - if (count == 0) - /* Found the terminating NUL. */ - break; - if (count < 0) - /* Invalid or incomplete multibyte character. */ - goto fail_with_EILSEQ; - arg_end += count; - characters++; - } - } - else if (has_width) - { - /* Use the entire string, and count the number of wide - characters. */ -# if HAVE_MBRTOWC - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - arg_end = arg; - characters = 0; - for (;;) - { - int count; -# if HAVE_MBRTOWC - count = mbrlen (arg_end, MB_CUR_MAX, &state); -# else - count = mblen (arg_end, MB_CUR_MAX); -# endif - if (count == 0) - /* Found the terminating NUL. */ - break; - if (count < 0) - /* Invalid or incomplete multibyte character. */ - goto fail_with_EILSEQ; - arg_end += count; - characters++; - } - } - else - { - /* Use the entire string. */ - arg_end = arg + strlen (arg); - /* The number of characters doesn't matter. */ - characters = 0; - } - - if (characters < width && !(flags & FLAG_LEFT)) - { - size_t n = width - characters; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - - if (has_precision || has_width) - { - /* We know the number of wide characters in advance. */ - size_t remaining; -# if HAVE_MBRTOWC - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - ENSURE_ALLOCATION (xsum (length, characters)); - for (remaining = characters; remaining > 0; remaining--) - { - wchar_t wc; - int count; -# if HAVE_MBRTOWC - count = mbrtowc (&wc, arg, arg_end - arg, &state); -# else - count = mbtowc (&wc, arg, arg_end - arg); -# endif - if (count <= 0) - /* mbrtowc not consistent with mbrlen, or mbtowc - not consistent with mblen. */ - abort (); - result[length++] = wc; - arg += count; - } - if (!(arg == arg_end)) - abort (); - } - else - { -# if HAVE_MBRTOWC - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - while (arg < arg_end) - { - wchar_t wc; - int count; -# if HAVE_MBRTOWC - count = mbrtowc (&wc, arg, arg_end - arg, &state); -# else - count = mbtowc (&wc, arg, arg_end - arg); -# endif - if (count == 0) - /* mbrtowc not consistent with strlen. */ - abort (); - if (count < 0) - /* Invalid or incomplete multibyte character. */ - goto fail_with_EILSEQ; - ENSURE_ALLOCATION (xsum (length, 1)); - result[length++] = wc; - arg += count; - } - } - - if (characters < width && (flags & FLAG_LEFT)) - { - size_t n = width - characters; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - } -# else - /* %ls in vasnprintf. See the specification of fprintf. */ - { - const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string; - const wchar_t *arg_end; - size_t characters; -# if !DCHAR_IS_TCHAR - /* This code assumes that TCHAR_T is 'char'. */ - static_assert (sizeof (TCHAR_T) == 1); - TCHAR_T *tmpsrc; - DCHAR_T *tmpdst; - size_t tmpdst_len; -# endif - size_t w; - - if (has_precision) - { - /* Use only as many wide characters as needed to produce - at most PRECISION bytes, from the left. */ -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - arg_end = arg; - characters = 0; - while (precision > 0) - { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; - - if (*arg_end == 0) - /* Found the terminating null wide character. */ - break; - count = local_wcrtomb (cbuf, *arg_end, &state); - if (count < 0) - /* Cannot convert. */ - goto fail_with_EILSEQ; - if (precision < (unsigned int) count) - break; - arg_end++; - characters += count; - precision -= count; - } - } -# if DCHAR_IS_TCHAR - else if (has_width) -# else - else -# endif - { - /* Use the entire string, and count the number of - bytes. */ -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - arg_end = arg; - characters = 0; - for (;;) - { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; - - if (*arg_end == 0) - /* Found the terminating null wide character. */ - break; - count = local_wcrtomb (cbuf, *arg_end, &state); - if (count < 0) - /* Cannot convert. */ - goto fail_with_EILSEQ; - arg_end++; - characters += count; - } - } -# if DCHAR_IS_TCHAR - else - { - /* Use the entire string. */ - arg_end = arg + local_wcslen (arg); - /* The number of bytes doesn't matter. */ - characters = 0; - } -# endif - -# if !DCHAR_IS_TCHAR - /* Convert the string into a piece of temporary memory. */ - tmpsrc = (TCHAR_T *) malloc (characters * sizeof (TCHAR_T)); - if (tmpsrc == NULL) - goto out_of_memory; - { - TCHAR_T *tmpptr = tmpsrc; - size_t remaining; -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - for (remaining = characters; remaining > 0; ) - { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; - - if (*arg == 0) - abort (); - count = local_wcrtomb (cbuf, *arg, &state); - if (count <= 0) - /* Inconsistency. */ - abort (); - memcpy (tmpptr, cbuf, count); - tmpptr += count; - arg++; - remaining -= count; - } - if (!(arg == arg_end)) - abort (); - } - - /* Convert from TCHAR_T[] to DCHAR_T[]. */ - tmpdst = - DCHAR_CONV_FROM_ENCODING (locale_charset (), - iconveh_question_mark, - tmpsrc, characters, - NULL, - NULL, &tmpdst_len); - if (tmpdst == NULL) - { - free (tmpsrc); - goto fail_with_errno; - } - free (tmpsrc); -# endif - - if (has_width) - { -# if ENABLE_UNISTDIO - /* Outside POSIX, it's preferable to compare the width - against the number of _characters_ of the converted - value. */ - w = DCHAR_MBSNLEN (result + length, characters); -# else - /* The width is compared against the number of _bytes_ - of the converted value, says POSIX. */ - w = characters; -# endif - } - else - /* w doesn't matter. */ - w = 0; - - if (w < width && !(flags & FLAG_LEFT)) - { - size_t n = width - w; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - -# if DCHAR_IS_TCHAR - if (has_precision || has_width) - { - /* We know the number of bytes in advance. */ - size_t remaining; -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - ENSURE_ALLOCATION (xsum (length, characters)); - for (remaining = characters; remaining > 0; ) - { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; - - if (*arg == 0) - abort (); - count = local_wcrtomb (cbuf, *arg, &state); - if (count <= 0) - /* Inconsistency. */ - abort (); - memcpy (result + length, cbuf, count); - length += count; - arg++; - remaining -= count; - } - if (!(arg == arg_end)) - abort (); - } - else - { -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - while (arg < arg_end) - { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; - - if (*arg == 0) - abort (); - count = local_wcrtomb (cbuf, *arg, &state); - if (count <= 0) - /* Cannot convert. */ - goto fail_with_EILSEQ; - ENSURE_ALLOCATION (xsum (length, count)); - memcpy (result + length, cbuf, count); - length += count; - arg++; - } - } -# else - ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len), - { free (tmpdst); goto out_of_memory; }); - DCHAR_CPY (result + length, tmpdst, tmpdst_len); - free (tmpdst); - length += tmpdst_len; -# endif - - if (w < width && (flags & FLAG_LEFT)) - { - size_t n = width - w; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - } -# endif - } -#endif -#if (NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T && !WIDE_CHAR_VERSION - else if (dp->conversion == 'c' - && a.arg[dp->arg_index].type == TYPE_WIDE_CHAR) - { - /* Implement the 'lc' directive ourselves, in order to provide - a correct behaviour for the null wint_t argument and/or the - fallback that avoids EILSEQ. */ - int flags = dp->flags; - int has_width; - size_t width; - - has_width = 0; - width = 0; - if (dp->width_start != dp->width_end) - { - if (dp->width_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->width_arg_index].a.a_int; - width = arg; - if (arg < 0) - { - /* "A negative field width is taken as a '-' flag - followed by a positive field width." */ - flags |= FLAG_LEFT; - width = -width; - } - } - else - { - const FCHAR_T *digitp = dp->width_start; - - do - width = xsum (xtimes (width, 10), *digitp++ - '0'); - while (digitp != dp->width_end); - } - has_width = 1; - } - - /* %lc in vasnprintf. See the specification of fprintf. */ - { - wchar_t arg = (wchar_t) a.arg[dp->arg_index].a.a_wide_char; - size_t characters; -# if !DCHAR_IS_TCHAR - /* This code assumes that TCHAR_T is 'char'. */ - static_assert (sizeof (TCHAR_T) == 1); - TCHAR_T tmpsrc[64]; /* Assume MB_CUR_MAX <= 64. */ - DCHAR_T *tmpdst; - size_t tmpdst_len; -# endif - size_t w; - -# if DCHAR_IS_TCHAR - if (has_width) -# endif - { - /* Count the number of bytes. */ - characters = 0; - if (arg != 0) - { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - - count = local_wcrtomb (cbuf, arg, &state); - if (count < 0) - /* Cannot convert. */ - goto fail_with_EILSEQ; - characters = count; - } - } -# if DCHAR_IS_TCHAR - else - { - /* The number of bytes doesn't matter. */ - characters = 0; - } -# endif - -# if !DCHAR_IS_TCHAR - /* Convert the string into a piece of temporary memory. */ - if (characters > 0) /* implies arg != 0 */ - { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - - count = local_wcrtomb (cbuf, arg, &state); - if (count <= 0) - /* Inconsistency. */ - abort (); - memcpy (tmpsrc, cbuf, count); - } - - /* Convert from TCHAR_T[] to DCHAR_T[]. */ - tmpdst = - DCHAR_CONV_FROM_ENCODING (locale_charset (), - iconveh_question_mark, - tmpsrc, characters, - NULL, - NULL, &tmpdst_len); - if (tmpdst == NULL) - goto fail_with_errno; -# endif - - if (has_width) - { -# if ENABLE_UNISTDIO - /* Outside POSIX, it's preferable to compare the width - against the number of _characters_ of the converted - value. */ - w = DCHAR_MBSNLEN (result + length, characters); -# else - /* The width is compared against the number of _bytes_ - of the converted value, says POSIX. */ - w = characters; -# endif - } - else - /* w doesn't matter. */ - w = 0; - - if (w < width && !(flags & FLAG_LEFT)) - { - size_t n = width - w; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - -# if DCHAR_IS_TCHAR - if (has_width) - { - /* We know the number of bytes in advance. */ - ENSURE_ALLOCATION (xsum (length, characters)); - if (characters > 0) /* implies arg != 0 */ - { - int count; -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - - count = local_wcrtomb (result + length, arg, &state); - if (count <= 0) - /* Inconsistency. */ - abort (); - length += count; - } - } - else - { - if (arg != 0) - { - char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */ - int count; -# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t - mbstate_t state; - memset (&state, '\0', sizeof (mbstate_t)); -# endif - - count = local_wcrtomb (cbuf, arg, &state); - if (count < 0) - /* Cannot convert. */ - goto fail_with_EILSEQ; - ENSURE_ALLOCATION (xsum (length, count)); - memcpy (result + length, cbuf, count); - length += count; - } - } -# else - ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len), - { free (tmpdst); goto out_of_memory; }); - DCHAR_CPY (result + length, tmpdst, tmpdst_len); - free (tmpdst); - length += tmpdst_len; -# endif - - if (w < width && (flags & FLAG_LEFT)) - { - size_t n = width - w; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - } - } -#endif -#if NEED_WPRINTF_DIRECTIVE_C && WIDE_CHAR_VERSION - else if (dp->conversion == 'c' - && a.arg[dp->arg_index].type != TYPE_WIDE_CHAR) - { - /* Implement the 'c' directive ourselves, in order to avoid - EILSEQ in the "C" locale. */ - int flags = dp->flags; - size_t width; - - width = 0; - if (dp->width_start != dp->width_end) - { - if (dp->width_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->width_arg_index].a.a_int; - width = arg; - if (arg < 0) - { - /* "A negative field width is taken as a '-' flag - followed by a positive field width." */ - flags |= FLAG_LEFT; - width = -width; - } - } - else - { - const FCHAR_T *digitp = dp->width_start; - - do - width = xsum (xtimes (width, 10), *digitp++ - '0'); - while (digitp != dp->width_end); - } - } - - /* %c in vasnwprintf. See the specification of fwprintf. */ - { - char arg = (char) a.arg[dp->arg_index].a.a_char; - mbstate_t state; - wchar_t wc; - - memset (&state, '\0', sizeof (mbstate_t)); - int count = mbrtowc (&wc, &arg, 1, &state); - if (count < 0) - /* Invalid or incomplete multibyte character. */ - goto fail_with_EILSEQ; - - if (1 < width && !(flags & FLAG_LEFT)) - { - size_t n = width - 1; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - - ENSURE_ALLOCATION (xsum (length, 1)); - result[length++] = wc; - - if (1 < width && (flags & FLAG_LEFT)) - { - size_t n = width - 1; - ENSURE_ALLOCATION (xsum (length, n)); - DCHAR_SET (result + length, ' ', n); - length += n; - } - } - } -#endif -#if NEED_PRINTF_DIRECTIVE_B || NEED_PRINTF_DIRECTIVE_UPPERCASE_B - else if (0 -# if NEED_PRINTF_DIRECTIVE_B - || (dp->conversion == 'b') -# endif -# if NEED_PRINTF_DIRECTIVE_UPPERCASE_B - || (dp->conversion == 'B') -# endif - ) - { - arg_type type = a.arg[dp->arg_index].type; - int flags = dp->flags; - int has_width; - size_t width; - int has_precision; - size_t precision; - size_t tmp_length; - size_t count; - DCHAR_T tmpbuf[700]; - DCHAR_T *tmp; - DCHAR_T *tmp_end; - DCHAR_T *tmp_start; - DCHAR_T *pad_ptr; - DCHAR_T *p; - - has_width = 0; - width = 0; - if (dp->width_start != dp->width_end) - { - if (dp->width_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->width_arg_index].a.a_int; - width = arg; - if (arg < 0) - { - /* "A negative field width is taken as a '-' flag - followed by a positive field width." */ - flags |= FLAG_LEFT; - width = -width; - } - } - else - { - const FCHAR_T *digitp = dp->width_start; - - do - width = xsum (xtimes (width, 10), *digitp++ - '0'); - while (digitp != dp->width_end); - } - has_width = 1; - } - - has_precision = 0; - precision = 1; - if (dp->precision_start != dp->precision_end) - { - if (dp->precision_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; - /* "A negative precision is taken as if the precision - were omitted." */ - if (arg >= 0) - { - precision = arg; - has_precision = 1; - } - } - else - { - const FCHAR_T *digitp = dp->precision_start + 1; - - precision = 0; - while (digitp != dp->precision_end) - precision = xsum (xtimes (precision, 10), *digitp++ - '0'); - has_precision = 1; - } - } - - /* Allocate a temporary buffer of sufficient size. */ - switch (type) - { - default: - tmp_length = - (unsigned int) (sizeof (unsigned int) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_ULONGINT: - tmp_length = - (unsigned int) (sizeof (unsigned long int) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_ULONGLONGINT: - tmp_length = - (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT8_T: - tmp_length = - (unsigned int) (sizeof (uint8_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT16_T: - tmp_length = - (unsigned int) (sizeof (uint16_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT32_T: - tmp_length = - (unsigned int) (sizeof (uint32_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT64_T: - tmp_length = - (unsigned int) (sizeof (uint64_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST8_T: - tmp_length = - (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST16_T: - tmp_length = - (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST32_T: - tmp_length = - (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - case TYPE_UINT_FAST64_T: - tmp_length = - (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT) - + 1; /* turn floor into ceil */ - break; - } - if (tmp_length < precision) - tmp_length = precision; - /* Add 2, to account for a prefix from the alternate form. */ - tmp_length = xsum (tmp_length, 2); - - if (tmp_length < width) - tmp_length = width; - - if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) - tmp = tmpbuf; - else - { - size_t tmp_memsize = xtimes (tmp_length, sizeof (DCHAR_T)); - - if (size_overflow_p (tmp_memsize)) - /* Overflow, would lead to out of memory. */ - goto out_of_memory; - tmp = (DCHAR_T *) malloc (tmp_memsize); - if (tmp == NULL) - /* Out of memory. */ - goto out_of_memory; - } - - tmp_end = tmp + tmp_length; - - unsigned long long arg; - switch (type) - { - case TYPE_UCHAR: - arg = a.arg[dp->arg_index].a.a_uchar; - break; - case TYPE_USHORT: - arg = a.arg[dp->arg_index].a.a_ushort; - break; - case TYPE_UINT: - arg = a.arg[dp->arg_index].a.a_uint; - break; - case TYPE_ULONGINT: - arg = a.arg[dp->arg_index].a.a_ulongint; - break; - case TYPE_ULONGLONGINT: - arg = a.arg[dp->arg_index].a.a_ulonglongint; - break; - case TYPE_UINT8_T: - arg = a.arg[dp->arg_index].a.a_uint8_t; - break; - case TYPE_UINT16_T: - arg = a.arg[dp->arg_index].a.a_uint16_t; - break; - case TYPE_UINT32_T: - arg = a.arg[dp->arg_index].a.a_uint32_t; - break; - case TYPE_UINT64_T: - arg = a.arg[dp->arg_index].a.a_uint64_t; - break; - case TYPE_UINT_FAST8_T: - arg = a.arg[dp->arg_index].a.a_uint_fast8_t; - break; - case TYPE_UINT_FAST16_T: - arg = a.arg[dp->arg_index].a.a_uint_fast16_t; - break; - case TYPE_UINT_FAST32_T: - arg = a.arg[dp->arg_index].a.a_uint_fast32_t; - break; - case TYPE_UINT_FAST64_T: - arg = a.arg[dp->arg_index].a.a_uint_fast64_t; - break; - default: - abort (); - } - int need_prefix = ((flags & FLAG_ALT) && arg != 0); - - p = tmp_end; - /* "The result of converting a zero value with a precision - of zero is no characters." */ - if (!(has_precision && precision == 0 && arg == 0)) - { - do - { - *--p = '0' + (arg & 1); - arg = arg >> 1; - } - while (arg != 0); - } - - if (has_precision) - { - DCHAR_T *digits_start = tmp_end - precision; - while (p > digits_start) - *--p = '0'; - } - - pad_ptr = p; - - if (need_prefix) - { -# if NEED_PRINTF_DIRECTIVE_B && !NEED_PRINTF_DIRECTIVE_UPPERCASE_B - *--p = 'b'; -# elif NEED_PRINTF_DIRECTIVE_UPPERCASE_B && !NEED_PRINTF_DIRECTIVE_B - *--p = 'B'; -# else - *--p = dp->conversion; -# endif - *--p = '0'; - } - tmp_start = p; - - /* The generated string now extends from tmp_start to tmp_end, - with the zero padding insertion point being at pad_ptr, - tmp_start <= pad_ptr <= tmp_end. */ - count = tmp_end - tmp_start; - - if (count < width) - { - size_t pad = width - count; - - if (flags & FLAG_LEFT) - { - /* Pad with spaces on the right. */ - for (p = tmp_start; p < tmp_end; p++) - *(p - pad) = *p; - for (p = tmp_end - pad; p < tmp_end; p++) - *p = ' '; - } - else if ((flags & FLAG_ZERO) - /* Neither ISO C nor POSIX specify that the '0' - flag is ignored when a width and a precision - are both present. But most implementations - do so. */ - && !(has_width && has_precision)) - { - /* Pad with zeroes. */ - for (p = tmp_start; p < pad_ptr; p++) - *(p - pad) = *p; - for (p = pad_ptr - pad; p < pad_ptr; p++) - *p = '0'; - } - else - { - /* Pad with spaces on the left. */ - for (p = tmp_start - pad; p < tmp_start; p++) - *p = ' '; - } - - tmp_start = tmp_start - pad; - } - - count = tmp_end - tmp_start; - - if (count > tmp_length) - /* tmp_length was incorrectly calculated - fix the - code above! */ - abort (); - - /* Make room for the result. */ - if (count >= allocated - length) - { - size_t n = xsum (length, count); - - ENSURE_ALLOCATION (n); - } - - /* Append the result. */ - memcpy (result + length, tmp_start, count * sizeof (DCHAR_T)); - if (tmp != tmpbuf) - free (tmp); - length += count; - } -#endif -#if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) - else if ((dp->conversion == 'a' || dp->conversion == 'A') -# if !(NEED_PRINTF_DIRECTIVE_A || (NEED_PRINTF_LONG_DOUBLE && NEED_PRINTF_DOUBLE)) - && (0 -# if NEED_PRINTF_DOUBLE - || a.arg[dp->arg_index].type == TYPE_DOUBLE -# endif -# if NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) - || a.arg[dp->arg_index].type == TYPE_LONGDOUBLE -# endif - ) -# endif - ) - { - arg_type type = a.arg[dp->arg_index].type; - int flags = dp->flags; - size_t width; - int has_precision; - size_t precision; - size_t tmp_length; - size_t count; - DCHAR_T tmpbuf[700]; - DCHAR_T *tmp; - DCHAR_T *pad_ptr; - DCHAR_T *p; - - width = 0; - if (dp->width_start != dp->width_end) - { - if (dp->width_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->width_arg_index].a.a_int; - width = arg; - if (arg < 0) - { - /* "A negative field width is taken as a '-' flag - followed by a positive field width." */ - flags |= FLAG_LEFT; - width = -width; - } - } - else - { - const FCHAR_T *digitp = dp->width_start; - - do - width = xsum (xtimes (width, 10), *digitp++ - '0'); - while (digitp != dp->width_end); - } - } - - has_precision = 0; - precision = 0; - if (dp->precision_start != dp->precision_end) - { - if (dp->precision_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; - /* "A negative precision is taken as if the precision - were omitted." */ - if (arg >= 0) - { - precision = arg; - has_precision = 1; - } - } - else - { - const FCHAR_T *digitp = dp->precision_start + 1; - - precision = 0; - while (digitp != dp->precision_end) - precision = xsum (xtimes (precision, 10), *digitp++ - '0'); - has_precision = 1; - } - } - - /* Allocate a temporary buffer of sufficient size. */ - if (type == TYPE_LONGDOUBLE) - tmp_length = - (unsigned int) ((LDBL_DIG + 1) - * 0.831 /* decimal -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - else - tmp_length = - (unsigned int) ((DBL_DIG + 1) - * 0.831 /* decimal -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - if (tmp_length < precision) - tmp_length = precision; - /* Account for sign, decimal point etc. */ - tmp_length = xsum (tmp_length, 12); - - if (tmp_length < width) - tmp_length = width; - - tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ - - if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) - tmp = tmpbuf; - else - { - size_t tmp_memsize = xtimes (tmp_length, sizeof (DCHAR_T)); - - if (size_overflow_p (tmp_memsize)) - /* Overflow, would lead to out of memory. */ - goto out_of_memory; - tmp = (DCHAR_T *) malloc (tmp_memsize); - if (tmp == NULL) - /* Out of memory. */ - goto out_of_memory; - } - - pad_ptr = NULL; - p = tmp; - if (type == TYPE_LONGDOUBLE) - { -# if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) - long double arg = a.arg[dp->arg_index].a.a_longdouble; - - if (isnanl (arg)) - { - if (dp->conversion == 'A') - { - *p++ = 'N'; *p++ = 'A'; *p++ = 'N'; - } - else - { - *p++ = 'n'; *p++ = 'a'; *p++ = 'n'; - } - } - else - { - int sign = 0; - DECL_LONG_DOUBLE_ROUNDING - - BEGIN_LONG_DOUBLE_ROUNDING (); - - if (signbit (arg)) /* arg < 0.0L or negative zero */ - { - sign = -1; - arg = -arg; - } - - if (sign < 0) - *p++ = '-'; - else if (flags & FLAG_SHOWSIGN) - *p++ = '+'; - else if (flags & FLAG_SPACE) - *p++ = ' '; - - if (arg > 0.0L && arg + arg == arg) - { - if (dp->conversion == 'A') - { - *p++ = 'I'; *p++ = 'N'; *p++ = 'F'; - } - else - { - *p++ = 'i'; *p++ = 'n'; *p++ = 'f'; - } - } - else - { - int exponent; - long double mantissa; - - if (arg > 0.0L) - mantissa = printf_frexpl (arg, &exponent); - else - { - exponent = 0; - mantissa = 0.0L; - } - - if (has_precision - && precision < (unsigned int) ((LDBL_DIG + 1) * 0.831) + 1) - { - /* Round the mantissa. */ - long double tail = mantissa; - size_t q; - - for (q = precision; ; q--) - { - int digit = (int) tail; - tail -= digit; - if (q == 0) - { - if (digit & 1 ? tail >= 0.5L : tail > 0.5L) - tail = 1 - tail; - else - tail = - tail; - break; - } - tail *= 16.0L; - } - if (tail != 0.0L) - for (q = precision; q > 0; q--) - tail *= 0.0625L; - mantissa += tail; - } - - *p++ = '0'; - *p++ = dp->conversion - 'A' + 'X'; - pad_ptr = p; - { - int digit; - - digit = (int) mantissa; - mantissa -= digit; - *p++ = '0' + digit; - if ((flags & FLAG_ALT) - || mantissa > 0.0L || precision > 0) - { - *p++ = decimal_point_char (); - /* This loop terminates because we assume - that FLT_RADIX is a power of 2. */ - while (mantissa > 0.0L) - { - mantissa *= 16.0L; - digit = (int) mantissa; - mantissa -= digit; - *p++ = digit - + (digit < 10 - ? '0' - : dp->conversion - 10); - if (precision > 0) - precision--; - } - while (precision > 0) - { - *p++ = '0'; - precision--; - } - } - } - *p++ = dp->conversion - 'A' + 'P'; -# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR - { - static const wchar_t decimal_format[] = - { '%', '+', 'd', '\0' }; - SNPRINTF (p, 6 + 1, decimal_format, exponent); - } - while (*p != '\0') - p++; -# else - if (sizeof (DCHAR_T) == 1) - { - sprintf ((char *) p, "%+d", exponent); - while (*p != '\0') - p++; - } - else - { - char expbuf[6 + 1]; - const char *ep; - sprintf (expbuf, "%+d", exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) - p++; - } -# endif - } - - END_LONG_DOUBLE_ROUNDING (); - } -# else - abort (); -# endif - } - else - { -# if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE - double arg = a.arg[dp->arg_index].a.a_double; - - if (isnand (arg)) - { - if (dp->conversion == 'A') - { - *p++ = 'N'; *p++ = 'A'; *p++ = 'N'; - } - else - { - *p++ = 'n'; *p++ = 'a'; *p++ = 'n'; - } - } - else - { - int sign = 0; - - if (signbit (arg)) /* arg < 0.0 or negative zero */ - { - sign = -1; - arg = -arg; - } - - if (sign < 0) - *p++ = '-'; - else if (flags & FLAG_SHOWSIGN) - *p++ = '+'; - else if (flags & FLAG_SPACE) - *p++ = ' '; - - if (arg > 0.0 && arg + arg == arg) - { - if (dp->conversion == 'A') - { - *p++ = 'I'; *p++ = 'N'; *p++ = 'F'; - } - else - { - *p++ = 'i'; *p++ = 'n'; *p++ = 'f'; - } - } - else - { - int exponent; - double mantissa; - - if (arg > 0.0) - mantissa = printf_frexp (arg, &exponent); - else - { - exponent = 0; - mantissa = 0.0; - } - - if (has_precision - && precision < (unsigned int) ((DBL_DIG + 1) * 0.831) + 1) - { - /* Round the mantissa. */ - double tail = mantissa; - size_t q; - - for (q = precision; ; q--) - { - int digit = (int) tail; - tail -= digit; - if (q == 0) - { - if (digit & 1 ? tail >= 0.5 : tail > 0.5) - tail = 1 - tail; - else - tail = - tail; - break; - } - tail *= 16.0; - } - if (tail != 0.0) - for (q = precision; q > 0; q--) - tail *= 0.0625; - mantissa += tail; - } - - *p++ = '0'; - *p++ = dp->conversion - 'A' + 'X'; - pad_ptr = p; - { - int digit; - - digit = (int) mantissa; - mantissa -= digit; - *p++ = '0' + digit; - if ((flags & FLAG_ALT) - || mantissa > 0.0 || precision > 0) - { - *p++ = decimal_point_char (); - /* This loop terminates because we assume - that FLT_RADIX is a power of 2. */ - while (mantissa > 0.0) - { - mantissa *= 16.0; - digit = (int) mantissa; - mantissa -= digit; - *p++ = digit - + (digit < 10 - ? '0' - : dp->conversion - 10); - if (precision > 0) - precision--; - } - while (precision > 0) - { - *p++ = '0'; - precision--; - } - } - } - *p++ = dp->conversion - 'A' + 'P'; -# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR - { - static const wchar_t decimal_format[] = - { '%', '+', 'd', '\0' }; - SNPRINTF (p, 6 + 1, decimal_format, exponent); - } - while (*p != '\0') - p++; -# else - if (sizeof (DCHAR_T) == 1) - { - sprintf ((char *) p, "%+d", exponent); - while (*p != '\0') - p++; - } - else - { - char expbuf[6 + 1]; - const char *ep; - sprintf (expbuf, "%+d", exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) - p++; - } -# endif - } - } -# else - abort (); -# endif - } - - /* The generated string now extends from tmp to p, with the - zero padding insertion point being at pad_ptr. */ - count = p - tmp; - - if (count < width) - { - size_t pad = width - count; - DCHAR_T *end = p + pad; - - if (flags & FLAG_LEFT) - { - /* Pad with spaces on the right. */ - for (; pad > 0; pad--) - *p++ = ' '; - } - else if ((flags & FLAG_ZERO) && pad_ptr != NULL) - { - /* Pad with zeroes. */ - DCHAR_T *q = end; - - while (p > pad_ptr) - *--q = *--p; - for (; pad > 0; pad--) - *p++ = '0'; - } - else - { - /* Pad with spaces on the left. */ - DCHAR_T *q = end; - - while (p > tmp) - *--q = *--p; - for (; pad > 0; pad--) - *p++ = ' '; - } - - p = end; - } - - count = p - tmp; - - if (count >= tmp_length) - /* tmp_length was incorrectly calculated - fix the - code above! */ - abort (); - - /* Make room for the result. */ - if (count >= allocated - length) - { - size_t n = xsum (length, count); - - ENSURE_ALLOCATION (n); - } - - /* Append the result. */ - memcpy (result + length, tmp, count * sizeof (DCHAR_T)); - if (tmp != tmpbuf) - free (tmp); - length += count; - } -#endif -#if NEED_PRINTF_INFINITE_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_LONG_DOUBLE - else if ((dp->conversion == 'f' || dp->conversion == 'F' - || dp->conversion == 'e' || dp->conversion == 'E' - || dp->conversion == 'g' || dp->conversion == 'G' - || dp->conversion == 'a' || dp->conversion == 'A') - && (0 -# if NEED_PRINTF_DOUBLE - || a.arg[dp->arg_index].type == TYPE_DOUBLE -# elif NEED_PRINTF_INFINITE_DOUBLE - || (a.arg[dp->arg_index].type == TYPE_DOUBLE - /* The systems (mingw) which produce wrong output - for Inf, -Inf, and NaN also do so for -0.0. - Therefore we treat this case here as well. */ - && is_infinite_or_zero (a.arg[dp->arg_index].a.a_double)) -# endif -# if NEED_PRINTF_LONG_DOUBLE - || a.arg[dp->arg_index].type == TYPE_LONGDOUBLE -# elif NEED_PRINTF_INFINITE_LONG_DOUBLE - || (a.arg[dp->arg_index].type == TYPE_LONGDOUBLE - /* Some systems produce wrong output for Inf, - -Inf, and NaN. Some systems in this category - (IRIX 5.3) also do so for -0.0. Therefore we - treat this case here as well. */ - && is_infinite_or_zerol (a.arg[dp->arg_index].a.a_longdouble)) -# endif - )) - { -# if (NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE) - arg_type type = a.arg[dp->arg_index].type; -# endif - int flags = dp->flags; - size_t width; - size_t count; - int has_precision; - size_t precision; - size_t tmp_length; - DCHAR_T tmpbuf[700]; - DCHAR_T *tmp; - DCHAR_T *pad_ptr; - DCHAR_T *p; - - width = 0; - if (dp->width_start != dp->width_end) - { - if (dp->width_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->width_arg_index].a.a_int; - width = arg; - if (arg < 0) - { - /* "A negative field width is taken as a '-' flag - followed by a positive field width." */ - flags |= FLAG_LEFT; - width = -width; - } - } - else - { - const FCHAR_T *digitp = dp->width_start; - - do - width = xsum (xtimes (width, 10), *digitp++ - '0'); - while (digitp != dp->width_end); - } - } - - has_precision = 0; - precision = 0; - if (dp->precision_start != dp->precision_end) - { - if (dp->precision_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; - /* "A negative precision is taken as if the precision - were omitted." */ - if (arg >= 0) - { - precision = arg; - has_precision = 1; - } - } - else - { - const FCHAR_T *digitp = dp->precision_start + 1; - - precision = 0; - while (digitp != dp->precision_end) - precision = xsum (xtimes (precision, 10), *digitp++ - '0'); - has_precision = 1; - } - } - - /* POSIX specifies the default precision to be 6 for %f, %F, - %e, %E, but not for %g, %G. Implementations appear to use - the same default precision also for %g, %G. But for %a, %A, - the default precision is 0. */ - if (!has_precision) - if (!(dp->conversion == 'a' || dp->conversion == 'A')) - precision = 6; - - /* Allocate a temporary buffer of sufficient size. */ -# if NEED_PRINTF_DOUBLE && NEED_PRINTF_LONG_DOUBLE - tmp_length = (type == TYPE_LONGDOUBLE ? LDBL_DIG + 1 : DBL_DIG + 1); -# elif NEED_PRINTF_INFINITE_DOUBLE && NEED_PRINTF_LONG_DOUBLE - tmp_length = (type == TYPE_LONGDOUBLE ? LDBL_DIG + 1 : 0); -# elif NEED_PRINTF_LONG_DOUBLE - tmp_length = LDBL_DIG + 1; -# elif NEED_PRINTF_DOUBLE - tmp_length = DBL_DIG + 1; -# else - tmp_length = 0; -# endif - if (tmp_length < precision) - tmp_length = precision; -# if NEED_PRINTF_LONG_DOUBLE -# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE - if (type == TYPE_LONGDOUBLE) -# endif - if (dp->conversion == 'f' || dp->conversion == 'F') - { - long double arg = a.arg[dp->arg_index].a.a_longdouble; - if (!(isnanl (arg) || arg + arg == arg)) - { - /* arg is finite and nonzero. */ - int exponent = floorlog10l (arg < 0 ? -arg : arg); - if (exponent >= 0 && tmp_length < exponent + precision) - tmp_length = exponent + precision; - } - } -# endif -# if NEED_PRINTF_DOUBLE -# if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE - if (type == TYPE_DOUBLE) -# endif - if (dp->conversion == 'f' || dp->conversion == 'F') - { - double arg = a.arg[dp->arg_index].a.a_double; - if (!(isnand (arg) || arg + arg == arg)) - { - /* arg is finite and nonzero. */ - int exponent = floorlog10 (arg < 0 ? -arg : arg); - if (exponent >= 0 && tmp_length < exponent + precision) - tmp_length = exponent + precision; - } - } -# endif - /* Account for sign, decimal point etc. */ - tmp_length = xsum (tmp_length, 12); - - if (tmp_length < width) - tmp_length = width; - - tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */ - - if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) - tmp = tmpbuf; - else - { - size_t tmp_memsize = xtimes (tmp_length, sizeof (DCHAR_T)); - - if (size_overflow_p (tmp_memsize)) - /* Overflow, would lead to out of memory. */ - goto out_of_memory; - tmp = (DCHAR_T *) malloc (tmp_memsize); - if (tmp == NULL) - /* Out of memory. */ - goto out_of_memory; - } - - pad_ptr = NULL; - p = tmp; - -# if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE -# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE - if (type == TYPE_LONGDOUBLE) -# endif - { - long double arg = a.arg[dp->arg_index].a.a_longdouble; - - if (isnanl (arg)) - { - if (dp->conversion >= 'A' && dp->conversion <= 'Z') - { - *p++ = 'N'; *p++ = 'A'; *p++ = 'N'; - } - else - { - *p++ = 'n'; *p++ = 'a'; *p++ = 'n'; - } - } - else - { - int sign = 0; - DECL_LONG_DOUBLE_ROUNDING - - BEGIN_LONG_DOUBLE_ROUNDING (); - - if (signbit (arg)) /* arg < 0.0L or negative zero */ - { - sign = -1; - arg = -arg; - } - - if (sign < 0) - *p++ = '-'; - else if (flags & FLAG_SHOWSIGN) - *p++ = '+'; - else if (flags & FLAG_SPACE) - *p++ = ' '; - - if (arg > 0.0L && arg + arg == arg) - { - if (dp->conversion >= 'A' && dp->conversion <= 'Z') - { - *p++ = 'I'; *p++ = 'N'; *p++ = 'F'; - } - else - { - *p++ = 'i'; *p++ = 'n'; *p++ = 'f'; - } - } - else - { -# if NEED_PRINTF_LONG_DOUBLE - pad_ptr = p; - - if (dp->conversion == 'f' || dp->conversion == 'F') - { - char *digits; - size_t ndigits; - - digits = - scale10_round_decimal_long_double (arg, precision); - if (digits == NULL) - { - END_LONG_DOUBLE_ROUNDING (); - goto out_of_memory; - } - ndigits = strlen (digits); - - if (ndigits > precision) - do - { - --ndigits; - *p++ = digits[ndigits]; - } - while (ndigits > precision); - else - *p++ = '0'; - /* Here ndigits <= precision. */ - if ((flags & FLAG_ALT) || precision > 0) - { - *p++ = decimal_point_char (); - for (; precision > ndigits; precision--) - *p++ = '0'; - while (ndigits > 0) - { - --ndigits; - *p++ = digits[ndigits]; - } - } - - free (digits); - } - else if (dp->conversion == 'e' || dp->conversion == 'E') - { - int exponent; - - if (arg == 0.0L) - { - exponent = 0; - *p++ = '0'; - if ((flags & FLAG_ALT) || precision > 0) - { - *p++ = decimal_point_char (); - for (; precision > 0; precision--) - *p++ = '0'; - } - } - else - { - /* arg > 0.0L. */ - int adjusted; - char *digits; - size_t ndigits; - - exponent = floorlog10l (arg); - adjusted = 0; - for (;;) - { - digits = - scale10_round_decimal_long_double (arg, - (int)precision - exponent); - if (digits == NULL) - { - END_LONG_DOUBLE_ROUNDING (); - goto out_of_memory; - } - ndigits = strlen (digits); - - if (ndigits == precision + 1) - break; - if (ndigits < precision - || ndigits > precision + 2) - /* The exponent was not guessed - precisely enough. */ - abort (); - if (adjusted) - /* None of two values of exponent is - the right one. Prevent an endless - loop. */ - abort (); - free (digits); - if (ndigits == precision) - exponent -= 1; - else - exponent += 1; - adjusted = 1; - } - /* Here ndigits = precision+1. */ - if (is_borderline (digits, precision)) - { - /* Maybe the exponent guess was too high - and a smaller exponent can be reached - by turning a 10...0 into 9...9x. */ - char *digits2 = - scale10_round_decimal_long_double (arg, - (int)precision - exponent + 1); - if (digits2 == NULL) - { - free (digits); - END_LONG_DOUBLE_ROUNDING (); - goto out_of_memory; - } - if (strlen (digits2) == precision + 1) - { - free (digits); - digits = digits2; - exponent -= 1; - } - else - free (digits2); - } - /* Here ndigits = precision+1. */ - - *p++ = digits[--ndigits]; - if ((flags & FLAG_ALT) || precision > 0) - { - *p++ = decimal_point_char (); - while (ndigits > 0) - { - --ndigits; - *p++ = digits[ndigits]; - } - } - - free (digits); - } - - *p++ = dp->conversion; /* 'e' or 'E' */ -# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR - { - static const wchar_t decimal_format[] = - { '%', '+', '.', '2', 'd', '\0' }; - SNPRINTF (p, 6 + 1, decimal_format, exponent); - } - while (*p != '\0') - p++; -# else - if (sizeof (DCHAR_T) == 1) - { - sprintf ((char *) p, "%+.2d", exponent); - while (*p != '\0') - p++; - } - else - { - char expbuf[6 + 1]; - const char *ep; - sprintf (expbuf, "%+.2d", exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) - p++; - } -# endif - } - else if (dp->conversion == 'g' || dp->conversion == 'G') - { - if (precision == 0) - precision = 1; - /* precision >= 1. */ - - if (arg == 0.0L) - /* The exponent is 0, >= -4, < precision. - Use fixed-point notation. */ - { - size_t ndigits = precision; - /* Number of trailing zeroes that have to be - dropped. */ - size_t nzeroes = - (flags & FLAG_ALT ? 0 : precision - 1); - - --ndigits; - *p++ = '0'; - if ((flags & FLAG_ALT) || ndigits > nzeroes) - { - *p++ = decimal_point_char (); - while (ndigits > nzeroes) - { - --ndigits; - *p++ = '0'; - } - } - } - else - { - /* arg > 0.0L. */ - int exponent; - int adjusted; - char *digits; - size_t ndigits; - size_t nzeroes; - - exponent = floorlog10l (arg); - adjusted = 0; - for (;;) - { - digits = - scale10_round_decimal_long_double (arg, - (int)(precision - 1) - exponent); - if (digits == NULL) - { - END_LONG_DOUBLE_ROUNDING (); - goto out_of_memory; - } - ndigits = strlen (digits); - - if (ndigits == precision) - break; - if (ndigits < precision - 1 - || ndigits > precision + 1) - /* The exponent was not guessed - precisely enough. */ - abort (); - if (adjusted) - /* None of two values of exponent is - the right one. Prevent an endless - loop. */ - abort (); - free (digits); - if (ndigits < precision) - exponent -= 1; - else - exponent += 1; - adjusted = 1; - } - /* Here ndigits = precision. */ - if (is_borderline (digits, precision - 1)) - { - /* Maybe the exponent guess was too high - and a smaller exponent can be reached - by turning a 10...0 into 9...9x. */ - char *digits2 = - scale10_round_decimal_long_double (arg, - (int)(precision - 1) - exponent + 1); - if (digits2 == NULL) - { - free (digits); - END_LONG_DOUBLE_ROUNDING (); - goto out_of_memory; - } - if (strlen (digits2) == precision) - { - free (digits); - digits = digits2; - exponent -= 1; - } - else - free (digits2); - } - /* Here ndigits = precision. */ - - /* Determine the number of trailing zeroes - that have to be dropped. */ - nzeroes = 0; - if ((flags & FLAG_ALT) == 0) - while (nzeroes < ndigits - && digits[nzeroes] == '0') - nzeroes++; - - /* The exponent is now determined. */ - if (exponent >= -4 - && exponent < (long)precision) - { - /* Fixed-point notation: - max(exponent,0)+1 digits, then the - decimal point, then the remaining - digits without trailing zeroes. */ - if (exponent >= 0) - { - size_t ecount = exponent + 1; - /* Note: count <= precision = ndigits. */ - for (; ecount > 0; ecount--) - *p++ = digits[--ndigits]; - if ((flags & FLAG_ALT) || ndigits > nzeroes) - { - *p++ = decimal_point_char (); - while (ndigits > nzeroes) - { - --ndigits; - *p++ = digits[ndigits]; - } - } - } - else - { - size_t ecount = -exponent - 1; - *p++ = '0'; - *p++ = decimal_point_char (); - for (; ecount > 0; ecount--) - *p++ = '0'; - while (ndigits > nzeroes) - { - --ndigits; - *p++ = digits[ndigits]; - } - } - } - else - { - /* Exponential notation. */ - *p++ = digits[--ndigits]; - if ((flags & FLAG_ALT) || ndigits > nzeroes) - { - *p++ = decimal_point_char (); - while (ndigits > nzeroes) - { - --ndigits; - *p++ = digits[ndigits]; - } - } - *p++ = dp->conversion - 'G' + 'E'; /* 'e' or 'E' */ -# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR - { - static const wchar_t decimal_format[] = - { '%', '+', '.', '2', 'd', '\0' }; - SNPRINTF (p, 6 + 1, decimal_format, exponent); - } - while (*p != '\0') - p++; -# else - if (sizeof (DCHAR_T) == 1) - { - sprintf ((char *) p, "%+.2d", exponent); - while (*p != '\0') - p++; - } - else - { - char expbuf[6 + 1]; - const char *ep; - sprintf (expbuf, "%+.2d", exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) - p++; - } -# endif - } - - free (digits); - } - } - else - abort (); -# else - /* arg is finite. */ - if (!(arg == 0.0L)) - abort (); - - pad_ptr = p; - - if (dp->conversion == 'f' || dp->conversion == 'F') - { - *p++ = '0'; - if ((flags & FLAG_ALT) || precision > 0) - { - *p++ = decimal_point_char (); - for (; precision > 0; precision--) - *p++ = '0'; - } - } - else if (dp->conversion == 'e' || dp->conversion == 'E') - { - *p++ = '0'; - if ((flags & FLAG_ALT) || precision > 0) - { - *p++ = decimal_point_char (); - for (; precision > 0; precision--) - *p++ = '0'; - } - *p++ = dp->conversion; /* 'e' or 'E' */ - *p++ = '+'; - *p++ = '0'; - *p++ = '0'; - } - else if (dp->conversion == 'g' || dp->conversion == 'G') - { - *p++ = '0'; - if (flags & FLAG_ALT) - { - size_t ndigits = - (precision > 0 ? precision - 1 : 0); - *p++ = decimal_point_char (); - for (; ndigits > 0; --ndigits) - *p++ = '0'; - } - } - else if (dp->conversion == 'a' || dp->conversion == 'A') - { - *p++ = '0'; - *p++ = dp->conversion - 'A' + 'X'; - pad_ptr = p; - *p++ = '0'; - if ((flags & FLAG_ALT) || precision > 0) - { - *p++ = decimal_point_char (); - for (; precision > 0; precision--) - *p++ = '0'; - } - *p++ = dp->conversion - 'A' + 'P'; - *p++ = '+'; - *p++ = '0'; - } - else - abort (); -# endif - } - - END_LONG_DOUBLE_ROUNDING (); - } - } -# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE - else -# endif -# endif -# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE - { - double arg = a.arg[dp->arg_index].a.a_double; - - if (isnand (arg)) - { - if (dp->conversion >= 'A' && dp->conversion <= 'Z') - { - *p++ = 'N'; *p++ = 'A'; *p++ = 'N'; - } - else - { - *p++ = 'n'; *p++ = 'a'; *p++ = 'n'; - } - } - else - { - int sign = 0; - - if (signbit (arg)) /* arg < 0.0 or negative zero */ - { - sign = -1; - arg = -arg; - } - - if (sign < 0) - *p++ = '-'; - else if (flags & FLAG_SHOWSIGN) - *p++ = '+'; - else if (flags & FLAG_SPACE) - *p++ = ' '; - - if (arg > 0.0 && arg + arg == arg) - { - if (dp->conversion >= 'A' && dp->conversion <= 'Z') - { - *p++ = 'I'; *p++ = 'N'; *p++ = 'F'; - } - else - { - *p++ = 'i'; *p++ = 'n'; *p++ = 'f'; - } - } - else - { -# if NEED_PRINTF_DOUBLE - pad_ptr = p; - - if (dp->conversion == 'f' || dp->conversion == 'F') - { - char *digits; - size_t ndigits; - - digits = - scale10_round_decimal_double (arg, precision); - if (digits == NULL) - goto out_of_memory; - ndigits = strlen (digits); - - if (ndigits > precision) - do - { - --ndigits; - *p++ = digits[ndigits]; - } - while (ndigits > precision); - else - *p++ = '0'; - /* Here ndigits <= precision. */ - if ((flags & FLAG_ALT) || precision > 0) - { - *p++ = decimal_point_char (); - for (; precision > ndigits; precision--) - *p++ = '0'; - while (ndigits > 0) - { - --ndigits; - *p++ = digits[ndigits]; - } - } - - free (digits); - } - else if (dp->conversion == 'e' || dp->conversion == 'E') - { - int exponent; - - if (arg == 0.0) - { - exponent = 0; - *p++ = '0'; - if ((flags & FLAG_ALT) || precision > 0) - { - *p++ = decimal_point_char (); - for (; precision > 0; precision--) - *p++ = '0'; - } - } - else - { - /* arg > 0.0. */ - int adjusted; - char *digits; - size_t ndigits; - - exponent = floorlog10 (arg); - adjusted = 0; - for (;;) - { - digits = - scale10_round_decimal_double (arg, - (int)precision - exponent); - if (digits == NULL) - goto out_of_memory; - ndigits = strlen (digits); - - if (ndigits == precision + 1) - break; - if (ndigits < precision - || ndigits > precision + 2) - /* The exponent was not guessed - precisely enough. */ - abort (); - if (adjusted) - /* None of two values of exponent is - the right one. Prevent an endless - loop. */ - abort (); - free (digits); - if (ndigits == precision) - exponent -= 1; - else - exponent += 1; - adjusted = 1; - } - /* Here ndigits = precision+1. */ - if (is_borderline (digits, precision)) - { - /* Maybe the exponent guess was too high - and a smaller exponent can be reached - by turning a 10...0 into 9...9x. */ - char *digits2 = - scale10_round_decimal_double (arg, - (int)precision - exponent + 1); - if (digits2 == NULL) - { - free (digits); - goto out_of_memory; - } - if (strlen (digits2) == precision + 1) - { - free (digits); - digits = digits2; - exponent -= 1; - } - else - free (digits2); - } - /* Here ndigits = precision+1. */ - - *p++ = digits[--ndigits]; - if ((flags & FLAG_ALT) || precision > 0) - { - *p++ = decimal_point_char (); - while (ndigits > 0) - { - --ndigits; - *p++ = digits[ndigits]; - } - } - - free (digits); - } - - *p++ = dp->conversion; /* 'e' or 'E' */ -# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR - { - static const wchar_t decimal_format[] = - /* Produce the same number of exponent digits - as the native printf implementation. */ -# if defined _WIN32 && ! defined __CYGWIN__ - { '%', '+', '.', '3', 'd', '\0' }; -# else - { '%', '+', '.', '2', 'd', '\0' }; -# endif - SNPRINTF (p, 6 + 1, decimal_format, exponent); - } - while (*p != '\0') - p++; -# else - { - static const char decimal_format[] = - /* Produce the same number of exponent digits - as the native printf implementation. */ -# if defined _WIN32 && ! defined __CYGWIN__ - "%+.3d"; -# else - "%+.2d"; -# endif - if (sizeof (DCHAR_T) == 1) - { - sprintf ((char *) p, decimal_format, exponent); - while (*p != '\0') - p++; - } - else - { - char expbuf[6 + 1]; - const char *ep; - sprintf (expbuf, decimal_format, exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) - p++; - } - } -# endif - } - else if (dp->conversion == 'g' || dp->conversion == 'G') - { - if (precision == 0) - precision = 1; - /* precision >= 1. */ - - if (arg == 0.0) - /* The exponent is 0, >= -4, < precision. - Use fixed-point notation. */ - { - size_t ndigits = precision; - /* Number of trailing zeroes that have to be - dropped. */ - size_t nzeroes = - (flags & FLAG_ALT ? 0 : precision - 1); - - --ndigits; - *p++ = '0'; - if ((flags & FLAG_ALT) || ndigits > nzeroes) - { - *p++ = decimal_point_char (); - while (ndigits > nzeroes) - { - --ndigits; - *p++ = '0'; - } - } - } - else - { - /* arg > 0.0. */ - int exponent; - int adjusted; - char *digits; - size_t ndigits; - size_t nzeroes; - - exponent = floorlog10 (arg); - adjusted = 0; - for (;;) - { - digits = - scale10_round_decimal_double (arg, - (int)(precision - 1) - exponent); - if (digits == NULL) - goto out_of_memory; - ndigits = strlen (digits); - - if (ndigits == precision) - break; - if (ndigits < precision - 1 - || ndigits > precision + 1) - /* The exponent was not guessed - precisely enough. */ - abort (); - if (adjusted) - /* None of two values of exponent is - the right one. Prevent an endless - loop. */ - abort (); - free (digits); - if (ndigits < precision) - exponent -= 1; - else - exponent += 1; - adjusted = 1; - } - /* Here ndigits = precision. */ - if (is_borderline (digits, precision - 1)) - { - /* Maybe the exponent guess was too high - and a smaller exponent can be reached - by turning a 10...0 into 9...9x. */ - char *digits2 = - scale10_round_decimal_double (arg, - (int)(precision - 1) - exponent + 1); - if (digits2 == NULL) - { - free (digits); - goto out_of_memory; - } - if (strlen (digits2) == precision) - { - free (digits); - digits = digits2; - exponent -= 1; - } - else - free (digits2); - } - /* Here ndigits = precision. */ - - /* Determine the number of trailing zeroes - that have to be dropped. */ - nzeroes = 0; - if ((flags & FLAG_ALT) == 0) - while (nzeroes < ndigits - && digits[nzeroes] == '0') - nzeroes++; - - /* The exponent is now determined. */ - if (exponent >= -4 - && exponent < (long)precision) - { - /* Fixed-point notation: - max(exponent,0)+1 digits, then the - decimal point, then the remaining - digits without trailing zeroes. */ - if (exponent >= 0) - { - size_t ecount = exponent + 1; - /* Note: ecount <= precision = ndigits. */ - for (; ecount > 0; ecount--) - *p++ = digits[--ndigits]; - if ((flags & FLAG_ALT) || ndigits > nzeroes) - { - *p++ = decimal_point_char (); - while (ndigits > nzeroes) - { - --ndigits; - *p++ = digits[ndigits]; - } - } - } - else - { - size_t ecount = -exponent - 1; - *p++ = '0'; - *p++ = decimal_point_char (); - for (; ecount > 0; ecount--) - *p++ = '0'; - while (ndigits > nzeroes) - { - --ndigits; - *p++ = digits[ndigits]; - } - } - } - else - { - /* Exponential notation. */ - *p++ = digits[--ndigits]; - if ((flags & FLAG_ALT) || ndigits > nzeroes) - { - *p++ = decimal_point_char (); - while (ndigits > nzeroes) - { - --ndigits; - *p++ = digits[ndigits]; - } - } - *p++ = dp->conversion - 'G' + 'E'; /* 'e' or 'E' */ -# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR - { - static const wchar_t decimal_format[] = - /* Produce the same number of exponent digits - as the native printf implementation. */ -# if defined _WIN32 && ! defined __CYGWIN__ - { '%', '+', '.', '3', 'd', '\0' }; -# else - { '%', '+', '.', '2', 'd', '\0' }; -# endif - SNPRINTF (p, 6 + 1, decimal_format, exponent); - } - while (*p != '\0') - p++; -# else - { - static const char decimal_format[] = - /* Produce the same number of exponent digits - as the native printf implementation. */ -# if defined _WIN32 && ! defined __CYGWIN__ - "%+.3d"; -# else - "%+.2d"; -# endif - if (sizeof (DCHAR_T) == 1) - { - sprintf ((char *) p, decimal_format, exponent); - while (*p != '\0') - p++; - } - else - { - char expbuf[6 + 1]; - const char *ep; - sprintf (expbuf, decimal_format, exponent); - for (ep = expbuf; (*p = *ep) != '\0'; ep++) - p++; - } - } -# endif - } - - free (digits); - } - } - else - abort (); -# else - /* arg is finite. */ - if (!(arg == 0.0)) - abort (); - - pad_ptr = p; - - if (dp->conversion == 'f' || dp->conversion == 'F') - { - *p++ = '0'; - if ((flags & FLAG_ALT) || precision > 0) - { - *p++ = decimal_point_char (); - for (; precision > 0; precision--) - *p++ = '0'; - } - } - else if (dp->conversion == 'e' || dp->conversion == 'E') - { - *p++ = '0'; - if ((flags & FLAG_ALT) || precision > 0) - { - *p++ = decimal_point_char (); - for (; precision > 0; precision--) - *p++ = '0'; - } - *p++ = dp->conversion; /* 'e' or 'E' */ - *p++ = '+'; - /* Produce the same number of exponent digits as - the native printf implementation. */ -# if defined _WIN32 && ! defined __CYGWIN__ - *p++ = '0'; -# endif - *p++ = '0'; - *p++ = '0'; - } - else if (dp->conversion == 'g' || dp->conversion == 'G') - { - *p++ = '0'; - if (flags & FLAG_ALT) - { - size_t ndigits = - (precision > 0 ? precision - 1 : 0); - *p++ = decimal_point_char (); - for (; ndigits > 0; --ndigits) - *p++ = '0'; - } - } - else - abort (); -# endif - } - } - } -# endif - - /* The generated string now extends from tmp to p, with the - zero padding insertion point being at pad_ptr. */ - count = p - tmp; - - if (count < width) - { - size_t pad = width - count; - DCHAR_T *end = p + pad; - - if (flags & FLAG_LEFT) - { - /* Pad with spaces on the right. */ - for (; pad > 0; pad--) - *p++ = ' '; - } - else if ((flags & FLAG_ZERO) && pad_ptr != NULL) - { - /* Pad with zeroes. */ - DCHAR_T *q = end; - - while (p > pad_ptr) - *--q = *--p; - for (; pad > 0; pad--) - *p++ = '0'; - } - else - { - /* Pad with spaces on the left. */ - DCHAR_T *q = end; - - while (p > tmp) - *--q = *--p; - for (; pad > 0; pad--) - *p++ = ' '; - } - - p = end; - } - - count = p - tmp; - - if (count >= tmp_length) - /* tmp_length was incorrectly calculated - fix the - code above! */ - abort (); - - /* Make room for the result. */ - if (count >= allocated - length) - { - size_t n = xsum (length, count); - - ENSURE_ALLOCATION (n); - } - - /* Append the result. */ - memcpy (result + length, tmp, count * sizeof (DCHAR_T)); - if (tmp != tmpbuf) - free (tmp); - length += count; - } -#endif - else - { - arg_type type = a.arg[dp->arg_index].type; - int flags = dp->flags; -#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - int has_width; -#endif -#if !USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - size_t width; -#endif -#if !USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - int has_precision; - size_t precision; -#endif -#if NEED_PRINTF_UNBOUNDED_PRECISION - int prec_ourselves; -#else -# define prec_ourselves 0 -#endif -#if (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST -# define pad_ourselves 1 -#elif !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - int pad_ourselves; -#else -# define pad_ourselves 0 -#endif - TCHAR_T *fbp; - unsigned int prefix_count; - int prefixes[2] IF_LINT (= { 0 }); - int orig_errno; -#if !USE_SNPRINTF - size_t tmp_length; - TCHAR_T tmpbuf[700]; - TCHAR_T *tmp; -#endif - -#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - has_width = 0; -#endif -#if !USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - width = 0; - if (dp->width_start != dp->width_end) - { - if (dp->width_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->width_arg_index].a.a_int; - width = arg; - if (arg < 0) - { - /* "A negative field width is taken as a '-' flag - followed by a positive field width." */ - flags |= FLAG_LEFT; - width = -width; - } - } - else - { - const FCHAR_T *digitp = dp->width_start; - - do - width = xsum (xtimes (width, 10), *digitp++ - '0'); - while (digitp != dp->width_end); - } -# if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - has_width = 1; -# endif - } -#endif - -#if !USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - has_precision = 0; - precision = 6; - if (dp->precision_start != dp->precision_end) - { - if (dp->precision_arg_index != ARG_NONE) - { - int arg; - - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; - /* "A negative precision is taken as if the precision - were omitted." */ - if (arg >= 0) - { - precision = arg; - has_precision = 1; - } - } - else - { - const FCHAR_T *digitp = dp->precision_start + 1; - - precision = 0; - while (digitp != dp->precision_end) - precision = xsum (xtimes (precision, 10), *digitp++ - '0'); - has_precision = 1; - } - } -#endif - - /* Decide whether to handle the precision ourselves. */ -#if NEED_PRINTF_UNBOUNDED_PRECISION - switch (dp->conversion) - { - case 'd': case 'i': case 'u': - case 'b': - #if SUPPORT_GNU_PRINTF_DIRECTIVES \ - || (__GLIBC__ + (__GLIBC_MINOR__ >= 35) > 2) - case 'B': - #endif - case 'o': - case 'x': case 'X': case 'p': - prec_ourselves = has_precision && (precision > 0); - break; - default: - prec_ourselves = 0; - break; - } -#endif - - /* Decide whether to perform the padding ourselves. */ -#if !((WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST) && (!DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION) - switch (dp->conversion) - { -# if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO - /* If we need conversion from TCHAR_T[] to DCHAR_T[], we need - to perform the padding after this conversion. Functions - with unistdio extensions perform the padding based on - character count rather than element count. */ - case 'c': case 's': -# endif -# if NEED_PRINTF_FLAG_ZERO - case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': - case 'a': case 'A': -# endif - pad_ourselves = 1; - break; - default: - pad_ourselves = prec_ourselves; - break; - } -#endif - -#if !USE_SNPRINTF - /* Allocate a temporary buffer of sufficient size for calling - sprintf. */ - tmp_length = - MAX_ROOM_NEEDED (&a, dp->arg_index, dp->conversion, type, - flags, width, has_precision, precision, - pad_ourselves); - - if (tmp_length <= sizeof (tmpbuf) / sizeof (TCHAR_T)) - tmp = tmpbuf; - else - { - size_t tmp_memsize = xtimes (tmp_length, sizeof (TCHAR_T)); - - if (size_overflow_p (tmp_memsize)) - /* Overflow, would lead to out of memory. */ - goto out_of_memory; - tmp = (TCHAR_T *) malloc (tmp_memsize); - if (tmp == NULL) - /* Out of memory. */ - goto out_of_memory; - } -#endif - - /* Construct the format string for calling snprintf or - sprintf. */ - fbp = buf; - *fbp++ = '%'; -#if NEED_PRINTF_FLAG_GROUPING - /* The underlying implementation doesn't support the ' flag. - Produce no grouping characters in this case; this is - acceptable because the grouping is locale dependent. */ -#else - if (flags & FLAG_GROUP) - *fbp++ = '\''; -#endif - if (flags & FLAG_LEFT) - *fbp++ = '-'; - if (flags & FLAG_SHOWSIGN) - *fbp++ = '+'; - if (flags & FLAG_SPACE) - *fbp++ = ' '; - if (flags & FLAG_ALT) - *fbp++ = '#'; -#if __GLIBC__ >= 2 && !defined __UCLIBC__ - if (flags & FLAG_LOCALIZED) - *fbp++ = 'I'; -#endif - if (!pad_ourselves) - { - if (flags & FLAG_ZERO) - *fbp++ = '0'; - if (dp->width_start != dp->width_end) - { - size_t n = dp->width_end - dp->width_start; - /* The width specification is known to consist only - of standard ASCII characters. */ - if (sizeof (FCHAR_T) == sizeof (TCHAR_T)) - { - memcpy (fbp, dp->width_start, n * sizeof (TCHAR_T)); - fbp += n; - } - else - { - const FCHAR_T *mp = dp->width_start; - do - *fbp++ = *mp++; - while (--n > 0); - } - } - } - if (!prec_ourselves) - { - if (dp->precision_start != dp->precision_end) - { - size_t n = dp->precision_end - dp->precision_start; - /* The precision specification is known to consist only - of standard ASCII characters. */ - if (sizeof (FCHAR_T) == sizeof (TCHAR_T)) - { - memcpy (fbp, dp->precision_start, n * sizeof (TCHAR_T)); - fbp += n; - } - else - { - const FCHAR_T *mp = dp->precision_start; - do - *fbp++ = *mp++; - while (--n > 0); - } - } - } - - switch (type) - { - case TYPE_LONGLONGINT: - case TYPE_ULONGLONGINT: - #if INT8_WIDTH > LONG_WIDTH - case TYPE_INT8_T: - #endif - #if UINT8_WIDTH > LONG_WIDTH - case TYPE_UINT8_T: - #endif - #if INT16_WIDTH > LONG_WIDTH - case TYPE_INT16_T: - #endif - #if UINT16_WIDTH > LONG_WIDTH - case TYPE_UINT16_T: - #endif - #if INT32_WIDTH > LONG_WIDTH - case TYPE_INT32_T: - #endif - #if UINT32_WIDTH > LONG_WIDTH - case TYPE_UINT32_T: - #endif - #if INT64_WIDTH > LONG_WIDTH - case TYPE_INT64_T: - #endif - #if UINT64_WIDTH > LONG_WIDTH - case TYPE_UINT64_T: - #endif - #if INT_FAST8_WIDTH > LONG_WIDTH - case TYPE_INT_FAST8_T: - #endif - #if UINT_FAST8_WIDTH > LONG_WIDTH - case TYPE_UINT_FAST8_T: - #endif - #if INT_FAST16_WIDTH > LONG_WIDTH - case TYPE_INT_FAST16_T: - #endif - #if UINT_FAST16_WIDTH > LONG_WIDTH - case TYPE_UINT_FAST16_T: - #endif - #if INT_FAST32_WIDTH > LONG_WIDTH - case TYPE_INT3_FAST2_T: - #endif - #if UINT_FAST32_WIDTH > LONG_WIDTH - case TYPE_UINT_FAST32_T: - #endif - #if INT_FAST64_WIDTH > LONG_WIDTH - case TYPE_INT_FAST64_T: - #endif - #if UINT_FAST64_WIDTH > LONG_WIDTH - case TYPE_UINT_FAST64_T: - #endif -#if defined _WIN32 && ! defined __CYGWIN__ - *fbp++ = 'I'; - *fbp++ = '6'; - *fbp++ = '4'; - break; -#else - *fbp++ = 'l'; -#endif - FALLTHROUGH; - case TYPE_LONGINT: - case TYPE_ULONGINT: - #if INT8_WIDTH > INT_WIDTH && INT8_WIDTH <= LONG_WIDTH - case TYPE_INT8_T: - #endif - #if UINT8_WIDTH > INT_WIDTH && UINT8_WIDTH <= LONG_WIDTH - case TYPE_UINT8_T: - #endif - #if INT16_WIDTH > INT_WIDTH && INT16_WIDTH <= LONG_WIDTH - case TYPE_INT16_T: - #endif - #if UINT16_WIDTH > INT_WIDTH && UINT16_WIDTH <= LONG_WIDTH - case TYPE_UINT16_T: - #endif - #if INT32_WIDTH > INT_WIDTH && INT32_WIDTH <= LONG_WIDTH - case TYPE_INT32_T: - #endif - #if UINT32_WIDTH > INT_WIDTH && UINT32_WIDTH <= LONG_WIDTH - case TYPE_UINT32_T: - #endif - #if INT64_WIDTH > INT_WIDTH && INT64_WIDTH <= LONG_WIDTH - case TYPE_INT64_T: - #endif - #if UINT64_WIDTH > INT_WIDTH && UINT64_WIDTH <= LONG_WIDTH - case TYPE_UINT64_T: - #endif - #if INT_FAST8_WIDTH > INT_WIDTH && INT_FAST8_WIDTH <= LONG_WIDTH - case TYPE_INT_FAST8_T: - #endif - #if UINT_FAST8_WIDTH > INT_WIDTH && UINT_FAST8_WIDTH <= LONG_WIDTH - case TYPE_UINT_FAST8_T: - #endif - #if INT_FAST16_WIDTH > INT_WIDTH && INT_FAST16_WIDTH <= LONG_WIDTH - case TYPE_INT_FAST16_T: - #endif - #if UINT_FAST16_WIDTH > INT_WIDTH && UINT_FAST16_WIDTH <= LONG_WIDTH - case TYPE_UINT_FAST16_T: - #endif - #if INT_FAST32_WIDTH > INT_WIDTH && INT_FAST32_WIDTH <= LONG_WIDTH - case TYPE_INT_FAST32_T: - #endif - #if UINT_FAST32_WIDTH > INT_WIDTH && UINT_FAST32_WIDTH <= LONG_WIDTH - case TYPE_UINT_FAST32_T: - #endif - #if INT_FAST64_WIDTH > INT_WIDTH && INT_FAST64_WIDTH <= LONG_WIDTH - case TYPE_INT_FAST64_T: - #endif - #if UINT_FAST64_WIDTH > INT_WIDTH && UINT_FAST64_WIDTH <= LONG_WIDTH - case TYPE_UINT_FAST64_T: - #endif - #if HAVE_WINT_T - case TYPE_WIDE_CHAR: - #endif - #if HAVE_WCHAR_T - case TYPE_WIDE_STRING: - #endif - *fbp++ = 'l'; - break; - case TYPE_LONGDOUBLE: - *fbp++ = 'L'; - break; - default: - break; - } -#if NEED_PRINTF_DIRECTIVE_F - if (dp->conversion == 'F') - *fbp = 'f'; - else -#endif - *fbp = dp->conversion; -#if USE_SNPRINTF - /* Decide whether to pass %n in the format string - to SNPRINTF. */ -# if (((!WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR) \ - && (HAVE_SNPRINTF_RETVAL_C99 && HAVE_SNPRINTF_TRUNCATION_C99)) \ - || ((__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) \ - && !defined __UCLIBC__) \ - || (defined __APPLE__ && defined __MACH__) \ - || defined __OpenBSD__ \ - || defined __ANDROID__ \ - || (defined _WIN32 && ! defined __CYGWIN__)) \ - || (WIDE_CHAR_VERSION && MUSL_LIBC) - /* We can avoid passing %n and instead rely on SNPRINTF's - return value if - - !WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR, because otherwise, - when WIDE_CHAR_VERSION && DCHAR_IS_TCHAR, - snwprintf()/_snwprintf() (Windows) and swprintf() (Unix) - don't return the needed buffer size, - and - - we're compiling for a system where we know - - that snprintf's return value conforms to ISO C 99 - (HAVE_SNPRINTF_RETVAL_C99) and - - that snprintf always produces NUL-terminated strings - (HAVE_SNPRINTF_TRUNCATION_C99). - And it is desirable to do so, because more and more platforms - no longer support %n, for "security reasons". */ - /* On specific platforms, listed below, we *must* avoid %n. - In the case - !WIDE_CHAR_VERSION && HAVE_SNPRINTF_RETVAL_C99 && !USE_MSVC__SNPRINTF - we can rely on the return value of snprintf instead. Whereas - in the opposite case - WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF - we need to make room based on an estimation, computed by - MAX_ROOM_NEEDED. */ - /* The following platforms forbid %n: - - On glibc2 systems from 2004-10-18 or newer, the use of - %n in format strings in writable memory may crash the - program (if compiled with _FORTIFY_SOURCE=2). - - On macOS 10.13 or newer, the use of %n in format - strings in writable memory by default crashes the - program. - - On OpenBSD, since 2021-08-30, the use of %n in format - strings produces an abort (see - , - ). - - On Android, starting on 2018-03-07, the use of %n in - format strings produces a fatal error (see - ). - - On native Windows systems (such as mingw) where the OS is - Windows Vista, the use of %n in format strings by default - crashes the program. See - and - - On the first four of these platforms, if !WIDE_CHAR_VERSION, - it is not a big deal to avoid %n, because on these platforms, - HAVE_SNPRINTF_RETVAL_C99 and HAVE_SNPRINTF_TRUNCATION_C99 are - 1. - On native Windows, if !WIDE_CHAR_VERSION, it's not a big deal - either because: - - Although the gl_SNPRINTF_TRUNCATION_C99 test fails, - snprintf does not write more than the specified number - of bytes. (snprintf (buf, 3, "%d %d", 4567, 89) writes - '4', '5', '6' into buf, not '4', '5', '\0'.) - - Although the gl_SNPRINTF_RETVAL_C99 test fails, snprintf - allows us to recognize the case of an insufficient - buffer size: it returns -1 in this case. */ - /* Additionally, in the WIDE_CHAR_VERSION case, we cannot use %n - on musl libc because we would run into an swprintf() bug. - See . */ - fbp[1] = '\0'; -# else /* AIX <= 5.1, HP-UX, IRIX, OSF/1, Solaris <= 9, BeOS */ - fbp[1] = '%'; - fbp[2] = 'n'; - fbp[3] = '\0'; -# endif -#else - fbp[1] = '\0'; -#endif - - /* Construct the arguments for calling snprintf or sprintf. */ - prefix_count = 0; - if (!pad_ourselves && dp->width_arg_index != ARG_NONE) - { - if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) - abort (); - prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int; - } - if (!prec_ourselves && dp->precision_arg_index != ARG_NONE) - { - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) - abort (); - prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int; - } - -#if USE_SNPRINTF - /* The SNPRINTF result is appended after result[0..length]. - The latter is an array of DCHAR_T; SNPRINTF appends an - array of TCHAR_T to it. This is possible because - sizeof (TCHAR_T) divides sizeof (DCHAR_T) and - alignof (TCHAR_T) <= alignof (DCHAR_T). */ -# define TCHARS_PER_DCHAR (sizeof (DCHAR_T) / sizeof (TCHAR_T)) - /* Ensure that maxlen below will be >= 2. Needed on BeOS, - where an snprintf() with maxlen==1 acts like sprintf(). */ - ENSURE_ALLOCATION (xsum (length, - (2 + TCHARS_PER_DCHAR - 1) - / TCHARS_PER_DCHAR)); - /* Prepare checking whether snprintf returns the count - via %n. */ - *(TCHAR_T *) (result + length) = '\0'; -#endif - - orig_errno = errno; - - for (;;) - { - int count = -1; - -#if USE_SNPRINTF - int retcount = 0; - size_t maxlen = allocated - length; - /* SNPRINTF can fail if its second argument is - > INT_MAX. */ - if (maxlen > INT_MAX / TCHARS_PER_DCHAR) - maxlen = INT_MAX / TCHARS_PER_DCHAR; - maxlen = maxlen * TCHARS_PER_DCHAR; -# define SNPRINTF_BUF(arg) \ - switch (prefix_count) \ - { \ - case 0: \ - retcount = SNPRINTF ((TCHAR_T *) (result + length), \ - maxlen, buf, \ - arg, &count); \ - break; \ - case 1: \ - retcount = SNPRINTF ((TCHAR_T *) (result + length), \ - maxlen, buf, \ - prefixes[0], arg, &count); \ - break; \ - case 2: \ - retcount = SNPRINTF ((TCHAR_T *) (result + length), \ - maxlen, buf, \ - prefixes[0], prefixes[1], arg, \ - &count); \ - break; \ - default: \ - abort (); \ - } -#else -# define SNPRINTF_BUF(arg) \ - switch (prefix_count) \ - { \ - case 0: \ - count = sprintf (tmp, buf, arg); \ - break; \ - case 1: \ - count = sprintf (tmp, buf, prefixes[0], arg); \ - break; \ - case 2: \ - count = sprintf (tmp, buf, prefixes[0], prefixes[1],\ - arg); \ - break; \ - default: \ - abort (); \ - } -#endif - - errno = 0; - switch (type) - { - case TYPE_SCHAR: - { - int arg = a.arg[dp->arg_index].a.a_schar; - SNPRINTF_BUF (arg); - } - break; - case TYPE_UCHAR: - { - unsigned int arg = a.arg[dp->arg_index].a.a_uchar; - SNPRINTF_BUF (arg); - } - break; - case TYPE_SHORT: - { - int arg = a.arg[dp->arg_index].a.a_short; - SNPRINTF_BUF (arg); - } - break; - case TYPE_USHORT: - { - unsigned int arg = a.arg[dp->arg_index].a.a_ushort; - SNPRINTF_BUF (arg); - } - break; - case TYPE_INT: - { - int arg = a.arg[dp->arg_index].a.a_int; - SNPRINTF_BUF (arg); - } - break; - case TYPE_UINT: - { - unsigned int arg = a.arg[dp->arg_index].a.a_uint; - SNPRINTF_BUF (arg); - } - break; - case TYPE_LONGINT: - { - long int arg = a.arg[dp->arg_index].a.a_longint; - SNPRINTF_BUF (arg); - } - break; - case TYPE_ULONGINT: - { - unsigned long int arg = a.arg[dp->arg_index].a.a_ulongint; - SNPRINTF_BUF (arg); - } - break; - case TYPE_LONGLONGINT: - { - long long int arg = a.arg[dp->arg_index].a.a_longlongint; - SNPRINTF_BUF (arg); - } - break; - case TYPE_ULONGLONGINT: - { - unsigned long long int arg = a.arg[dp->arg_index].a.a_ulonglongint; - SNPRINTF_BUF (arg); - } - break; - case TYPE_INT8_T: - { - int8_t arg = a.arg[dp->arg_index].a.a_int8_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_UINT8_T: - { - uint8_t arg = a.arg[dp->arg_index].a.a_uint8_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_INT16_T: - { - int16_t arg = a.arg[dp->arg_index].a.a_int16_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_UINT16_T: - { - uint16_t arg = a.arg[dp->arg_index].a.a_uint16_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_INT32_T: - { - int32_t arg = a.arg[dp->arg_index].a.a_int32_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_UINT32_T: - { - uint32_t arg = a.arg[dp->arg_index].a.a_uint32_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_INT64_T: - { - int64_t arg = a.arg[dp->arg_index].a.a_int64_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_UINT64_T: - { - uint64_t arg = a.arg[dp->arg_index].a.a_uint64_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_INT_FAST8_T: - { - int_fast8_t arg = a.arg[dp->arg_index].a.a_int_fast8_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_UINT_FAST8_T: - { - uint_fast8_t arg = a.arg[dp->arg_index].a.a_uint_fast8_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_INT_FAST16_T: - { - int_fast16_t arg = a.arg[dp->arg_index].a.a_int_fast16_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_UINT_FAST16_T: - { - uint_fast16_t arg = a.arg[dp->arg_index].a.a_uint_fast16_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_INT_FAST32_T: - { - int_fast32_t arg = a.arg[dp->arg_index].a.a_int_fast32_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_UINT_FAST32_T: - { - uint_fast32_t arg = a.arg[dp->arg_index].a.a_uint_fast32_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_INT_FAST64_T: - { - int_fast64_t arg = a.arg[dp->arg_index].a.a_int_fast64_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_UINT_FAST64_T: - { - uint_fast64_t arg = a.arg[dp->arg_index].a.a_uint_fast64_t; - SNPRINTF_BUF (arg); - } - break; - case TYPE_DOUBLE: - { - double arg = a.arg[dp->arg_index].a.a_double; - SNPRINTF_BUF (arg); - } - break; - case TYPE_LONGDOUBLE: - { - long double arg = a.arg[dp->arg_index].a.a_longdouble; - SNPRINTF_BUF (arg); - } - break; - case TYPE_CHAR: - { - int arg = a.arg[dp->arg_index].a.a_char; - SNPRINTF_BUF (arg); - } - break; -#if HAVE_WINT_T - case TYPE_WIDE_CHAR: - { - wint_t arg = a.arg[dp->arg_index].a.a_wide_char; - SNPRINTF_BUF (arg); - } - break; -#endif - case TYPE_STRING: - { - const char *arg = a.arg[dp->arg_index].a.a_string; - SNPRINTF_BUF (arg); - } - break; -#if HAVE_WCHAR_T - case TYPE_WIDE_STRING: - { - const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string; - SNPRINTF_BUF (arg); - } - break; -#endif - case TYPE_POINTER: - { - void *arg = a.arg[dp->arg_index].a.a_pointer; - SNPRINTF_BUF (arg); - } - break; - default: - abort (); - } - -#if USE_SNPRINTF - /* Portability: Not all implementations of snprintf() - are ISO C 99 compliant. Determine the number of - bytes that snprintf() has produced or would have - produced. */ - if (count >= 0) - { - /* Verify that snprintf() has NUL-terminated its - result. */ - if ((unsigned int) count < maxlen - && ((TCHAR_T *) (result + length)) [count] != '\0') - abort (); - /* Portability hack. */ - if (retcount > count) - count = retcount; - } - else - { - /* snprintf() doesn't understand the '%n' - directive. */ - if (fbp[1] != '\0') - { - /* Don't use the '%n' directive; instead, look - at the snprintf() return value. */ - fbp[1] = '\0'; - continue; - } - else - { - /* Look at the snprintf() return value. */ - if (retcount < 0) - { -# if (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF - /* HP-UX 10.20 snprintf() is doubly deficient: - It doesn't understand the '%n' directive, - *and* it returns -1 (rather than the length - that would have been required) when the - buffer is too small. - Likewise, in case of - WIDE_CHAR_VERSION && DCHAR_IS_TCHAR, the - functions snwprintf()/_snwprintf() (Windows) - or swprintf() (Unix). - But a failure at this point can also come - from other reasons than a too small buffer, - such as an invalid wide string argument to - the %ls directive, or possibly an invalid - floating-point argument. */ - size_t tmp_length = - MAX_ROOM_NEEDED (&a, dp->arg_index, - dp->conversion, type, flags, - width, - has_precision, - precision, pad_ourselves); - - if (maxlen < tmp_length) - { - /* Make more room. But try to do through - this reallocation only once. */ - size_t bigger_need = - xsum (length, - xsum (tmp_length, - TCHARS_PER_DCHAR - 1) - / TCHARS_PER_DCHAR); - /* And always grow proportionally. - (There may be several arguments, each - needing a little more room than the - previous one.) */ - size_t bigger_need2 = - xsum (xtimes (allocated, 2), 12); - if (bigger_need < bigger_need2) - bigger_need = bigger_need2; - ENSURE_ALLOCATION (bigger_need); - continue; - } -# endif - } - else - { - count = retcount; -# if WIDE_CHAR_VERSION && defined __MINGW32__ - if (count == 0 && dp->conversion == 'c') - /* snwprintf returned 0 instead of 1. But it - wrote a null wide character. */ - count = 1; -# endif - } - } - } -#endif - - /* Attempt to handle failure. */ - if (count < 0) - { - /* SNPRINTF or sprintf failed. Use the errno that it - has set, if any. */ - if (errno == 0) - { - if (dp->conversion == 'c' || dp->conversion == 's') - errno = EILSEQ; - else - errno = EINVAL; - } - - goto fail_with_errno; - } - -#if USE_SNPRINTF - /* Handle overflow of the allocated buffer. - If such an overflow occurs, a C99 compliant snprintf() - returns a count >= maxlen. However, a non-compliant - snprintf() function returns only count = maxlen - 1. To - cover both cases, test whether count >= maxlen - 1. */ - if ((unsigned int) count + 1 >= maxlen) - { - /* If maxlen already has attained its allowed maximum, - allocating more memory will not increase maxlen. - Instead of looping, bail out. */ - if (maxlen == INT_MAX / TCHARS_PER_DCHAR) - goto overflow; - else - { - /* Need at least (count + 1) * sizeof (TCHAR_T) - bytes. (The +1 is for the trailing NUL.) - But ask for (count + 2) * sizeof (TCHAR_T) - bytes, so that in the next round, we likely get - maxlen > (unsigned int) count + 1 - and so we don't get here again. - And allocate proportionally, to avoid looping - eternally if snprintf() reports a too small - count. */ - size_t n = - xmax (xsum (length, - ((unsigned int) count + 2 - + TCHARS_PER_DCHAR - 1) - / TCHARS_PER_DCHAR), - xtimes (allocated, 2)); - - ENSURE_ALLOCATION (n); - continue; - } - } -#endif - -#if NEED_PRINTF_UNBOUNDED_PRECISION - if (prec_ourselves) - { - /* Handle the precision. */ - TCHAR_T *prec_ptr = -# if USE_SNPRINTF - (TCHAR_T *) (result + length); -# else - tmp; -# endif - size_t prefix_count; - size_t move; - - prefix_count = 0; - /* Put the additional zeroes after the sign. */ - if (count >= 1 - && (*prec_ptr == '-' || *prec_ptr == '+' - || *prec_ptr == ' ')) - prefix_count = 1; - /* Put the additional zeroes after the 0x prefix if - (flags & FLAG_ALT) || (dp->conversion == 'p'). */ - else if (count >= 2 - && prec_ptr[0] == '0' - && (prec_ptr[1] == 'x' || prec_ptr[1] == 'X')) - prefix_count = 2; - - move = count - prefix_count; - if (precision > move) - { - /* Insert zeroes. */ - size_t insert = precision - move; - TCHAR_T *prec_end; - -# if USE_SNPRINTF - size_t n = - xsum (length, - (count + insert + TCHARS_PER_DCHAR - 1) - / TCHARS_PER_DCHAR); - length += (count + TCHARS_PER_DCHAR - 1) / TCHARS_PER_DCHAR; - ENSURE_ALLOCATION (n); - length -= (count + TCHARS_PER_DCHAR - 1) / TCHARS_PER_DCHAR; - prec_ptr = (TCHAR_T *) (result + length); -# endif - - prec_end = prec_ptr + count; - prec_ptr += prefix_count; - - while (prec_end > prec_ptr) - { - prec_end--; - prec_end[insert] = prec_end[0]; - } - - prec_end += insert; - do - *--prec_end = '0'; - while (prec_end > prec_ptr); - - count += insert; - } - } -#endif - -#if !USE_SNPRINTF - if (count >= tmp_length) - /* tmp_length was incorrectly calculated - fix the - code above! */ - abort (); -#endif - -#if !DCHAR_IS_TCHAR - /* Convert from TCHAR_T[] to DCHAR_T[]. */ - if (dp->conversion == 'c' || dp->conversion == 's' -# if __GLIBC__ >= 2 && !defined __UCLIBC__ - || (flags & FLAG_LOCALIZED) -# endif - ) - { - /* The result string is not guaranteed to be ASCII. */ - const TCHAR_T *tmpsrc; - DCHAR_T *tmpdst; - size_t tmpdst_len; - /* This code assumes that TCHAR_T is 'char'. */ - static_assert (sizeof (TCHAR_T) == 1); -# if USE_SNPRINTF - tmpsrc = (TCHAR_T *) (result + length); -# else - tmpsrc = tmp; -# endif -# if WIDE_CHAR_VERSION - /* Convert tmpsrc[0..count-1] to a freshly allocated - wide character array. */ - mbstate_t state; - - memset (&state, '\0', sizeof (mbstate_t)); - tmpdst_len = 0; - { - const TCHAR_T *src = tmpsrc; - size_t srclen = count; - - for (; srclen > 0; tmpdst_len++) - { - /* Parse the next multibyte character. */ - size_t ret = mbrtowc (NULL, src, srclen, &state); - if (ret == (size_t)(-2) || ret == (size_t)(-1)) - goto fail_with_EILSEQ; - if (ret == 0) - ret = 1; - src += ret; - srclen -= ret; - } - } - - tmpdst = - (wchar_t *) malloc ((tmpdst_len + 1) * sizeof (wchar_t)); - if (tmpdst == NULL) - goto out_of_memory; - - memset (&state, '\0', sizeof (mbstate_t)); - { - DCHAR_T *destptr = tmpdst; - const TCHAR_T *src = tmpsrc; - size_t srclen = count; - - for (; srclen > 0; destptr++) - { - /* Parse the next multibyte character. */ - size_t ret = mbrtowc (destptr, src, srclen, &state); - if (ret == (size_t)(-2) || ret == (size_t)(-1)) - /* Should already have been caught in the first - loop, above. */ - abort (); - if (ret == 0) - ret = 1; - src += ret; - srclen -= ret; - } - } -# else - tmpdst = - DCHAR_CONV_FROM_ENCODING (locale_charset (), - iconveh_question_mark, - tmpsrc, count, - NULL, - NULL, &tmpdst_len); - if (tmpdst == NULL) - goto fail_with_errno; -# endif - ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len), - { free (tmpdst); goto out_of_memory; }); - DCHAR_CPY (result + length, tmpdst, tmpdst_len); - free (tmpdst); - count = tmpdst_len; - } - else - { - /* The result string is ASCII. - Simple 1:1 conversion. */ -# if USE_SNPRINTF - /* If sizeof (DCHAR_T) == sizeof (TCHAR_T), it's a - no-op conversion, in-place on the array starting - at (result + length). */ - if (sizeof (DCHAR_T) != sizeof (TCHAR_T)) -# endif - { - const TCHAR_T *tmpsrc; - DCHAR_T *tmpdst; - size_t n; - -# if USE_SNPRINTF - if (result == resultbuf) - { - tmpsrc = (TCHAR_T *) (result + length); - /* ENSURE_ALLOCATION will not move tmpsrc - (because it's part of resultbuf). */ - ENSURE_ALLOCATION (xsum (length, count)); - } - else - { - /* ENSURE_ALLOCATION will move the array - (because it uses realloc(). */ - ENSURE_ALLOCATION (xsum (length, count)); - tmpsrc = (TCHAR_T *) (result + length); - } -# else - tmpsrc = tmp; - ENSURE_ALLOCATION (xsum (length, count)); -# endif - tmpdst = result + length; - /* Copy backwards, because of overlapping. */ - tmpsrc += count; - tmpdst += count; - for (n = count; n > 0; n--) - *--tmpdst = *--tmpsrc; - } - } -#endif - -#if DCHAR_IS_TCHAR && !USE_SNPRINTF - /* Make room for the result. */ - if (count > allocated - length) - { - /* Need at least count elements. But allocate - proportionally. */ - size_t n = - xmax (xsum (length, count), xtimes (allocated, 2)); - - ENSURE_ALLOCATION (n); - } -#endif - - /* Here count <= allocated - length. */ - - /* Perform padding. */ -#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION - if (pad_ourselves && has_width) - { - size_t w; -# if ENABLE_UNISTDIO - /* Outside POSIX, it's preferable to compare the width - against the number of _characters_ of the converted - value. */ - w = DCHAR_MBSNLEN (result + length, count); -# else - /* The width is compared against the number of _bytes_ - of the converted value, says POSIX. */ - w = count; -# endif - if (w < width) - { - size_t pad = width - w; - - /* Make room for the result. */ - if (xsum (count, pad) > allocated - length) - { - /* Need at least count + pad elements. But - allocate proportionally. */ - size_t n = - xmax (xsum3 (length, count, pad), - xtimes (allocated, 2)); - -# if USE_SNPRINTF - length += count; - ENSURE_ALLOCATION (n); - length -= count; -# else - ENSURE_ALLOCATION (n); -# endif - } - /* Here count + pad <= allocated - length. */ - - { -# if !DCHAR_IS_TCHAR || USE_SNPRINTF - DCHAR_T * const rp = result + length; -# else - DCHAR_T * const rp = tmp; -# endif - DCHAR_T *p = rp + count; - DCHAR_T *end = p + pad; - DCHAR_T *pad_ptr; -# if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO - if (dp->conversion == 'c' - || dp->conversion == 's') - /* No zero-padding for string directives. */ - pad_ptr = NULL; - else -# endif - { - pad_ptr = (*rp == '-' ? rp + 1 : rp); - /* No zero-padding of "inf" and "nan". */ - if ((*pad_ptr >= 'A' && *pad_ptr <= 'Z') - || (*pad_ptr >= 'a' && *pad_ptr <= 'z')) - pad_ptr = NULL; - else - /* Do the zero-padding after the "0x" or - "0b" prefix, not before. */ - if (p - rp >= 2 - && *rp == '0' - && (((dp->conversion == 'a' - || dp->conversion == 'x') - && rp[1] == 'x') - || ((dp->conversion == 'A' - || dp->conversion == 'X') - && rp[1] == 'X') - || (dp->conversion == 'b' - && rp[1] == 'b') - || (dp->conversion == 'B' - && rp[1] == 'B'))) - pad_ptr += 2; - } - /* The generated string now extends from rp to p, - with the zero padding insertion point being at - pad_ptr. */ - - count = count + pad; /* = end - rp */ - - if (flags & FLAG_LEFT) - { - /* Pad with spaces on the right. */ - for (; pad > 0; pad--) - *p++ = ' '; - } - else if ((flags & FLAG_ZERO) && pad_ptr != NULL - /* ISO C says: "For d, i, o, u, x, and X - conversions, if a precision is - specified, the 0 flag is ignored. */ - && !(has_precision - && (dp->conversion == 'd' - || dp->conversion == 'i' - || dp->conversion == 'o' - || dp->conversion == 'u' - || dp->conversion == 'x' - || dp->conversion == 'X' - /* Although ISO C does not - require it, treat 'b' and 'B' - like 'x' and 'X'. */ - || dp->conversion == 'b' - || dp->conversion == 'B'))) - { - /* Pad with zeroes. */ - DCHAR_T *q = end; - - while (p > pad_ptr) - *--q = *--p; - for (; pad > 0; pad--) - *p++ = '0'; - } - else - { - /* Pad with spaces on the left. */ - DCHAR_T *q = end; - - while (p > rp) - *--q = *--p; - for (; pad > 0; pad--) - *p++ = ' '; - } - } - } - } -#endif - - /* Here still count <= allocated - length. */ - -#if !DCHAR_IS_TCHAR || USE_SNPRINTF - /* The snprintf() result did fit. */ -#else - /* Append the sprintf() result. */ - memcpy (result + length, tmp, count * sizeof (DCHAR_T)); -#endif -#if !USE_SNPRINTF - if (tmp != tmpbuf) - free (tmp); -#endif - -#if NEED_PRINTF_DIRECTIVE_F - if (dp->conversion == 'F') - { - /* Convert the %f result to upper case for %F. */ - DCHAR_T *rp = result + length; - size_t rc; - for (rc = count; rc > 0; rc--, rp++) - if (*rp >= 'a' && *rp <= 'z') - *rp = *rp - 'a' + 'A'; - } -#endif - - length += count; - break; - } - errno = orig_errno; -#undef pad_ourselves -#undef prec_ourselves - } - } - } - - /* Add the final NUL. */ - ENSURE_ALLOCATION (xsum (length, 1)); - result[length] = '\0'; - - if (result != resultbuf && length + 1 < allocated) - { - /* Shrink the allocated memory if possible. */ - DCHAR_T *memory; - - memory = (DCHAR_T *) realloc (result, (length + 1) * sizeof (DCHAR_T)); - if (memory != NULL) - result = memory; - } - - if (buf_malloced != NULL) - free (buf_malloced); - CLEANUP (); - *lengthp = length; - /* Note that we can produce a big string of a length > INT_MAX. POSIX - says that snprintf() fails with errno = EOVERFLOW in this case, but - that's only because snprintf() returns an 'int'. This function does - not have this limitation. */ - return result; - -#if USE_SNPRINTF - overflow: - errno = EOVERFLOW; - goto fail_with_errno; -#endif - - out_of_memory: - errno = ENOMEM; - goto fail_with_errno; - -#if ENABLE_UNISTDIO || ((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T && !WIDE_CHAR_VERSION) || (NEED_WPRINTF_DIRECTIVE_C && WIDE_CHAR_VERSION) - fail_with_EILSEQ: - errno = EILSEQ; - goto fail_with_errno; -#endif - - fail_with_errno: - if (result != resultbuf) - free (result); - if (buf_malloced != NULL) - free (buf_malloced); - CLEANUP (); - return NULL; - } - - out_of_memory_1: - errno = ENOMEM; - goto fail_1_with_errno; - - fail_1_with_EINVAL: - errno = EINVAL; - goto fail_1_with_errno; - - fail_1_with_errno: - CLEANUP (); - return NULL; -} - -#undef MAX_ROOM_NEEDED -#undef TCHARS_PER_DCHAR -#undef SNPRINTF -#undef USE_SNPRINTF -#undef DCHAR_SET -#undef DCHAR_CPY -#undef PRINTF_PARSE -#undef DIRECTIVES -#undef DIRECTIVE -#undef DCHAR_IS_TCHAR -#undef TCHAR_T -#undef DCHAR_T -#undef FCHAR_T -#undef VASNPRINTF diff --git a/lib/vasnprintf.h b/lib/vasnprintf.h deleted file mode 100644 index 2d134070796..00000000000 --- a/lib/vasnprintf.h +++ /dev/null @@ -1,77 +0,0 @@ -/* vsprintf with automatic memory allocation. - Copyright (C) 2002-2004, 2007-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#ifndef _VASNPRINTF_H -#define _VASNPRINTF_H - -/* This file uses _GL_ATTRIBUTE_FORMAT. */ -#if !_GL_CONFIG_H_INCLUDED - #error "Please include config.h first." -#endif - -/* Get va_list. */ -#include - -/* Get size_t. */ -#include - -/* Get _GL_ATTRIBUTE_SPEC_PRINTF_STANDARD. */ -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* Write formatted output to a string dynamically allocated with malloc(). - You can pass a preallocated buffer for the result in RESULTBUF and its - size in *LENGTHP; otherwise you pass RESULTBUF = NULL. - If successful, return the address of the string (this may be = RESULTBUF - if no dynamic memory allocation was necessary) and set *LENGTHP to the - number of resulting bytes, excluding the trailing NUL. Upon error, set - errno and return NULL. - - When dynamic memory allocation occurs, the preallocated buffer is left - alone (with possibly modified contents). This makes it possible to use - a statically allocated or stack-allocated buffer, like this: - - char buf[100]; - size_t len = sizeof (buf); - char *output = vasnprintf (buf, &len, format, args); - if (output == NULL) - ... error handling ...; - else - { - ... use the output string ...; - if (output != buf) - free (output); - } - */ -#if REPLACE_VASNPRINTF -# define asnprintf rpl_asnprintf -# define vasnprintf rpl_vasnprintf -#endif -extern char * asnprintf (char *restrict resultbuf, size_t *lengthp, - const char *format, ...) - _GL_ATTRIBUTE_FORMAT ((_GL_ATTRIBUTE_SPEC_PRINTF_STANDARD, 3, 4)); -extern char * vasnprintf (char *restrict resultbuf, size_t *lengthp, - const char *format, va_list args) - _GL_ATTRIBUTE_FORMAT ((_GL_ATTRIBUTE_SPEC_PRINTF_STANDARD, 3, 0)); - -#ifdef __cplusplus -} -#endif - -#endif /* _VASNPRINTF_H */ diff --git a/lib/vasprintf.c b/lib/vasprintf.c deleted file mode 100644 index d2878cd91d8..00000000000 --- a/lib/vasprintf.c +++ /dev/null @@ -1,50 +0,0 @@ -/* Formatted output to strings. - Copyright (C) 1999, 2002, 2006-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -/* Specification. */ -#ifdef IN_LIBASPRINTF -# include "vasprintf.h" -#else -# include -#endif - -#include -#include -#include - -#include "vasnprintf.h" - -int -vasprintf (char **resultp, const char *format, va_list args) -{ - size_t length; - char *result = vasnprintf (NULL, &length, format, args); - if (result == NULL) - return -1; - - if (length > INT_MAX) - { - free (result); - errno = EOVERFLOW; - return -1; - } - - *resultp = result; - /* Return the number of resulting bytes, excluding the trailing NUL. */ - return length; -} diff --git a/lib/vfprintf.c b/lib/vfprintf.c deleted file mode 100644 index 01d79a2beca..00000000000 --- a/lib/vfprintf.c +++ /dev/null @@ -1,70 +0,0 @@ -/* Formatted output to a stream. - Copyright (C) 2004, 2006-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation, either version 3 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -/* Specification. */ -#include - -#include -#include -#include -#include - -#include "fseterr.h" -#include "vasnprintf.h" - -/* Print formatted output to the stream FP. - Return string length of formatted string. On error, return a negative - value. */ -int -vfprintf (FILE *fp, const char *format, va_list args) -{ - char buf[2000]; - char *output; - size_t len; - size_t lenbuf = sizeof (buf); - - output = vasnprintf (buf, &lenbuf, format, args); - len = lenbuf; - - if (!output) - { - fseterr (fp); - return -1; - } - - if (fwrite (output, 1, len, fp) < len) - { - if (output != buf) - free (output); - return -1; - } - - if (output != buf) - free (output); - - if (len > INT_MAX) - { - errno = EOVERFLOW; - fseterr (fp); - return -1; - } - - return len; -} diff --git a/lib/xsize.c b/lib/xsize.c deleted file mode 100644 index 279ae824f87..00000000000 --- a/lib/xsize.c +++ /dev/null @@ -1,21 +0,0 @@ -/* Checked size_t computations. - - Copyright (C) 2012-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#include - -#define XSIZE_INLINE _GL_EXTERN_INLINE -#include "xsize.h" diff --git a/lib/xsize.h b/lib/xsize.h deleted file mode 100644 index 5b08d61f2f7..00000000000 --- a/lib/xsize.h +++ /dev/null @@ -1,110 +0,0 @@ -/* xsize.h -- Checked size_t computations. - - Copyright (C) 2003, 2008-2023 Free Software Foundation, Inc. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file 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 Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -#ifndef _XSIZE_H -#define _XSIZE_H - -/* This file uses _GL_INLINE_HEADER_BEGIN, _GL_INLINE, HAVE_STDINT_H. */ -#if !_GL_CONFIG_H_INCLUDED - #error "Please include config.h first." -#endif - -/* Get size_t. */ -#include - -/* Get SIZE_MAX. */ -#include -#if HAVE_STDINT_H -# include -#endif - -/* Get ATTRIBUTE_PURE. */ -#include "attribute.h" - -_GL_INLINE_HEADER_BEGIN -#ifndef XSIZE_INLINE -# define XSIZE_INLINE _GL_INLINE -#endif - -/* The size of memory objects is often computed through expressions of - type size_t. Example: - void* p = malloc (header_size + n * element_size). - These computations can lead to overflow. When this happens, malloc() - returns a piece of memory that is way too small, and the program then - crashes while attempting to fill the memory. - To avoid this, the functions and macros in this file check for overflow. - The convention is that SIZE_MAX represents overflow. - malloc (SIZE_MAX) is not guaranteed to fail -- think of a malloc - implementation that uses mmap --, it's recommended to use size_overflow_p() - or size_in_bounds_p() before invoking malloc(). - The example thus becomes: - size_t size = xsum (header_size, xtimes (n, element_size)); - void *p = (size_in_bounds_p (size) ? malloc (size) : NULL); -*/ - -/* Convert an arbitrary value >= 0 to type size_t. */ -#define xcast_size_t(N) \ - ((N) <= SIZE_MAX ? (size_t) (N) : SIZE_MAX) - -/* Sum of two sizes, with overflow check. */ -XSIZE_INLINE size_t ATTRIBUTE_PURE -xsum (size_t size1, size_t size2) -{ - size_t sum = size1 + size2; - return (sum >= size1 ? sum : SIZE_MAX); -} - -/* Sum of three sizes, with overflow check. */ -XSIZE_INLINE size_t ATTRIBUTE_PURE -xsum3 (size_t size1, size_t size2, size_t size3) -{ - return xsum (xsum (size1, size2), size3); -} - -/* Sum of four sizes, with overflow check. */ -XSIZE_INLINE size_t ATTRIBUTE_PURE -xsum4 (size_t size1, size_t size2, size_t size3, size_t size4) -{ - return xsum (xsum (xsum (size1, size2), size3), size4); -} - -/* Maximum of two sizes, with overflow check. */ -XSIZE_INLINE size_t ATTRIBUTE_PURE -xmax (size_t size1, size_t size2) -{ - /* No explicit check is needed here, because for any n: - max (SIZE_MAX, n) == SIZE_MAX and max (n, SIZE_MAX) == SIZE_MAX. */ - return (size1 >= size2 ? size1 : size2); -} - -/* Multiplication of a count with an element size, with overflow check. - The count must be >= 0 and the element size must be > 0. - This is a macro, not a function, so that it works correctly even - when N is of a wider type and N > SIZE_MAX. */ -#define xtimes(N, ELSIZE) \ - ((N) <= SIZE_MAX / (ELSIZE) ? (size_t) (N) * (ELSIZE) : SIZE_MAX) - -/* Check for overflow. */ -#define size_overflow_p(SIZE) \ - ((SIZE) == SIZE_MAX) -/* Check against overflow. */ -#define size_in_bounds_p(SIZE) \ - ((SIZE) != SIZE_MAX) - -_GL_INLINE_HEADER_END - -#endif /* _XSIZE_H */ diff --git a/m4/asm-underscore.m4 b/m4/asm-underscore.m4 deleted file mode 100644 index 65ae55a75fd..00000000000 --- a/m4/asm-underscore.m4 +++ /dev/null @@ -1,83 +0,0 @@ -# asm-underscore.m4 serial 5 -dnl Copyright (C) 2010-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl From Bruno Haible. Based on as-underscore.m4 in GNU clisp. - -# gl_ASM_SYMBOL_PREFIX -# Tests for the prefix of C symbols at the assembly language level and the -# linker level. This prefix is either an underscore or empty. Defines the -# C macro USER_LABEL_PREFIX to this prefix, and sets ASM_SYMBOL_PREFIX to -# a stringified variant of this prefix. - -AC_DEFUN([gl_ASM_SYMBOL_PREFIX], -[ - AC_REQUIRE([AC_PROG_EGREP]) - dnl We don't use GCC's __USER_LABEL_PREFIX__ here, because - dnl 1. It works only for GCC. - dnl 2. It is incorrectly defined on some platforms, in some GCC versions. - AC_REQUIRE([gl_C_ASM]) - AC_CACHE_CHECK( - [whether C symbols are prefixed with underscore at the linker level], - [gl_cv_prog_as_underscore], - [cat > conftest.c </dev/null 2>&1 - if LC_ALL=C $EGREP '(^|[[^a-zA-Z0-9_]])_foo([[^a-zA-Z0-9_]]|$)' conftest.$gl_asmext >/dev/null; then - gl_cv_prog_as_underscore=yes - else - gl_cv_prog_as_underscore=no - fi - rm -f conftest* - ]) - if test $gl_cv_prog_as_underscore = yes; then - USER_LABEL_PREFIX=_ - else - USER_LABEL_PREFIX= - fi - AC_DEFINE_UNQUOTED([USER_LABEL_PREFIX], [$USER_LABEL_PREFIX], - [Define to the prefix of C symbols at the assembler and linker level, - either an underscore or empty.]) - ASM_SYMBOL_PREFIX='"'${USER_LABEL_PREFIX}'"' - AC_SUBST([ASM_SYMBOL_PREFIX]) -]) - -# gl_C_ASM -# Determines how to produce an assembly language file from C source code. -# Sets the variables: -# gl_asmext - the extension of assembly language output, -# gl_c_asm_opt - the C compiler option that produces assembly language output. - -AC_DEFUN([gl_C_ASM], -[ - AC_EGREP_CPP([MicrosoftCompiler], - [ -#ifdef _MSC_VER -MicrosoftCompiler -#endif - ], - [dnl Microsoft's 'cl' and 'clang-cl' produce an .asm file, whereas 'clang' - dnl produces a .s file. Need to distinguish 'clang' and 'clang-cl'. - rm -f conftest* - echo 'int dummy;' > conftest.c - AC_TRY_COMMAND(${CC-cc} $CFLAGS $CPPFLAGS -c conftest.c) >/dev/null 2>&1 - if test -f conftest.o; then - gl_asmext='s' - gl_c_asm_opt='-S' - else - gl_asmext='asm' - gl_c_asm_opt='-c -Fa' - fi - rm -f conftest* - ], - [gl_asmext='s' - gl_c_asm_opt='-S' - ]) -]) diff --git a/m4/exponentd.m4 b/m4/exponentd.m4 deleted file mode 100644 index 163114b89ec..00000000000 --- a/m4/exponentd.m4 +++ /dev/null @@ -1,116 +0,0 @@ -# exponentd.m4 serial 4 -dnl Copyright (C) 2007-2008, 2010-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. -AC_DEFUN_ONCE([gl_DOUBLE_EXPONENT_LOCATION], -[ - AC_CACHE_CHECK([where to find the exponent in a 'double'], - [gl_cv_cc_double_expbit0], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#include -#include -#define NWORDS \ - ((sizeof (double) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) -typedef union { double value; unsigned int word[NWORDS]; } memory_double; -static unsigned int ored_words[NWORDS]; -static unsigned int anded_words[NWORDS]; -static void add_to_ored_words (double x) -{ - memory_double m; - size_t i; - /* Clear it first, in case sizeof (double) < sizeof (memory_double). */ - memset (&m, 0, sizeof (memory_double)); - m.value = x; - for (i = 0; i < NWORDS; i++) - { - ored_words[i] |= m.word[i]; - anded_words[i] &= m.word[i]; - } -} -int main () -{ - size_t j; - FILE *fp = fopen ("conftest.out", "w"); - if (fp == NULL) - return 1; - for (j = 0; j < NWORDS; j++) - anded_words[j] = ~ (unsigned int) 0; - add_to_ored_words (0.25); - add_to_ored_words (0.5); - add_to_ored_words (1.0); - add_to_ored_words (2.0); - add_to_ored_words (4.0); - /* Remove bits that are common (e.g. if representation of the first mantissa - bit is explicit). */ - for (j = 0; j < NWORDS; j++) - ored_words[j] &= ~anded_words[j]; - /* Now find the nonzero word. */ - for (j = 0; j < NWORDS; j++) - if (ored_words[j] != 0) - break; - if (j < NWORDS) - { - size_t i; - for (i = j + 1; i < NWORDS; i++) - if (ored_words[i] != 0) - { - fprintf (fp, "unknown"); - return (fclose (fp) != 0); - } - for (i = 0; ; i++) - if ((ored_words[j] >> i) & 1) - { - fprintf (fp, "word %d bit %d", (int) j, (int) i); - return (fclose (fp) != 0); - } - } - fprintf (fp, "unknown"); - return (fclose (fp) != 0); -} - ]])], - [gl_cv_cc_double_expbit0=`cat conftest.out`], - [gl_cv_cc_double_expbit0="unknown"], - [ - dnl On ARM, there are two 'double' floating-point formats, used by - dnl different sets of instructions: The older FPA instructions assume - dnl that they are stored in big-endian word order, while the words - dnl (like integer types) are stored in little-endian byte order. - dnl The newer VFP instructions assume little-endian order - dnl consistently. - AC_EGREP_CPP([mixed_endianness], [ -#if defined arm || defined __arm || defined __arm__ - mixed_endianness -#endif - ], - [gl_cv_cc_double_expbit0="unknown"], - [ - pushdef([AC_MSG_CHECKING],[:])dnl - pushdef([AC_MSG_RESULT],[:])dnl - pushdef([AC_MSG_RESULT_UNQUOTED],[:])dnl - AC_C_BIGENDIAN( - [gl_cv_cc_double_expbit0="word 0 bit 20"], - [gl_cv_cc_double_expbit0="word 1 bit 20"], - [gl_cv_cc_double_expbit0="unknown"]) - popdef([AC_MSG_RESULT_UNQUOTED])dnl - popdef([AC_MSG_RESULT])dnl - popdef([AC_MSG_CHECKING])dnl - ]) - ]) - rm -f conftest.out - ]) - case "$gl_cv_cc_double_expbit0" in - word*bit*) - word=`echo "$gl_cv_cc_double_expbit0" | sed -e 's/word //' -e 's/ bit.*//'` - bit=`echo "$gl_cv_cc_double_expbit0" | sed -e 's/word.*bit //'` - AC_DEFINE_UNQUOTED([DBL_EXPBIT0_WORD], [$word], - [Define as the word index where to find the exponent of 'double'.]) - AC_DEFINE_UNQUOTED([DBL_EXPBIT0_BIT], [$bit], - [Define as the bit index in the word where to find bit 0 of the exponent of 'double'.]) - ;; - esac -]) diff --git a/m4/exponentf.m4 b/m4/exponentf.m4 deleted file mode 100644 index e761883939b..00000000000 --- a/m4/exponentf.m4 +++ /dev/null @@ -1,92 +0,0 @@ -# exponentf.m4 serial 3 -dnl Copyright (C) 2007-2008, 2010-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. -AC_DEFUN_ONCE([gl_FLOAT_EXPONENT_LOCATION], -[ - AC_CACHE_CHECK([where to find the exponent in a 'float'], - [gl_cv_cc_float_expbit0], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#include -#include -#define NWORDS \ - ((sizeof (float) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) -typedef union { float value; unsigned int word[NWORDS]; } memory_float; -static unsigned int ored_words[NWORDS]; -static unsigned int anded_words[NWORDS]; -static void add_to_ored_words (float x) -{ - memory_float m; - size_t i; - /* Clear it first, in case - sizeof (float) < sizeof (memory_float). */ - memset (&m, 0, sizeof (memory_float)); - m.value = x; - for (i = 0; i < NWORDS; i++) - { - ored_words[i] |= m.word[i]; - anded_words[i] &= m.word[i]; - } -} -int main () -{ - size_t j; - FILE *fp = fopen ("conftest.out", "w"); - if (fp == NULL) - return 1; - for (j = 0; j < NWORDS; j++) - anded_words[j] = ~ (unsigned int) 0; - add_to_ored_words (0.25f); - add_to_ored_words (0.5f); - add_to_ored_words (1.0f); - add_to_ored_words (2.0f); - add_to_ored_words (4.0f); - /* Remove bits that are common (e.g. if representation of the first mantissa - bit is explicit). */ - for (j = 0; j < NWORDS; j++) - ored_words[j] &= ~anded_words[j]; - /* Now find the nonzero word. */ - for (j = 0; j < NWORDS; j++) - if (ored_words[j] != 0) - break; - if (j < NWORDS) - { - size_t i; - for (i = j + 1; i < NWORDS; i++) - if (ored_words[i] != 0) - { - fprintf (fp, "unknown"); - return (fclose (fp) != 0); - } - for (i = 0; ; i++) - if ((ored_words[j] >> i) & 1) - { - fprintf (fp, "word %d bit %d", (int) j, (int) i); - return (fclose (fp) != 0); - } - } - fprintf (fp, "unknown"); - return (fclose (fp) != 0); -} - ]])], - [gl_cv_cc_float_expbit0=`cat conftest.out`], - [gl_cv_cc_float_expbit0="unknown"], - [gl_cv_cc_float_expbit0="word 0 bit 23"]) - rm -f conftest.out - ]) - case "$gl_cv_cc_float_expbit0" in - word*bit*) - word=`echo "$gl_cv_cc_float_expbit0" | sed -e 's/word //' -e 's/ bit.*//'` - bit=`echo "$gl_cv_cc_float_expbit0" | sed -e 's/word.*bit //'` - AC_DEFINE_UNQUOTED([FLT_EXPBIT0_WORD], [$word], - [Define as the word index where to find the exponent of 'float'.]) - AC_DEFINE_UNQUOTED([FLT_EXPBIT0_BIT], [$bit], - [Define as the bit index in the word where to find bit 0 of the exponent of 'float'.]) - ;; - esac -]) diff --git a/m4/exponentl.m4 b/m4/exponentl.m4 deleted file mode 100644 index bc5638737e5..00000000000 --- a/m4/exponentl.m4 +++ /dev/null @@ -1,112 +0,0 @@ -# exponentl.m4 serial 6 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. -AC_DEFUN_ONCE([gl_LONG_DOUBLE_EXPONENT_LOCATION], -[ - AC_REQUIRE([gl_BIGENDIAN]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([where to find the exponent in a 'long double'], - [gl_cv_cc_long_double_expbit0], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#include -#include -#define NWORDS \ - ((sizeof (long double) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) -typedef union { long double value; unsigned int word[NWORDS]; } - memory_long_double; -static unsigned int ored_words[NWORDS]; -static unsigned int anded_words[NWORDS]; -static void add_to_ored_words (long double *x) -{ - memory_long_double m; - size_t i; - /* Clear it first, in case - sizeof (long double) < sizeof (memory_long_double). */ - memset (&m, 0, sizeof (memory_long_double)); - m.value = *x; - for (i = 0; i < NWORDS; i++) - { - ored_words[i] |= m.word[i]; - anded_words[i] &= m.word[i]; - } -} -int main () -{ - static long double samples[5] = { 0.25L, 0.5L, 1.0L, 2.0L, 4.0L }; - size_t j; - FILE *fp = fopen ("conftest.out", "w"); - if (fp == NULL) - return 1; - for (j = 0; j < NWORDS; j++) - anded_words[j] = ~ (unsigned int) 0; - for (j = 0; j < 5; j++) - add_to_ored_words (&samples[j]); - /* Remove bits that are common (e.g. if representation of the first mantissa - bit is explicit). */ - for (j = 0; j < NWORDS; j++) - ored_words[j] &= ~anded_words[j]; - /* Now find the nonzero word. */ - for (j = 0; j < NWORDS; j++) - if (ored_words[j] != 0) - break; - if (j < NWORDS) - { - size_t i; - for (i = j + 1; i < NWORDS; i++) - if (ored_words[i] != 0) - { - fprintf (fp, "unknown"); - return (fclose (fp) != 0); - } - for (i = 0; ; i++) - if ((ored_words[j] >> i) & 1) - { - fprintf (fp, "word %d bit %d", (int) j, (int) i); - return (fclose (fp) != 0); - } - } - fprintf (fp, "unknown"); - return (fclose (fp) != 0); -} - ]])], - [gl_cv_cc_long_double_expbit0=`cat conftest.out`], - [gl_cv_cc_long_double_expbit0="unknown"], - [ - dnl When cross-compiling, in general we don't know. It depends on the - dnl ABI and compiler version. There are too many cases. - gl_cv_cc_long_double_expbit0="unknown" - case "$host_os" in - mingw*) # On native Windows (little-endian), we know the result - # in two cases: mingw, MSVC. - AC_EGREP_CPP([Known], [ -#ifdef __MINGW32__ - Known -#endif - ], [gl_cv_cc_long_double_expbit0="word 2 bit 0"]) - AC_EGREP_CPP([Known], [ -#ifdef _MSC_VER - Known -#endif - ], [gl_cv_cc_long_double_expbit0="word 1 bit 20"]) - ;; - esac - ]) - rm -f conftest.out - ]) - case "$gl_cv_cc_long_double_expbit0" in - word*bit*) - word=`echo "$gl_cv_cc_long_double_expbit0" | sed -e 's/word //' -e 's/ bit.*//'` - bit=`echo "$gl_cv_cc_long_double_expbit0" | sed -e 's/word.*bit //'` - AC_DEFINE_UNQUOTED([LDBL_EXPBIT0_WORD], [$word], - [Define as the word index where to find the exponent of 'long double'.]) - AC_DEFINE_UNQUOTED([LDBL_EXPBIT0_BIT], [$bit], - [Define as the bit index in the word where to find bit 0 of the exponent of 'long double'.]) - ;; - esac -]) diff --git a/m4/float_h.m4 b/m4/float_h.m4 deleted file mode 100644 index 2f0c9c4ee9c..00000000000 --- a/m4/float_h.m4 +++ /dev/null @@ -1,106 +0,0 @@ -# float_h.m4 serial 13 -dnl Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FLOAT_H], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) - GL_GENERATE_FLOAT_H=false - REPLACE_FLOAT_LDBL=0 - case "$host_os" in - aix* | beos* | openbsd* | mirbsd* | irix*) - GL_GENERATE_FLOAT_H=true - ;; - freebsd* | dragonfly*) - case "$host_cpu" in -changequote(,)dnl - i[34567]86 ) -changequote([,])dnl - GL_GENERATE_FLOAT_H=true - ;; - x86_64 ) - # On x86_64 systems, the C compiler may still be generating - # 32-bit code. - AC_COMPILE_IFELSE( - [AC_LANG_SOURCE( - [[#if defined __LP64__ || defined __x86_64__ || defined __amd64__ - int ok; - #else - error fail - #endif - ]])], - [], - [GL_GENERATE_FLOAT_H=true]) - ;; - esac - ;; - linux*) - case "$host_cpu" in - powerpc*) - GL_GENERATE_FLOAT_H=true - ;; - esac - ;; - esac - case "$host_os" in - aix* | freebsd* | dragonfly* | linux*) - if $GL_GENERATE_FLOAT_H; then - REPLACE_FLOAT_LDBL=1 - fi - ;; - esac - - dnl Test against glibc-2.7 Linux/SPARC64 bug. - REPLACE_ITOLD=0 - AC_CACHE_CHECK([whether conversion from 'int' to 'long double' works], - [gl_cv_func_itold_works], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -int i = -1; -volatile long double ld; -int main () -{ - ld += i * 1.0L; - if (ld > 0) - return 1; - return 0; -}]])], - [gl_cv_func_itold_works=yes], - [gl_cv_func_itold_works=no], - [case "$host" in - sparc*-*-linux*) - AC_COMPILE_IFELSE( - [AC_LANG_SOURCE( - [[#if defined __LP64__ || defined __arch64__ - int ok; - #else - error fail - #endif - ]])], - [gl_cv_func_itold_works="guessing no"], - [gl_cv_func_itold_works="guessing yes"]) - ;; - # Guess yes on native Windows. - mingw*) gl_cv_func_itold_works="guessing yes" ;; - *) gl_cv_func_itold_works="guessing yes" ;; - esac - ]) - ]) - case "$gl_cv_func_itold_works" in - *no) - REPLACE_ITOLD=1 - dnl We add the workaround to but also to , - dnl to increase the chances that the fix function gets pulled in. - GL_GENERATE_FLOAT_H=true - ;; - esac - - if $GL_GENERATE_FLOAT_H; then - gl_NEXT_HEADERS([float.h]) - fi - AC_SUBST([REPLACE_ITOLD]) -]) diff --git a/m4/frexp.m4 b/m4/frexp.m4 deleted file mode 100644 index 0480d98f304..00000000000 --- a/m4/frexp.m4 +++ /dev/null @@ -1,181 +0,0 @@ -# frexp.m4 serial 16 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_FREXP], -[ - AC_REQUIRE([gl_MATH_H_DEFAULTS]) - AC_REQUIRE([gl_CHECK_FREXP_NO_LIBM]) - FREXP_LIBM= - if test $gl_cv_func_frexp_no_libm = no; then - AC_CACHE_CHECK([whether frexp() can be used with libm], - [gl_cv_func_frexp_in_libm], - [ - save_LIBS="$LIBS" - LIBS="$LIBS -lm" - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - double x;]], - [[int e; return frexp (x, &e) > 0;]])], - [gl_cv_func_frexp_in_libm=yes], - [gl_cv_func_frexp_in_libm=no]) - LIBS="$save_LIBS" - ]) - if test $gl_cv_func_frexp_in_libm = yes; then - FREXP_LIBM=-lm - fi - fi - if test $gl_cv_func_frexp_no_libm = yes \ - || test $gl_cv_func_frexp_in_libm = yes; then - save_LIBS="$LIBS" - LIBS="$LIBS $FREXP_LIBM" - gl_FUNC_FREXP_WORKS - LIBS="$save_LIBS" - case "$gl_cv_func_frexp_works" in - *yes) gl_func_frexp=yes ;; - *) gl_func_frexp=no; REPLACE_FREXP=1; FREXP_LIBM= ;; - esac - else - gl_func_frexp=no - fi - if test $gl_func_frexp = yes; then - AC_DEFINE([HAVE_FREXP], [1], - [Define if the frexp() function is available and works.]) - fi - AC_SUBST([FREXP_LIBM]) -]) - -AC_DEFUN([gl_FUNC_FREXP_NO_LIBM], -[ - AC_REQUIRE([gl_MATH_H_DEFAULTS]) - AC_REQUIRE([gl_CHECK_FREXP_NO_LIBM]) - if test $gl_cv_func_frexp_no_libm = yes; then - gl_FUNC_FREXP_WORKS - case "$gl_cv_func_frexp_works" in - *yes) gl_func_frexp_no_libm=yes ;; - *) gl_func_frexp_no_libm=no; REPLACE_FREXP=1 ;; - esac - else - gl_func_frexp_no_libm=no - dnl Set REPLACE_FREXP here because the system may have frexp in libm. - REPLACE_FREXP=1 - fi - if test $gl_func_frexp_no_libm = yes; then - AC_DEFINE([HAVE_FREXP_IN_LIBC], [1], - [Define if the frexp() function is available in libc.]) - fi -]) - -dnl Test whether frexp() can be used without linking with libm. -dnl Set gl_cv_func_frexp_no_libm to 'yes' or 'no' accordingly. -AC_DEFUN([gl_CHECK_FREXP_NO_LIBM], -[ - AC_CACHE_CHECK([whether frexp() can be used without linking with libm], - [gl_cv_func_frexp_no_libm], - [ - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - double x;]], - [[int e; return frexp (x, &e) > 0;]])], - [gl_cv_func_frexp_no_libm=yes], - [gl_cv_func_frexp_no_libm=no]) - ]) -]) - -dnl Test whether frexp() works also on denormalized numbers (this fails e.g. on -dnl NetBSD 3.0), on infinite numbers (this fails e.g. on IRIX 6.5 and mingw), -dnl and on negative zero (this fails e.g. on NetBSD 4.99 and mingw). -AC_DEFUN([gl_FUNC_FREXP_WORKS], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CHECK_DECLS_ONCE([alarm]) - AC_CACHE_CHECK([whether frexp works], [gl_cv_func_frexp_works], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#include -#if HAVE_DECL_ALARM -# include -# include -#endif -/* HP cc on HP-UX 10.20 has a bug with the constant expression -0.0. - ICC 10.0 has a bug when optimizing the expression -zero. - The expression -DBL_MIN * DBL_MIN does not work when cross-compiling - to PowerPC on Mac OS X 10.5. */ -#if defined __hpux || defined __sgi || defined __ICC -static double -compute_minus_zero (void) -{ - return -DBL_MIN * DBL_MIN; -} -# define minus_zero compute_minus_zero () -#else -double minus_zero = -0.0; -#endif -int main() -{ - int result = 0; - int i; - volatile double x; - double zero = 0.0; -#if HAVE_DECL_ALARM - /* NeXTstep 3.3 frexp() runs into an endless loop when called on an infinite - number. Let the test fail in this case. */ - signal (SIGALRM, SIG_DFL); - alarm (5); -#endif - /* Test on denormalized numbers. */ - for (i = 1, x = 1.0; i >= DBL_MIN_EXP; i--, x *= 0.5) - ; - if (x > 0.0) - { - int exp; - double y = frexp (x, &exp); - /* On machines with IEEE754 arithmetic: x = 1.11254e-308, exp = -1022. - On NetBSD: y = 0.75. Correct: y = 0.5. */ - if (y != 0.5) - result |= 1; - } - /* Test on infinite numbers. */ - x = 1.0 / zero; - { - int exp; - double y = frexp (x, &exp); - if (y != x) - result |= 2; - } - /* Test on negative zero. */ - x = minus_zero; - { - int exp; - double y = frexp (x, &exp); - if (memcmp (&y, &x, sizeof x)) - result |= 4; - } - return result; -}]])], - [gl_cv_func_frexp_works=yes], - [gl_cv_func_frexp_works=no], - [case "$host_os" in - netbsd* | irix*) gl_cv_func_frexp_works="guessing no" ;; - mingw*) # Guess yes with MSVC, no with mingw. - AC_EGREP_CPP([Good], [ -#ifdef _MSC_VER - Good -#endif - ], - [gl_cv_func_frexp_works="guessing yes"], - [gl_cv_func_frexp_works="guessing no"]) - ;; - *) gl_cv_func_frexp_works="guessing yes" ;; - esac - ]) - ]) -]) diff --git a/m4/frexpl.m4 b/m4/frexpl.m4 deleted file mode 100644 index b4cf0ca9ea1..00000000000 --- a/m4/frexpl.m4 +++ /dev/null @@ -1,233 +0,0 @@ -# frexpl.m4 serial 22 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_FREXPL], -[ - AC_REQUIRE([gl_MATH_H_DEFAULTS]) - AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE]) - - dnl Persuade glibc to declare frexpl(). - AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) - - dnl Check whether it's declared. - dnl Mac OS X 10.3 has frexpl() in libc but doesn't declare it in . - AC_CHECK_DECL([frexpl], , [HAVE_DECL_FREXPL=0], [[#include ]]) - - FREXPL_LIBM= - if test $HAVE_DECL_FREXPL = 1; then - gl_CHECK_FREXPL_NO_LIBM - if test $gl_cv_func_frexpl_no_libm = no; then - AC_CACHE_CHECK([whether frexpl() can be used with libm], - [gl_cv_func_frexpl_in_libm], - [ - save_LIBS="$LIBS" - LIBS="$LIBS -lm" - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - long double x;]], - [[int e; return frexpl (x, &e) > 0;]])], - [gl_cv_func_frexpl_in_libm=yes], - [gl_cv_func_frexpl_in_libm=no]) - LIBS="$save_LIBS" - ]) - if test $gl_cv_func_frexpl_in_libm = yes; then - FREXPL_LIBM=-lm - fi - fi - if test $gl_cv_func_frexpl_no_libm = yes \ - || test $gl_cv_func_frexpl_in_libm = yes; then - save_LIBS="$LIBS" - LIBS="$LIBS $FREXPL_LIBM" - gl_FUNC_FREXPL_WORKS - LIBS="$save_LIBS" - case "$gl_cv_func_frexpl_works" in - *yes) gl_func_frexpl=yes ;; - *) gl_func_frexpl=no; REPLACE_FREXPL=1 ;; - esac - else - gl_func_frexpl=no - fi - if test $gl_func_frexpl = yes; then - AC_DEFINE([HAVE_FREXPL], [1], - [Define if the frexpl() function is available.]) - fi - fi - if test $HAVE_DECL_FREXPL = 0 || test $gl_func_frexpl = no; then - dnl Find libraries needed to link lib/frexpl.c. - if test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 1; then - AC_REQUIRE([gl_FUNC_FREXP]) - FREXPL_LIBM="$FREXP_LIBM" - else - FREXPL_LIBM= - fi - fi - AC_SUBST([FREXPL_LIBM]) -]) - -AC_DEFUN([gl_FUNC_FREXPL_NO_LIBM], -[ - AC_REQUIRE([gl_MATH_H_DEFAULTS]) - AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE]) - dnl Check whether it's declared. - dnl Mac OS X 10.3 has frexpl() in libc but doesn't declare it in . - AC_CHECK_DECL([frexpl], , [HAVE_DECL_FREXPL=0], [[#include ]]) - if test $HAVE_DECL_FREXPL = 1; then - gl_CHECK_FREXPL_NO_LIBM - if test $gl_cv_func_frexpl_no_libm = yes; then - gl_FUNC_FREXPL_WORKS - case "$gl_cv_func_frexpl_works" in - *yes) gl_func_frexpl_no_libm=yes ;; - *) gl_func_frexpl_no_libm=no; REPLACE_FREXPL=1 ;; - esac - else - gl_func_frexpl_no_libm=no - dnl Set REPLACE_FREXPL here because the system may have frexpl in libm. - REPLACE_FREXPL=1 - fi - if test $gl_func_frexpl_no_libm = yes; then - AC_DEFINE([HAVE_FREXPL_IN_LIBC], [1], - [Define if the frexpl() function is available in libc.]) - fi - fi -]) - -dnl Test whether frexpl() can be used without linking with libm. -dnl Set gl_cv_func_frexpl_no_libm to 'yes' or 'no' accordingly. -AC_DEFUN([gl_CHECK_FREXPL_NO_LIBM], -[ - AC_CACHE_CHECK([whether frexpl() can be used without linking with libm], - [gl_cv_func_frexpl_no_libm], - [ - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - long double x;]], - [[int e; return frexpl (x, &e) > 0;]])], - [gl_cv_func_frexpl_no_libm=yes], - [gl_cv_func_frexpl_no_libm=no]) - ]) -]) - -dnl Test whether frexpl() works on finite numbers (this fails on -dnl Mac OS X 10.4/PowerPC, on AIX 5.1, and on BeOS), on denormalized numbers -dnl (this fails on Mac OS X 10.5/i386), and also on infinite numbers (this -dnl fails e.g. on IRIX 6.5 and mingw). -AC_DEFUN([gl_FUNC_FREXPL_WORKS], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether frexpl works], [gl_cv_func_frexpl_works], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -/* Override the values of , like done in float.in.h. */ -#if defined __i386__ && (defined __BEOS__ || defined __OpenBSD__) -# undef LDBL_MIN_EXP -# define LDBL_MIN_EXP (-16381) -#endif -#if defined __i386__ && (defined __FreeBSD__ || defined __DragonFly__) -# undef LDBL_MIN_EXP -# define LDBL_MIN_EXP (-16381) -#endif -#if (defined _ARCH_PPC || defined _POWER) && defined _AIX && (LDBL_MANT_DIG == 106) && defined __GNUC__ -# undef LDBL_MIN_EXP -# define LDBL_MIN_EXP DBL_MIN_EXP -#endif -#if defined __sgi && (LDBL_MANT_DIG >= 106) -# if defined __GNUC__ -# undef LDBL_MIN_EXP -# define LDBL_MIN_EXP DBL_MIN_EXP -# endif -#endif -extern -#ifdef __cplusplus -"C" -#endif -long double frexpl (long double, int *); -long double zero = 0.0L; -int main() -{ - int result = 0; - volatile long double x; - /* Test on finite numbers that fails on AIX 5.1. */ - x = 16.0L; - { - int exp = -9999; - frexpl (x, &exp); - if (exp != 5) - result |= 1; - } - /* Test on finite numbers that fails on Mac OS X 10.4, because its frexpl - function returns an invalid (incorrectly normalized) value: it returns - y = { 0x3fe028f5, 0xc28f5c28, 0x3c9eb851, 0xeb851eb8 } - but the correct result is - 0.505L = { 0x3fe028f5, 0xc28f5c29, 0xbc547ae1, 0x47ae1480 } */ - x = 1.01L; - { - int exp = -9999; - long double y = frexpl (x, &exp); - if (!(exp == 1 && y == 0.505L)) - result |= 2; - } - /* Test on large finite numbers. This fails on BeOS at i = 16322, while - LDBL_MAX_EXP = 16384. - In the loop end test, we test x against Infinity, rather than comparing - i with LDBL_MAX_EXP, because BeOS has a wrong LDBL_MAX_EXP. */ - { - int i; - for (i = 1, x = 1.0L; x != x + x; i++, x *= 2.0L) - { - int exp = -9999; - frexpl (x, &exp); - if (exp != i) - { - result |= 4; - break; - } - } - } - /* Test on denormalized numbers. */ - { - int i; - for (i = 1, x = 1.0L; i >= LDBL_MIN_EXP; i--, x *= 0.5L) - ; - if (x > 0.0L) - { - int exp; - long double y = frexpl (x, &exp); - /* On machines with IEEE854 arithmetic: x = 1.68105e-4932, - exp = -16382, y = 0.5. On Mac OS X 10.5: exp = -16384, y = 0.5. */ - if (exp != LDBL_MIN_EXP - 1) - result |= 8; - } - } - /* Test on infinite numbers. */ - /* The Microsoft MSVC 14 compiler chokes on the expression 1.0 / 0.0. */ - x = 1.0L / zero; - { - int exp; - long double y = frexpl (x, &exp); - if (y != x) - result |= 16; - } - return result; -}]])], - [gl_cv_func_frexpl_works=yes], - [gl_cv_func_frexpl_works=no], - [ -changequote(,)dnl - case "$host_os" in - aix | aix[3-6]* | beos* | darwin* | irix* | mingw* | pw*) - gl_cv_func_frexpl_works="guessing no";; - *) gl_cv_func_frexpl_works="guessing yes";; - esac -changequote([,])dnl - ]) - ]) -]) diff --git a/m4/fseterr.m4 b/m4/fseterr.m4 deleted file mode 100644 index 61ac03d49d1..00000000000 --- a/m4/fseterr.m4 +++ /dev/null @@ -1,13 +0,0 @@ -# fseterr.m4 serial 2 -dnl Copyright (C) 2012-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_FSETERR], -[ - gl_CHECK_FUNCS_ANDROID([__fseterr], - [[#include - #include - ]]) -]) diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 4c1e41daf51..72adcf90d5e 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -95,15 +95,10 @@ AC_DEFUN # Code from module filename: # Code from module filevercmp: # Code from module flexmember: - # Code from module float: # Code from module fpending: # Code from module fpieee: AC_REQUIRE([gl_FP_IEEE]) - # Code from module fpucw: # Code from module free-posix: - # Code from module frexp-nolibm: - # Code from module frexpl-nolibm: - # Code from module fseterr: # Code from module fstatat: # Code from module fsusage: # Code from module fsync: @@ -130,9 +125,6 @@ AC_DEFUN # Code from module include_next: # Code from module intprops: # Code from module inttypes-incomplete: - # Code from module isnand-nolibm: - # Code from module isnanf-nolibm: - # Code from module isnanl-nolibm: # Code from module largefile: AC_REQUIRE([AC_SYS_LARGEFILE]) # Code from module lchmod: @@ -143,7 +135,6 @@ AC_DEFUN # Code from module malloc-gnu: # Code from module malloc-posix: # Code from module manywarnings: - # Code from module math: # Code from module memmem-simple: # Code from module mempcpy: # Code from module memrchr: @@ -161,10 +152,6 @@ AC_DEFUN # Code from module openat-h: # Code from module pathmax: # Code from module pipe2: - # Code from module printf-frexp: - # Code from module printf-frexpl: - # Code from module printf-posix: - # Code from module printf-safe: # Code from module pselect: # Code from module pthread_sigmask: # Code from module qcopy-acl: @@ -178,8 +165,6 @@ AC_DEFUN # Code from module sig2str: # Code from module sigdescr_np: # Code from module signal-h: - # Code from module signbit: - # Code from module size_max: # Code from module snippet/_Noreturn: # Code from module snippet/arg-nonnull: # Code from module snippet/c++defs: @@ -224,15 +209,10 @@ AC_DEFUN # Code from module utimens: # Code from module utimensat: # Code from module vararrays: - # Code from module vasnprintf: - # Code from module vasprintf: - # Code from module vasprintf-posix: # Code from module verify: - # Code from module vfprintf-posix: # Code from module vla: # Code from module warnings: # Code from module xalloc-oversized: - # Code from module xsize: # Code from module year2038: AC_REQUIRE([AC_SYS_YEAR2038]) ]) @@ -342,11 +322,6 @@ AC_DEFUN gl_FILE_HAS_ACL gl_FILEMODE AC_C_FLEXIBLE_ARRAY_MEMBER - gl_FLOAT_H - gl_CONDITIONAL_HEADER([float.h]) - AC_PROG_MKDIR_P - gl_CONDITIONAL([GL_COND_OBJ_FLOAT], [test $REPLACE_FLOAT_LDBL = 1]) - gl_CONDITIONAL([GL_COND_OBJ_ITOLD], [test $REPLACE_ITOLD = 1]) gl_FUNC_FPENDING gl_CONDITIONAL([GL_COND_OBJ_FPENDING], [test $gl_cv_func___fpending = no]) gl_FUNC_FREE @@ -355,16 +330,6 @@ AC_DEFUN gl_PREREQ_FREE ]) gl_STDLIB_MODULE_INDICATOR([free-posix]) - gl_FUNC_FREXP_NO_LIBM - if test $gl_func_frexp_no_libm != yes; then - AC_LIBOBJ([frexp]) - fi - gl_MATH_MODULE_INDICATOR([frexp]) - gl_FUNC_FREXPL_NO_LIBM - if test $HAVE_DECL_FREXPL = 0 || test $gl_func_frexpl_no_libm = no; then - AC_LIBOBJ([frexpl]) - fi - gl_MATH_MODULE_INDICATOR([frexpl]) gl_FUNC_FSTATAT gl_CONDITIONAL([GL_COND_OBJ_FSTATAT], [test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1]) @@ -432,16 +397,6 @@ AC_DEFUN gl_INTTYPES_INCOMPLETE gl_INTTYPES_H_REQUIRE_DEFAULTS AC_PROG_MKDIR_P - gl_FUNC_ISNAND_NO_LIBM - if test $gl_func_isnand_no_libm != yes; then - AC_LIBOBJ([isnand]) - gl_PREREQ_ISNAND - fi - gl_FUNC_ISNANL_NO_LIBM - if test $gl_func_isnanl_no_libm != yes; then - AC_LIBOBJ([isnanl]) - gl_PREREQ_ISNANL - fi AC_REQUIRE([gl_LARGEFILE]) gl___INLINE gl_LIBGMP @@ -457,9 +412,6 @@ AC_DEFUN gl_PREREQ_LSTAT ]) gl_SYS_STAT_MODULE_INDICATOR([lstat]) - gl_MATH_H - gl_MATH_H_REQUIRE_DEFAULTS - AC_PROG_MKDIR_P gl_FUNC_MEMMEM_SIMPLE if test $HAVE_MEMMEM = 0 || test $REPLACE_MEMMEM = 1; then AC_LIBOBJ([memmem]) @@ -509,11 +461,6 @@ AC_DEFUN gl_PATHMAX gl_FUNC_PIPE2 gl_UNISTD_MODULE_INDICATOR([pipe2]) - gl_FUNC_PRINTF_FREXP - gl_FUNC_PRINTF_FREXPL - gl_FUNC_PRINTF_POSIX - gl_STDIO_MODULE_INDICATOR([printf-posix]) - m4_divert_text([INIT_PREPARE], [gl_printf_safe=yes]) gl_FUNC_PSELECT gl_CONDITIONAL([GL_COND_OBJ_PSELECT], [test $HAVE_PSELECT = 0 || test $REPLACE_PSELECT = 1]) @@ -560,9 +507,6 @@ AC_DEFUN gl_SIGNAL_H gl_SIGNAL_H_REQUIRE_DEFAULTS AC_PROG_MKDIR_P - gl_SIGNBIT - gl_CONDITIONAL([GL_COND_OBJ_SIGNBIT3], [test $REPLACE_SIGNBIT = 1]) - gl_MATH_MODULE_INDICATOR([signbit]) gl_TYPE_SOCKLEN_T gt_TYPE_SSIZE_T gl_STAT_TIME @@ -709,18 +653,11 @@ AC_DEFUN [test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1]) gl_SYS_STAT_MODULE_INDICATOR([utimensat]) AC_C_VARARRAYS - gl_FUNC_VASPRINTF - gl_STDIO_MODULE_INDICATOR([vasprintf]) - m4_ifdef([AM_XGETTEXT_OPTION], - [AM_][XGETTEXT_OPTION([--flag=asprintf:2:c-format]) - AM_][XGETTEXT_OPTION([--flag=vasprintf:2:c-format])]) - gl_FUNC_VASPRINTF_POSIX gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false gl_gnulib_enabled_cloexec=false gl_gnulib_enabled_dirfd=false gl_gnulib_enabled_925677f0343de64b89a9f0c790b4104c=false gl_gnulib_enabled_euidaccess=false - gl_gnulib_enabled_fseterr=false gl_gnulib_enabled_getdelim=false gl_gnulib_enabled_getdtablesize=false gl_gnulib_enabled_getgroups=false @@ -728,7 +665,6 @@ AC_DEFUN gl_gnulib_enabled_fd38c7e463b54744b77b98aeafb4fa7c=false gl_gnulib_enabled_8444034ea779b88768865bb60b4fb8c9=false gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false - gl_gnulib_enabled_3f0e593033d1fc2c127581960f641b66=false gl_gnulib_enabled_lchmod=false gl_gnulib_enabled_e80bf6f757095d2e5fc94dafb8f8fc8b=false gl_gnulib_enabled_ef455225c00f5049c808c2eda3e76866=false @@ -739,13 +675,9 @@ AC_DEFUN gl_gnulib_enabled_d3b2383720ee0e541357aa2aac598e2b=false gl_gnulib_enabled_61bcaca76b3e6f9ae55d57a1c3193bc4=false gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false - gl_gnulib_enabled_size_max=false gl_gnulib_enabled_strtoll=false gl_gnulib_enabled_utimens=false - gl_gnulib_enabled_vasnprintf=false - gl_gnulib_enabled_ed5616be3593d355b981ffab56b9f37b=false gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false - gl_gnulib_enabled_xsize=false func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b () { if $gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b; then :; else @@ -798,14 +730,6 @@ AC_DEFUN func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c fi } - func_gl_gnulib_m4code_fseterr () - { - if $gl_gnulib_enabled_fseterr; then :; else - gl_FUNC_FSETERR - gl_CONDITIONAL([GL_COND_OBJ_FSETERR], [test $ac_cv_func___fseterr = no]) - gl_gnulib_enabled_fseterr=true - fi - } func_gl_gnulib_m4code_getdelim () { if $gl_gnulib_enabled_getdelim; then :; else @@ -887,17 +811,6 @@ AC_DEFUN fi fi } - func_gl_gnulib_m4code_3f0e593033d1fc2c127581960f641b66 () - { - if $gl_gnulib_enabled_3f0e593033d1fc2c127581960f641b66; then :; else - gl_FUNC_ISNANF_NO_LIBM - if test $gl_func_isnanf_no_libm != yes; then - AC_LIBOBJ([isnanf]) - gl_PREREQ_ISNANF - fi - gl_gnulib_enabled_3f0e593033d1fc2c127581960f641b66=true - fi - } func_gl_gnulib_m4code_lchmod () { if $gl_gnulib_enabled_lchmod; then :; else @@ -1024,13 +937,6 @@ AC_DEFUN gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=true fi } - func_gl_gnulib_m4code_size_max () - { - if $gl_gnulib_enabled_size_max; then :; else - gl_SIZE_MAX - gl_gnulib_enabled_size_max=true - fi - } func_gl_gnulib_m4code_strtoll () { if $gl_gnulib_enabled_strtoll; then :; else @@ -1051,44 +957,12 @@ AC_DEFUN gl_gnulib_enabled_utimens=true fi } - func_gl_gnulib_m4code_vasnprintf () - { - if $gl_gnulib_enabled_vasnprintf; then :; else - AC_REQUIRE([AC_C_RESTRICT]) - gl_FUNC_VASNPRINTF - gl_gnulib_enabled_vasnprintf=true - func_gl_gnulib_m4code_xsize - fi - } - func_gl_gnulib_m4code_ed5616be3593d355b981ffab56b9f37b () - { - if $gl_gnulib_enabled_ed5616be3593d355b981ffab56b9f37b; then :; else - gl_FUNC_VFPRINTF_POSIX - gl_STDIO_MODULE_INDICATOR([vfprintf-posix]) - gl_MODULE_INDICATOR([vfprintf-posix]) - gl_gnulib_enabled_ed5616be3593d355b981ffab56b9f37b=true - if test $REPLACE_VFPRINTF = 1; then - func_gl_gnulib_m4code_fseterr - fi - if test $REPLACE_VFPRINTF = 1; then - func_gl_gnulib_m4code_vasnprintf - fi - fi - } func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec () { if $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then :; else gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=true fi } - func_gl_gnulib_m4code_xsize () - { - if $gl_gnulib_enabled_xsize; then :; else - gl_XSIZE - gl_gnulib_enabled_xsize=true - func_gl_gnulib_m4code_size_max - fi - } if test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1; then func_gl_gnulib_m4code_925677f0343de64b89a9f0c790b4104c fi @@ -1146,9 +1020,6 @@ AC_DEFUN if case $host_os in mingw*) false;; *) test $HAVE_GETRANDOM = 0 || test $REPLACE_GETRANDOM = 1;; esac; then func_gl_gnulib_m4code_open fi - if test $REPLACE_PRINTF = 1; then - func_gl_gnulib_m4code_ed5616be3593d355b981ffab56b9f37b - fi if test $HAVE_READLINKAT = 0 || test $REPLACE_READLINKAT = 1; then func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b fi @@ -1158,9 +1029,6 @@ AC_DEFUN if test $ac_use_included_regex = yes; then func_gl_gnulib_m4code_fd38c7e463b54744b77b98aeafb4fa7c fi - if test $REPLACE_SIGNBIT = 1; then - func_gl_gnulib_m4code_3f0e593033d1fc2c127581960f641b66 - fi if { test $HAVE_DECL_STRTOIMAX = 0 || test $REPLACE_STRTOIMAX = 1; } && test $ac_cv_type_long_long_int = yes; then func_gl_gnulib_m4code_strtoll fi @@ -1176,19 +1044,12 @@ AC_DEFUN if test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1; then func_gl_gnulib_m4code_utimens fi - if test $HAVE_VASPRINTF = 0 || test $REPLACE_VASPRINTF = 1; then - func_gl_gnulib_m4code_vasnprintf - fi - if test $HAVE_VASPRINTF = 0 || test $REPLACE_VASPRINTF = 1; then - func_gl_gnulib_m4code_vasnprintf - fi m4_pattern_allow([^gl_GNULIB_ENABLED_]) AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b]) AM_CONDITIONAL([gl_GNULIB_ENABLED_cloexec], [$gl_gnulib_enabled_cloexec]) AM_CONDITIONAL([gl_GNULIB_ENABLED_dirfd], [$gl_gnulib_enabled_dirfd]) AM_CONDITIONAL([gl_GNULIB_ENABLED_925677f0343de64b89a9f0c790b4104c], [$gl_gnulib_enabled_925677f0343de64b89a9f0c790b4104c]) AM_CONDITIONAL([gl_GNULIB_ENABLED_euidaccess], [$gl_gnulib_enabled_euidaccess]) - AM_CONDITIONAL([gl_GNULIB_ENABLED_fseterr], [$gl_gnulib_enabled_fseterr]) AM_CONDITIONAL([gl_GNULIB_ENABLED_getdelim], [$gl_gnulib_enabled_getdelim]) AM_CONDITIONAL([gl_GNULIB_ENABLED_getdtablesize], [$gl_gnulib_enabled_getdtablesize]) AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups]) @@ -1196,7 +1057,6 @@ AC_DEFUN AM_CONDITIONAL([gl_GNULIB_ENABLED_fd38c7e463b54744b77b98aeafb4fa7c], [$gl_gnulib_enabled_fd38c7e463b54744b77b98aeafb4fa7c]) AM_CONDITIONAL([gl_GNULIB_ENABLED_8444034ea779b88768865bb60b4fb8c9], [$gl_gnulib_enabled_8444034ea779b88768865bb60b4fb8c9]) AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1]) - AM_CONDITIONAL([gl_GNULIB_ENABLED_3f0e593033d1fc2c127581960f641b66], [$gl_gnulib_enabled_3f0e593033d1fc2c127581960f641b66]) AM_CONDITIONAL([gl_GNULIB_ENABLED_lchmod], [$gl_gnulib_enabled_lchmod]) AM_CONDITIONAL([gl_GNULIB_ENABLED_e80bf6f757095d2e5fc94dafb8f8fc8b], [$gl_gnulib_enabled_e80bf6f757095d2e5fc94dafb8f8fc8b]) AM_CONDITIONAL([gl_GNULIB_ENABLED_ef455225c00f5049c808c2eda3e76866], [$gl_gnulib_enabled_ef455225c00f5049c808c2eda3e76866]) @@ -1207,13 +1067,9 @@ AC_DEFUN AM_CONDITIONAL([gl_GNULIB_ENABLED_d3b2383720ee0e541357aa2aac598e2b], [$gl_gnulib_enabled_d3b2383720ee0e541357aa2aac598e2b]) AM_CONDITIONAL([gl_GNULIB_ENABLED_61bcaca76b3e6f9ae55d57a1c3193bc4], [$gl_gnulib_enabled_61bcaca76b3e6f9ae55d57a1c3193bc4]) AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c]) - AM_CONDITIONAL([gl_GNULIB_ENABLED_size_max], [$gl_gnulib_enabled_size_max]) AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll]) AM_CONDITIONAL([gl_GNULIB_ENABLED_utimens], [$gl_gnulib_enabled_utimens]) - AM_CONDITIONAL([gl_GNULIB_ENABLED_vasnprintf], [$gl_gnulib_enabled_vasnprintf]) - AM_CONDITIONAL([gl_GNULIB_ENABLED_ed5616be3593d355b981ffab56b9f37b], [$gl_gnulib_enabled_ed5616be3593d355b981ffab56b9f37b]) AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec]) - AM_CONDITIONAL([gl_GNULIB_ENABLED_xsize], [$gl_gnulib_enabled_xsize]) # End of code from modules m4_ifval(gl_LIBSOURCES_LIST, [ m4_syscmd([test ! -d ]m4_defn([gl_LIBSOURCES_DIR])[ || @@ -1399,8 +1255,6 @@ AC_DEFUN lib/allocator.c lib/allocator.h lib/arg-nonnull.h - lib/asnprintf.c - lib/asprintf.c lib/assert.in.h lib/at-func.c lib/attribute.h @@ -1453,17 +1307,9 @@ AC_DEFUN lib/filevercmp.c lib/filevercmp.h lib/flexmember.h - lib/float+.h - lib/float.c - lib/float.in.h lib/fpending.c lib/fpending.h - lib/fpucw.h lib/free.c - lib/frexp.c - lib/frexpl.c - lib/fseterr.c - lib/fseterr.h lib/fstatat.c lib/fsusage.c lib/fsusage.h @@ -1498,14 +1344,6 @@ AC_DEFUN lib/intprops-internal.h lib/intprops.h lib/inttypes.in.h - lib/isnan.c - lib/isnand-nolibm.h - lib/isnand.c - lib/isnanf-nolibm.h - lib/isnanf.c - lib/isnanl-nolibm.h - lib/isnanl.c - lib/itold.c lib/lchmod.c lib/libc-config.h lib/limits.in.h @@ -1522,8 +1360,6 @@ AC_DEFUN lib/malloc/scratch_buffer_grow.c lib/malloc/scratch_buffer_grow_preserve.c lib/malloc/scratch_buffer_set_array_size.c - lib/math.c - lib/math.in.h lib/md5-stream.c lib/md5.c lib/md5.h @@ -1548,15 +1384,6 @@ AC_DEFUN lib/openat.h lib/pathmax.h lib/pipe2.c - lib/printf-args.c - lib/printf-args.h - lib/printf-frexp.c - lib/printf-frexp.h - lib/printf-frexpl.c - lib/printf-frexpl.h - lib/printf-parse.c - lib/printf-parse.h - lib/printf.c lib/pselect.c lib/pthread_sigmask.c lib/qcopy-acl.c @@ -1584,10 +1411,6 @@ AC_DEFUN lib/sig2str.h lib/sigdescr_np.c lib/signal.in.h - lib/signbitd.c - lib/signbitf.c - lib/signbitl.c - lib/size_max.h lib/stat-time.c lib/stat-time.h lib/stdckdint.in.h @@ -1632,22 +1455,15 @@ AC_DEFUN lib/utimens.c lib/utimens.h lib/utimensat.c - lib/vasnprintf.c - lib/vasnprintf.h - lib/vasprintf.c lib/verify.h - lib/vfprintf.c lib/vla.h lib/warn-on-use.h lib/xalloc-oversized.h - lib/xsize.c - lib/xsize.h m4/00gnulib.m4 m4/__inline.m4 m4/absolute-header.m4 m4/acl.m4 m4/alloca.m4 - m4/asm-underscore.m4 m4/assert_h.m4 m4/builtin-expect.m4 m4/byteswap.m4 @@ -1666,9 +1482,6 @@ AC_DEFUN m4/errno_h.m4 m4/euidaccess.m4 m4/execinfo.m4 - m4/exponentd.m4 - m4/exponentf.m4 - m4/exponentl.m4 m4/extensions.m4 m4/extern-inline.m4 m4/faccessat.m4 @@ -1679,13 +1492,9 @@ AC_DEFUN m4/fdopendir.m4 m4/filemode.m4 m4/flexmember.m4 - m4/float_h.m4 m4/fpending.m4 m4/fpieee.m4 m4/free.m4 - m4/frexp.m4 - m4/frexpl.m4 - m4/fseterr.m4 m4/fstatat.m4 m4/fsusage.m4 m4/fsync.m4 @@ -1704,15 +1513,9 @@ AC_DEFUN m4/group-member.m4 m4/ieee754-h.m4 m4/include_next.m4 - m4/intmax_t.m4 m4/inttypes.m4 - m4/inttypes_h.m4 - m4/isnand.m4 - m4/isnanf.m4 - m4/isnanl.m4 m4/largefile.m4 m4/lchmod.m4 - m4/ldexpl.m4 m4/libgmp.m4 m4/limits-h.m4 m4/locale-fr.m4 @@ -1720,7 +1523,6 @@ AC_DEFUN m4/malloc.m4 m4/manywarnings-c++.m4 m4/manywarnings.m4 - m4/math_h.m4 m4/mbstate_t.m4 m4/md5.m4 m4/memmem.m4 @@ -1744,10 +1546,6 @@ AC_DEFUN m4/pathmax.m4 m4/pid_t.m4 m4/pipe2.m4 - m4/printf-frexp.m4 - m4/printf-frexpl.m4 - m4/printf-posix.m4 - m4/printf.m4 m4/pselect.m4 m4/pthread_sigmask.m4 m4/rawmemchr.m4 @@ -1761,8 +1559,6 @@ AC_DEFUN m4/sig2str.m4 m4/sigdescr_np.m4 m4/signal_h.m4 - m4/signbit.m4 - m4/size_max.m4 m4/socklen.m4 m4/ssize_t.m4 m4/stat-time.m4 @@ -1770,7 +1566,6 @@ AC_DEFUN m4/stdalign.m4 m4/stddef_h.m4 m4/stdint.m4 - m4/stdint_h.m4 m4/stdio_h.m4 m4/stdlib_h.m4 m4/stpcpy.m4 @@ -1800,15 +1595,10 @@ AC_DEFUN m4/utimensat.m4 m4/utimes.m4 m4/vararrays.m4 - m4/vasnprintf.m4 - m4/vasprintf-posix.m4 - m4/vasprintf.m4 - m4/vfprintf-posix.m4 m4/warn-on-use.m4 m4/warnings.m4 m4/wchar_t.m4 m4/wint_t.m4 m4/xattr.m4 - m4/xsize.m4 m4/zzgnulib.m4 ]) diff --git a/m4/intmax_t.m4 b/m4/intmax_t.m4 deleted file mode 100644 index ef32e1b9ca9..00000000000 --- a/m4/intmax_t.m4 +++ /dev/null @@ -1,59 +0,0 @@ -# intmax_t.m4 serial 9 -dnl Copyright (C) 1997-2004, 2006-2007, 2009-2023 Free Software Foundation, -dnl Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl From Paul Eggert. - -AC_PREREQ([2.53]) - -# Define intmax_t to 'long' or 'long long' -# if it is not already defined in or . - -AC_DEFUN([gl_AC_TYPE_INTMAX_T], -[ - dnl For simplicity, we assume that a header file defines 'intmax_t' if and - dnl only if it defines 'uintmax_t'. - AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) - AC_REQUIRE([gl_AC_HEADER_STDINT_H]) - if test $gl_cv_header_inttypes_h = no && test $gl_cv_header_stdint_h = no; then - AC_DEFINE_UNQUOTED([intmax_t], [long long], - [Define to long or long long if and don't define.]) - else - AC_DEFINE([HAVE_INTMAX_T], [1], - [Define if you have the 'intmax_t' type in or .]) - fi -]) - -dnl An alternative would be to explicitly test for 'intmax_t'. - -AC_DEFUN([gt_AC_TYPE_INTMAX_T], -[ - AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) - AC_REQUIRE([gl_AC_HEADER_STDINT_H]) - AC_CACHE_CHECK([for intmax_t], [gt_cv_c_intmax_t], - [AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[ -#include -#include -#if HAVE_STDINT_H_WITH_UINTMAX -#include -#endif -#if HAVE_INTTYPES_H_WITH_UINTMAX -#include -#endif - ]], - [[intmax_t x = -1; return !x;]])], - [gt_cv_c_intmax_t=yes], - [gt_cv_c_intmax_t=no])]) - if test $gt_cv_c_intmax_t = yes; then - AC_DEFINE([HAVE_INTMAX_T], [1], - [Define if you have the 'intmax_t' type in or .]) - else - AC_DEFINE_UNQUOTED([intmax_t], [long long], - [Define to long or long long if and don't define.]) - fi -]) diff --git a/m4/inttypes_h.m4 b/m4/inttypes_h.m4 deleted file mode 100644 index 68c60e9dbb6..00000000000 --- a/m4/inttypes_h.m4 +++ /dev/null @@ -1,29 +0,0 @@ -# inttypes_h.m4 serial 10 -dnl Copyright (C) 1997-2004, 2006, 2008-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl From Paul Eggert. - -# Define HAVE_INTTYPES_H_WITH_UINTMAX if exists, -# doesn't clash with , and declares uintmax_t. - -AC_DEFUN([gl_AC_HEADER_INTTYPES_H], -[ - AC_CACHE_CHECK([for inttypes.h], [gl_cv_header_inttypes_h], - [AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[ -#include -#include - ]], - [[uintmax_t i = (uintmax_t) -1; return !i;]])], - [gl_cv_header_inttypes_h=yes], - [gl_cv_header_inttypes_h=no])]) - if test $gl_cv_header_inttypes_h = yes; then - AC_DEFINE_UNQUOTED([HAVE_INTTYPES_H_WITH_UINTMAX], [1], - [Define if exists, doesn't clash with , - and declares uintmax_t. ]) - fi -]) diff --git a/m4/isnand.m4 b/m4/isnand.m4 deleted file mode 100644 index 95346f420b7..00000000000 --- a/m4/isnand.m4 +++ /dev/null @@ -1,96 +0,0 @@ -# isnand.m4 serial 12 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl Check how to get or define isnand(). - -AC_DEFUN([gl_FUNC_ISNAND], -[ - AC_REQUIRE([gl_MATH_H_DEFAULTS]) - ISNAND_LIBM= - gl_HAVE_ISNAND_NO_LIBM - if test $gl_cv_func_isnand_no_libm = no; then - gl_HAVE_ISNAND_IN_LIBM - if test $gl_cv_func_isnand_in_libm = yes; then - ISNAND_LIBM=-lm - fi - fi - dnl The variable gl_func_isnand set here is used by isnan.m4. - if test $gl_cv_func_isnand_no_libm = yes \ - || test $gl_cv_func_isnand_in_libm = yes; then - gl_func_isnand=yes - else - gl_func_isnand=no - HAVE_ISNAND=0 - fi - AC_SUBST([ISNAND_LIBM]) -]) - -dnl Check how to get or define isnand() without linking with libm. - -AC_DEFUN([gl_FUNC_ISNAND_NO_LIBM], -[ - gl_HAVE_ISNAND_NO_LIBM - gl_func_isnand_no_libm=$gl_cv_func_isnand_no_libm - if test $gl_cv_func_isnand_no_libm = yes; then - AC_DEFINE([HAVE_ISNAND_IN_LIBC], [1], - [Define if the isnan(double) function is available in libc.]) - fi -]) - -dnl Prerequisites of replacement isnand definition. It does not need -lm. -AC_DEFUN([gl_PREREQ_ISNAND], -[ - AC_REQUIRE([gl_DOUBLE_EXPONENT_LOCATION]) -]) - -dnl Test whether isnand() can be used with libm. - -AC_DEFUN([gl_HAVE_ISNAND_IN_LIBM], -[ - AC_CACHE_CHECK([whether isnan(double) can be used with libm], - [gl_cv_func_isnand_in_libm], - [ - save_LIBS="$LIBS" - LIBS="$LIBS -lm" - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - #if (__GNUC__ >= 4) || (__clang_major__ >= 4) - # undef isnand - # define isnand(x) __builtin_isnan ((double)(x)) - #elif defined isnan - # undef isnand - # define isnand(x) isnan ((double)(x)) - #endif - double x;]], - [[return isnand (x);]])], - [gl_cv_func_isnand_in_libm=yes], - [gl_cv_func_isnand_in_libm=no]) - LIBS="$save_LIBS" - ]) -]) - -AC_DEFUN([gl_HAVE_ISNAND_NO_LIBM], -[ - AC_CACHE_CHECK([whether isnan(double) can be used without linking with libm], - [gl_cv_func_isnand_no_libm], - [ - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - #if (__GNUC__ >= 4) || (__clang_major__ >= 4) - # undef isnand - # define isnand(x) __builtin_isnan ((double)(x)) - #else - # undef isnand - # define isnand(x) isnan ((double)(x)) - #endif - double x;]], - [[return isnand (x);]])], - [gl_cv_func_isnand_no_libm=yes], - [gl_cv_func_isnand_no_libm=no]) - ]) -]) diff --git a/m4/isnanf.m4 b/m4/isnanf.m4 deleted file mode 100644 index 01f7bbd20d8..00000000000 --- a/m4/isnanf.m4 +++ /dev/null @@ -1,197 +0,0 @@ -# isnanf.m4 serial 18 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl Check how to get or define isnanf(). - -AC_DEFUN([gl_FUNC_ISNANF], -[ - AC_REQUIRE([gl_MATH_H_DEFAULTS]) - ISNANF_LIBM= - gl_HAVE_ISNANF_NO_LIBM - if test $gl_cv_func_isnanf_no_libm = no; then - gl_HAVE_ISNANF_IN_LIBM - if test $gl_cv_func_isnanf_in_libm = yes; then - ISNANF_LIBM=-lm - fi - fi - dnl The variable gl_func_isnanf set here is used by isnan.m4. - if test $gl_cv_func_isnanf_no_libm = yes \ - || test $gl_cv_func_isnanf_in_libm = yes; then - save_LIBS="$LIBS" - LIBS="$LIBS $ISNANF_LIBM" - gl_ISNANF_WORKS - LIBS="$save_LIBS" - case "$gl_cv_func_isnanf_works" in - *yes) gl_func_isnanf=yes ;; - *) gl_func_isnanf=no; ISNANF_LIBM= ;; - esac - else - gl_func_isnanf=no - fi - if test $gl_func_isnanf != yes; then - HAVE_ISNANF=0 - fi - AC_SUBST([ISNANF_LIBM]) -]) - -dnl Check how to get or define isnanf() without linking with libm. - -AC_DEFUN([gl_FUNC_ISNANF_NO_LIBM], -[ - gl_HAVE_ISNANF_NO_LIBM - if test $gl_cv_func_isnanf_no_libm = yes; then - gl_ISNANF_WORKS - fi - if test $gl_cv_func_isnanf_no_libm = yes \ - && { case "$gl_cv_func_isnanf_works" in - *yes) true;; - *) false;; - esac - }; then - gl_func_isnanf_no_libm=yes - AC_DEFINE([HAVE_ISNANF_IN_LIBC], [1], - [Define if the isnan(float) function is available in libc.]) - else - gl_func_isnanf_no_libm=no - fi -]) - -dnl Prerequisites of replacement isnanf definition. It does not need -lm. -AC_DEFUN([gl_PREREQ_ISNANF], -[ - gl_FLOAT_EXPONENT_LOCATION -]) - -dnl Test whether isnanf() can be used without libm. -AC_DEFUN([gl_HAVE_ISNANF_NO_LIBM], -[ - AC_CACHE_CHECK([whether isnan(float) can be used without linking with libm], - [gl_cv_func_isnanf_no_libm], - [ - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - #if (__GNUC__ >= 4) || (__clang_major__ >= 4) - # undef isnanf - # define isnanf(x) __builtin_isnan ((float)(x)) - #elif defined isnan - # undef isnanf - # define isnanf(x) isnan ((float)(x)) - #endif - float x;]], - [[return isnanf (x);]])], - [gl_cv_func_isnanf_no_libm=yes], - [gl_cv_func_isnanf_no_libm=no]) - ]) -]) - -dnl Test whether isnanf() can be used with libm. -AC_DEFUN([gl_HAVE_ISNANF_IN_LIBM], -[ - AC_CACHE_CHECK([whether isnan(float) can be used with libm], - [gl_cv_func_isnanf_in_libm], - [ - save_LIBS="$LIBS" - LIBS="$LIBS -lm" - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - #if (__GNUC__ >= 4) || (__clang_major__ >= 4) - # undef isnanf - # define isnanf(x) __builtin_isnan ((float)(x)) - #elif defined isnan - # undef isnanf - # define isnanf(x) isnan ((float)(x)) - #endif - float x;]], - [[return isnanf (x);]])], - [gl_cv_func_isnanf_in_libm=yes], - [gl_cv_func_isnanf_in_libm=no]) - LIBS="$save_LIBS" - ]) -]) - -dnl Test whether isnanf() rejects Infinity (this fails on Solaris 2.5.1), -dnl recognizes a NaN (this fails on IRIX 6.5 with cc), and recognizes a NaN -dnl with in-memory representation 0x7fbfffff (this fails on IRIX 6.5). -AC_DEFUN([gl_ISNANF_WORKS], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_REQUIRE([gl_FLOAT_EXPONENT_LOCATION]) - AC_CACHE_CHECK([whether isnan(float) works], [gl_cv_func_isnanf_works], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#if (__GNUC__ >= 4) || (__clang_major__ >= 4) -# undef isnanf -# define isnanf(x) __builtin_isnan ((float)(x)) -#elif defined isnan -# undef isnanf -# define isnanf(x) isnan ((float)(x)) -#endif -/* The Compaq (ex-DEC) C 6.4 compiler chokes on the expression 0.0 / 0.0. */ -#ifdef __DECC -static float -NaN () -{ - static float zero = 0.0f; - return zero / zero; -} -#else -# define NaN() (0.0f / 0.0f) -#endif -#define NWORDS \ - ((sizeof (float) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) -typedef union { unsigned int word[NWORDS]; float value; } memory_float; -int main() -{ - int result = 0; - - if (isnanf (1.0f / 0.0f)) - result |= 1; - - if (!isnanf (NaN ())) - result |= 2; - -#if defined FLT_EXPBIT0_WORD && defined FLT_EXPBIT0_BIT - /* The isnanf function should be immune against changes in the sign bit and - in the mantissa bits. The xor operation twiddles a bit that can only be - a sign bit or a mantissa bit. */ - if (FLT_EXPBIT0_WORD == 0 && FLT_EXPBIT0_BIT > 0) - { - memory_float m; - - m.value = NaN (); - /* Set the bits below the exponent to 01111...111. */ - m.word[0] &= -1U << FLT_EXPBIT0_BIT; - m.word[0] |= (1U << (FLT_EXPBIT0_BIT - 1)) - 1; - if (!isnanf (m.value)) - result |= 4; - } -#endif - - return result; -}]])], - [gl_cv_func_isnanf_works=yes], - [gl_cv_func_isnanf_works=no], - [case "$host_os" in - irix* | solaris*) gl_cv_func_isnanf_works="guessing no" ;; - mingw*) # Guess yes on mingw, no on MSVC. - AC_EGREP_CPP([Known], [ -#ifdef __MINGW32__ - Known -#endif - ], - [gl_cv_func_isnanf_works="guessing yes"], - [gl_cv_func_isnanf_works="guessing no"]) - ;; - *) gl_cv_func_isnanf_works="guessing yes" ;; - esac - ]) - ]) -]) diff --git a/m4/isnanl.m4 b/m4/isnanl.m4 deleted file mode 100644 index bb39d02558f..00000000000 --- a/m4/isnanl.m4 +++ /dev/null @@ -1,248 +0,0 @@ -# isnanl.m4 serial 22 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_ISNANL], -[ - AC_REQUIRE([gl_MATH_H_DEFAULTS]) - ISNANL_LIBM= - gl_HAVE_ISNANL_NO_LIBM - if test $gl_cv_func_isnanl_no_libm = no; then - gl_HAVE_ISNANL_IN_LIBM - if test $gl_cv_func_isnanl_in_libm = yes; then - ISNANL_LIBM=-lm - fi - fi - dnl The variable gl_func_isnanl set here is used by isnan.m4. - if test $gl_cv_func_isnanl_no_libm = yes \ - || test $gl_cv_func_isnanl_in_libm = yes; then - save_LIBS="$LIBS" - LIBS="$LIBS $ISNANL_LIBM" - gl_FUNC_ISNANL_WORKS - LIBS="$save_LIBS" - case "$gl_cv_func_isnanl_works" in - *yes) gl_func_isnanl=yes ;; - *) gl_func_isnanl=no; ISNANL_LIBM= ;; - esac - else - gl_func_isnanl=no - fi - if test $gl_func_isnanl != yes; then - HAVE_ISNANL=0 - fi - AC_SUBST([ISNANL_LIBM]) -]) - -AC_DEFUN([gl_FUNC_ISNANL_NO_LIBM], -[ - gl_HAVE_ISNANL_NO_LIBM - gl_func_isnanl_no_libm=$gl_cv_func_isnanl_no_libm - if test $gl_func_isnanl_no_libm = yes; then - gl_FUNC_ISNANL_WORKS - case "$gl_cv_func_isnanl_works" in - *yes) ;; - *) gl_func_isnanl_no_libm=no ;; - esac - fi - if test $gl_func_isnanl_no_libm = yes; then - AC_DEFINE([HAVE_ISNANL_IN_LIBC], [1], - [Define if the isnan(long double) function is available in libc.]) - fi -]) - -dnl Prerequisites of replacement isnanl definition. It does not need -lm. -AC_DEFUN([gl_PREREQ_ISNANL], -[ - gl_LONG_DOUBLE_EXPONENT_LOCATION - AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE]) -]) - -dnl Test whether isnanl() can be used without libm. -AC_DEFUN([gl_HAVE_ISNANL_NO_LIBM], -[ - AC_CACHE_CHECK([whether isnan(long double) can be used without linking with libm], - [gl_cv_func_isnanl_no_libm], - [ - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - #if (__GNUC__ >= 4) || (__clang_major__ >= 4) - # undef isnanl - # define isnanl(x) __builtin_isnan ((long double)(x)) - #elif defined isnan - # undef isnanl - # define isnanl(x) isnan ((long double)(x)) - #endif - long double x;]], - [[return isnanl (x);]])], - [gl_cv_func_isnanl_no_libm=yes], - [gl_cv_func_isnanl_no_libm=no]) - ]) -]) - -dnl Test whether isnanl() can be used with libm. -AC_DEFUN([gl_HAVE_ISNANL_IN_LIBM], -[ - AC_CACHE_CHECK([whether isnan(long double) can be used with libm], - [gl_cv_func_isnanl_in_libm], - [ - save_LIBS="$LIBS" - LIBS="$LIBS -lm" - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - #if (__GNUC__ >= 4) || (__clang_major__ >= 4) - # undef isnanl - # define isnanl(x) __builtin_isnan ((long double)(x)) - #elif defined isnan - # undef isnanl - # define isnanl(x) isnan ((long double)(x)) - #endif - long double x;]], - [[return isnanl (x);]])], - [gl_cv_func_isnanl_in_libm=yes], - [gl_cv_func_isnanl_in_libm=no]) - LIBS="$save_LIBS" - ]) -]) - -dnl Test whether isnanl() recognizes all canonical numbers which are neither -dnl finite nor infinite. -AC_DEFUN([gl_FUNC_ISNANL_WORKS], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([gl_BIGENDIAN]) - AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether isnanl works], [gl_cv_func_isnanl_works], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#include -#if (__GNUC__ >= 4) || (__clang_major__ >= 4) -# undef isnanl -# define isnanl(x) __builtin_isnan ((long double)(x)) -#elif defined isnan -# undef isnanl -# define isnanl(x) isnan ((long double)(x)) -#endif -#define NWORDS \ - ((sizeof (long double) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) -typedef union { unsigned int word[NWORDS]; long double value; } - memory_long_double; -/* On Irix 6.5, gcc 3.4.3 can't compute compile-time NaN, and needs the - runtime type conversion. */ -#ifdef __sgi -static long double NaNl () -{ - double zero = 0.0; - return zero / zero; -} -#else -# define NaNl() (0.0L / 0.0L) -#endif -int main () -{ - int result = 0; - - if (!isnanl (NaNl ())) - result |= 1; - - { - memory_long_double m; - unsigned int i; - - /* The isnanl function should be immune against changes in the sign bit and - in the mantissa bits. The xor operation twiddles a bit that can only be - a sign bit or a mantissa bit (since the exponent never extends to - bit 31). */ - m.value = NaNl (); - m.word[NWORDS / 2] ^= (unsigned int) 1 << (sizeof (unsigned int) * CHAR_BIT - 1); - for (i = 0; i < NWORDS; i++) - m.word[i] |= 1; - if (!isnanl (m.value)) - result |= 1; - } - -#if ((defined __ia64 && LDBL_MANT_DIG == 64) || (defined __x86_64__ || defined __amd64__) || (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_)) && !HAVE_SAME_LONG_DOUBLE_AS_DOUBLE -/* Representation of an 80-bit 'long double' as an initializer for a sequence - of 'unsigned int' words. */ -# ifdef WORDS_BIGENDIAN -# define LDBL80_WORDS(exponent,manthi,mantlo) \ - { ((unsigned int) (exponent) << 16) | ((unsigned int) (manthi) >> 16), \ - ((unsigned int) (manthi) << 16) | ((unsigned int) (mantlo) >> 16), \ - (unsigned int) (mantlo) << 16 \ - } -# else -# define LDBL80_WORDS(exponent,manthi,mantlo) \ - { mantlo, manthi, exponent } -# endif - { /* Quiet NaN. */ - static memory_long_double x = - { LDBL80_WORDS (0xFFFF, 0xC3333333, 0x00000000) }; - if (!isnanl (x.value)) - result |= 2; - } - { - /* Signalling NaN. */ - static memory_long_double x = - { LDBL80_WORDS (0xFFFF, 0x83333333, 0x00000000) }; - if (!isnanl (x.value)) - result |= 2; - } - /* isnanl should return something even for noncanonical values. */ - { /* Pseudo-NaN. */ - static memory_long_double x = - { LDBL80_WORDS (0xFFFF, 0x40000001, 0x00000000) }; - if (isnanl (x.value) && !isnanl (x.value)) - result |= 4; - } - { /* Pseudo-Infinity. */ - static memory_long_double x = - { LDBL80_WORDS (0xFFFF, 0x00000000, 0x00000000) }; - if (isnanl (x.value) && !isnanl (x.value)) - result |= 8; - } - { /* Pseudo-Zero. */ - static memory_long_double x = - { LDBL80_WORDS (0x4004, 0x00000000, 0x00000000) }; - if (isnanl (x.value) && !isnanl (x.value)) - result |= 16; - } - { /* Unnormalized number. */ - static memory_long_double x = - { LDBL80_WORDS (0x4000, 0x63333333, 0x00000000) }; - if (isnanl (x.value) && !isnanl (x.value)) - result |= 32; - } - { /* Pseudo-Denormal. */ - static memory_long_double x = - { LDBL80_WORDS (0x0000, 0x83333333, 0x00000000) }; - if (isnanl (x.value) && !isnanl (x.value)) - result |= 64; - } -#endif - - return result; -}]])], - [gl_cv_func_isnanl_works=yes], - [gl_cv_func_isnanl_works=no], - [case "$host_os" in - mingw*) # Guess yes on mingw, no on MSVC. - AC_EGREP_CPP([Known], [ -#ifdef __MINGW32__ - Known -#endif - ], - [gl_cv_func_isnanl_works="guessing yes"], - [gl_cv_func_isnanl_works="guessing no"]) - ;; - *) gl_cv_func_isnanl_works="guessing yes" ;; - esac - ]) - ]) -]) diff --git a/m4/ldexpl.m4 b/m4/ldexpl.m4 deleted file mode 100644 index f2785d67c5b..00000000000 --- a/m4/ldexpl.m4 +++ /dev/null @@ -1,135 +0,0 @@ -# ldexpl.m4 serial 17 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_LDEXPL], -[ - AC_REQUIRE([gl_MATH_H_DEFAULTS]) - AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE]) - AC_REQUIRE([gl_FUNC_ISNANL]) dnl for ISNANL_LIBM - - dnl Persuade glibc to declare ldexpl(). - AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) - - dnl Check whether it's declared. - dnl Mac OS X 10.3 has ldexpl() in libc but doesn't declare it in . - AC_CHECK_DECL([ldexpl], , [HAVE_DECL_LDEXPL=0], [[#include ]]) - - LDEXPL_LIBM= - if test $HAVE_DECL_LDEXPL = 1; then - gl_CHECK_LDEXPL_NO_LIBM - if test $gl_cv_func_ldexpl_no_libm = no; then - AC_CACHE_CHECK([whether ldexpl() can be used with libm], - [gl_cv_func_ldexpl_in_libm], - [ - save_LIBS="$LIBS" - LIBS="$LIBS -lm" - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - long double x;]], - [[return ldexpl (x, -1) > 0;]])], - [gl_cv_func_ldexpl_in_libm=yes], - [gl_cv_func_ldexpl_in_libm=no]) - LIBS="$save_LIBS" - ]) - if test $gl_cv_func_ldexpl_in_libm = yes; then - LDEXPL_LIBM=-lm - fi - fi - if test $gl_cv_func_ldexpl_no_libm = yes \ - || test $gl_cv_func_ldexpl_in_libm = yes; then - save_LIBS="$LIBS" - LIBS="$LIBS $LDEXPL_LIBM" - gl_FUNC_LDEXPL_WORKS - LIBS="$save_LIBS" - case "$gl_cv_func_ldexpl_works" in - *yes) gl_func_ldexpl=yes ;; - *) gl_func_ldexpl=no; REPLACE_LDEXPL=1 ;; - esac - else - gl_func_ldexpl=no - fi - if test $gl_func_ldexpl = yes; then - AC_DEFINE([HAVE_LDEXPL], [1], - [Define if the ldexpl() function is available.]) - fi - fi - if test $HAVE_DECL_LDEXPL = 0 || test $gl_func_ldexpl = no; then - dnl Find libraries needed to link lib/ldexpl.c. - if test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 1; then - AC_REQUIRE([gl_FUNC_LDEXP]) - LDEXPL_LIBM="$LDEXP_LIBM" - else - LDEXPL_LIBM="$ISNANL_LIBM" - fi - fi - AC_SUBST([LDEXPL_LIBM]) -]) - -dnl Test whether ldexpl() can be used without linking with libm. -dnl Set gl_cv_func_ldexpl_no_libm to 'yes' or 'no' accordingly. -AC_DEFUN([gl_CHECK_LDEXPL_NO_LIBM], -[ - AC_CACHE_CHECK([whether ldexpl() can be used without linking with libm], - [gl_cv_func_ldexpl_no_libm], - [ - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - long double x;]], - [[return ldexpl (x, -1) > 0;]])], - [gl_cv_func_ldexpl_no_libm=yes], - [gl_cv_func_ldexpl_no_libm=no]) - ]) -]) - -dnl Test whether ldexpl() works on finite numbers (this fails on AIX 5.1 -dnl and Mac OS X 10.4/PowerPC). -AC_DEFUN([gl_FUNC_LDEXPL_WORKS], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether ldexpl works], [gl_cv_func_ldexpl_works], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -extern -#ifdef __cplusplus -"C" -#endif -long double ldexpl (long double, int); -int main() -{ - int result = 0; - { - volatile long double x = 1.0; - volatile long double y = ldexpl (x, -1); - if (y != 0.5L) - result |= 1; - } - { - volatile long double x = 1.73205L; - volatile long double y = ldexpl (x, 0); - if (y != x) - result |= 2; - } - return result; -}]])], - [gl_cv_func_ldexpl_works=yes], - [gl_cv_func_ldexpl_works=no], - [ -changequote(,)dnl - case "$host_os" in - aix | aix[3-6]*) gl_cv_func_ldexpl_works="guessing no" ;; - # Guess yes on native Windows. - mingw*) gl_cv_func_ldexpl_works="guessing yes" ;; - *) gl_cv_func_ldexpl_works="guessing yes" ;; - esac -changequote([,])dnl - ]) - ]) -]) diff --git a/m4/math_h.m4 b/m4/math_h.m4 deleted file mode 100644 index d2e90ff1eb6..00000000000 --- a/m4/math_h.m4 +++ /dev/null @@ -1,391 +0,0 @@ -# math_h.m4 serial 125 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN_ONCE([gl_MATH_H], -[ - AC_REQUIRE([gl_MATH_H_DEFAULTS]) - gl_CHECK_NEXT_HEADERS([math.h]) - - AC_CACHE_CHECK([whether NAN macro works], [gl_cv_header_math_nan_works], - [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], - [[/* Solaris 10 has a broken definition of NAN. Other platforms - fail to provide NAN, or provide it only in C99 mode; this - test only needs to fail when NAN is provided but wrong. */ - float f = 1.0f; -#ifdef NAN - f = NAN; -#endif - return f == 0;]])], - [gl_cv_header_math_nan_works=yes], - [gl_cv_header_math_nan_works=no])]) - if test $gl_cv_header_math_nan_works = no; then - REPLACE_NAN=1 - fi - AC_CACHE_CHECK([whether HUGE_VAL works], [gl_cv_header_math_huge_val_works], - [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], - [[/* Solaris 10 has a broken definition of HUGE_VAL. */ - double d = HUGE_VAL; - return d == 0;]])], - [gl_cv_header_math_huge_val_works=yes], - [gl_cv_header_math_huge_val_works=no])]) - if test $gl_cv_header_math_huge_val_works = no; then - REPLACE_HUGE_VAL=1 - fi - - dnl Check for declarations of anything we want to poison if the - dnl corresponding gnulib module is not in use. - gl_WARN_ON_USE_PREPARE([[#include ]], - [acosf acosl asinf asinl atanf atanl - cbrt cbrtf cbrtl ceilf ceill copysign copysignf copysignl cosf cosl coshf - expf expl exp2 exp2f exp2l expm1 expm1f expm1l - fabsf fabsl floorf floorl fma fmaf fmal - fmod fmodf fmodl frexpf frexpl hypotf hypotl - ilogb ilogbf ilogbl - ldexpf ldexpl - log logf logl log10 log10f log10l log1p log1pf log1pl log2 log2f log2l - logb logbf logbl - modf modff modfl powf - remainder remainderf remainderl - rint rintf rintl round roundf roundl sinf sinl sinhf sqrtf sqrtl - tanf tanl tanhf trunc truncf truncl]) -]) - -# gl_MATH_MODULE_INDICATOR([modulename]) -# sets the shell variable that indicates the presence of the given module -# to a C preprocessor expression that will evaluate to 1. -# This macro invocation must not occur in macros that are AC_REQUIREd. -AC_DEFUN([gl_MATH_MODULE_INDICATOR], -[ - dnl Ensure to expand the default settings once only. - gl_MATH_H_REQUIRE_DEFAULTS - gl_MODULE_INDICATOR_SET_VARIABLE([$1]) - dnl Define it also as a C macro, for the benefit of the unit tests. - gl_MODULE_INDICATOR_FOR_TESTS([$1]) -]) - -# Initializes the default values for AC_SUBSTed shell variables. -# This macro must not be AC_REQUIREd. It must only be invoked, and only -# outside of macros or in macros that are not AC_REQUIREd. -AC_DEFUN([gl_MATH_H_REQUIRE_DEFAULTS], -[ - m4_defun(GL_MODULE_INDICATOR_PREFIX[_MATH_H_MODULE_INDICATOR_DEFAULTS], [ - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ACOSF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ACOSL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ASINF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ASINL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ATANF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ATANL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ATAN2F]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CBRT]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CBRTF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CBRTL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CEIL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CEILF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CEILL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COPYSIGN]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COPYSIGNF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COPYSIGNL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COSF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COSL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COSHF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXPF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXPL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXP2]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXP2F]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXP2L]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXPM1]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXPM1F]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXPM1L]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FABSF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FABSL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FLOOR]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FLOORF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FLOORL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMA]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMAF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMAL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMOD]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMODF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMODL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FREXPF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FREXP]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FREXPL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_HYPOT]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_HYPOTF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_HYPOTL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ILOGB]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ILOGBF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ILOGBL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISFINITE]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISINF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISNAN]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISNANF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISNAND]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISNANL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LDEXPF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LDEXPL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOGF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOGL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG10]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG10F]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG10L]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG1P]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG1PF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG1PL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG2]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG2F]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG2L]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOGB]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOGBF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOGBL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MODF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MODFF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MODFL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_POWF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_REMAINDER]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_REMAINDERF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_REMAINDERL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_RINT]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_RINTF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_RINTL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ROUND]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ROUNDF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ROUNDL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SIGNBIT]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SINF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SINL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SINHF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SQRTF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SQRTL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TANF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TANL]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TANHF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TRUNC]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TRUNCF]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TRUNCL]) - dnl Support Microsoft deprecated alias function names by default. - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_J0], [1]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_J1], [1]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_JN], [1]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_Y0], [1]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_Y1], [1]) - gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_YN], [1]) - ]) - m4_require(GL_MODULE_INDICATOR_PREFIX[_MATH_H_MODULE_INDICATOR_DEFAULTS]) - AC_REQUIRE([gl_MATH_H_DEFAULTS]) -]) - -AC_DEFUN([gl_MATH_H_DEFAULTS], -[ - dnl Assume proper GNU behavior unless another module says otherwise. - HAVE_ACOSF=1; AC_SUBST([HAVE_ACOSF]) - HAVE_ACOSL=1; AC_SUBST([HAVE_ACOSL]) - HAVE_ASINF=1; AC_SUBST([HAVE_ASINF]) - HAVE_ASINL=1; AC_SUBST([HAVE_ASINL]) - HAVE_ATANF=1; AC_SUBST([HAVE_ATANF]) - HAVE_ATANL=1; AC_SUBST([HAVE_ATANL]) - HAVE_ATAN2F=1; AC_SUBST([HAVE_ATAN2F]) - HAVE_CBRT=1; AC_SUBST([HAVE_CBRT]) - HAVE_CBRTF=1; AC_SUBST([HAVE_CBRTF]) - HAVE_CBRTL=1; AC_SUBST([HAVE_CBRTL]) - HAVE_COPYSIGN=1; AC_SUBST([HAVE_COPYSIGN]) - HAVE_COPYSIGNL=1; AC_SUBST([HAVE_COPYSIGNL]) - HAVE_COSF=1; AC_SUBST([HAVE_COSF]) - HAVE_COSL=1; AC_SUBST([HAVE_COSL]) - HAVE_COSHF=1; AC_SUBST([HAVE_COSHF]) - HAVE_EXPF=1; AC_SUBST([HAVE_EXPF]) - HAVE_EXPL=1; AC_SUBST([HAVE_EXPL]) - HAVE_EXPM1=1; AC_SUBST([HAVE_EXPM1]) - HAVE_EXPM1F=1; AC_SUBST([HAVE_EXPM1F]) - HAVE_FABSF=1; AC_SUBST([HAVE_FABSF]) - HAVE_FABSL=1; AC_SUBST([HAVE_FABSL]) - HAVE_FMA=1; AC_SUBST([HAVE_FMA]) - HAVE_FMAF=1; AC_SUBST([HAVE_FMAF]) - HAVE_FMAL=1; AC_SUBST([HAVE_FMAL]) - HAVE_FMODF=1; AC_SUBST([HAVE_FMODF]) - HAVE_FMODL=1; AC_SUBST([HAVE_FMODL]) - HAVE_FREXPF=1; AC_SUBST([HAVE_FREXPF]) - HAVE_HYPOTF=1; AC_SUBST([HAVE_HYPOTF]) - HAVE_HYPOTL=1; AC_SUBST([HAVE_HYPOTL]) - HAVE_ILOGB=1; AC_SUBST([HAVE_ILOGB]) - HAVE_ILOGBF=1; AC_SUBST([HAVE_ILOGBF]) - HAVE_ILOGBL=1; AC_SUBST([HAVE_ILOGBL]) - HAVE_ISNANF=1; AC_SUBST([HAVE_ISNANF]) - HAVE_ISNAND=1; AC_SUBST([HAVE_ISNAND]) - HAVE_ISNANL=1; AC_SUBST([HAVE_ISNANL]) - HAVE_LDEXPF=1; AC_SUBST([HAVE_LDEXPF]) - HAVE_LOGF=1; AC_SUBST([HAVE_LOGF]) - HAVE_LOGL=1; AC_SUBST([HAVE_LOGL]) - HAVE_LOG10F=1; AC_SUBST([HAVE_LOG10F]) - HAVE_LOG10L=1; AC_SUBST([HAVE_LOG10L]) - HAVE_LOG1P=1; AC_SUBST([HAVE_LOG1P]) - HAVE_LOG1PF=1; AC_SUBST([HAVE_LOG1PF]) - HAVE_LOG1PL=1; AC_SUBST([HAVE_LOG1PL]) - HAVE_LOGBF=1; AC_SUBST([HAVE_LOGBF]) - HAVE_LOGBL=1; AC_SUBST([HAVE_LOGBL]) - HAVE_MODFF=1; AC_SUBST([HAVE_MODFF]) - HAVE_MODFL=1; AC_SUBST([HAVE_MODFL]) - HAVE_POWF=1; AC_SUBST([HAVE_POWF]) - HAVE_REMAINDER=1; AC_SUBST([HAVE_REMAINDER]) - HAVE_REMAINDERF=1; AC_SUBST([HAVE_REMAINDERF]) - HAVE_RINT=1; AC_SUBST([HAVE_RINT]) - HAVE_RINTL=1; AC_SUBST([HAVE_RINTL]) - HAVE_SINF=1; AC_SUBST([HAVE_SINF]) - HAVE_SINL=1; AC_SUBST([HAVE_SINL]) - HAVE_SINHF=1; AC_SUBST([HAVE_SINHF]) - HAVE_SQRTF=1; AC_SUBST([HAVE_SQRTF]) - HAVE_SQRTL=1; AC_SUBST([HAVE_SQRTL]) - HAVE_TANF=1; AC_SUBST([HAVE_TANF]) - HAVE_TANL=1; AC_SUBST([HAVE_TANL]) - HAVE_TANHF=1; AC_SUBST([HAVE_TANHF]) - HAVE_DECL_ACOSL=1; AC_SUBST([HAVE_DECL_ACOSL]) - HAVE_DECL_ASINL=1; AC_SUBST([HAVE_DECL_ASINL]) - HAVE_DECL_ATANL=1; AC_SUBST([HAVE_DECL_ATANL]) - HAVE_DECL_CBRTF=1; AC_SUBST([HAVE_DECL_CBRTF]) - HAVE_DECL_CBRTL=1; AC_SUBST([HAVE_DECL_CBRTL]) - HAVE_DECL_CEILF=1; AC_SUBST([HAVE_DECL_CEILF]) - HAVE_DECL_CEILL=1; AC_SUBST([HAVE_DECL_CEILL]) - HAVE_DECL_COPYSIGNF=1; AC_SUBST([HAVE_DECL_COPYSIGNF]) - HAVE_DECL_COSL=1; AC_SUBST([HAVE_DECL_COSL]) - HAVE_DECL_EXPL=1; AC_SUBST([HAVE_DECL_EXPL]) - HAVE_DECL_EXP2=1; AC_SUBST([HAVE_DECL_EXP2]) - HAVE_DECL_EXP2F=1; AC_SUBST([HAVE_DECL_EXP2F]) - HAVE_DECL_EXP2L=1; AC_SUBST([HAVE_DECL_EXP2L]) - HAVE_DECL_EXPM1L=1; AC_SUBST([HAVE_DECL_EXPM1L]) - HAVE_DECL_FLOORF=1; AC_SUBST([HAVE_DECL_FLOORF]) - HAVE_DECL_FLOORL=1; AC_SUBST([HAVE_DECL_FLOORL]) - HAVE_DECL_FREXPL=1; AC_SUBST([HAVE_DECL_FREXPL]) - HAVE_DECL_LDEXPL=1; AC_SUBST([HAVE_DECL_LDEXPL]) - HAVE_DECL_LOGL=1; AC_SUBST([HAVE_DECL_LOGL]) - HAVE_DECL_LOG10L=1; AC_SUBST([HAVE_DECL_LOG10L]) - HAVE_DECL_LOG2=1; AC_SUBST([HAVE_DECL_LOG2]) - HAVE_DECL_LOG2F=1; AC_SUBST([HAVE_DECL_LOG2F]) - HAVE_DECL_LOG2L=1; AC_SUBST([HAVE_DECL_LOG2L]) - HAVE_DECL_LOGB=1; AC_SUBST([HAVE_DECL_LOGB]) - HAVE_DECL_REMAINDER=1; AC_SUBST([HAVE_DECL_REMAINDER]) - HAVE_DECL_REMAINDERL=1; AC_SUBST([HAVE_DECL_REMAINDERL]) - HAVE_DECL_RINTF=1; AC_SUBST([HAVE_DECL_RINTF]) - HAVE_DECL_ROUND=1; AC_SUBST([HAVE_DECL_ROUND]) - HAVE_DECL_ROUNDF=1; AC_SUBST([HAVE_DECL_ROUNDF]) - HAVE_DECL_ROUNDL=1; AC_SUBST([HAVE_DECL_ROUNDL]) - HAVE_DECL_SINL=1; AC_SUBST([HAVE_DECL_SINL]) - HAVE_DECL_SQRTL=1; AC_SUBST([HAVE_DECL_SQRTL]) - HAVE_DECL_TANL=1; AC_SUBST([HAVE_DECL_TANL]) - HAVE_DECL_TRUNC=1; AC_SUBST([HAVE_DECL_TRUNC]) - HAVE_DECL_TRUNCF=1; AC_SUBST([HAVE_DECL_TRUNCF]) - HAVE_DECL_TRUNCL=1; AC_SUBST([HAVE_DECL_TRUNCL]) - REPLACE_ACOSF=0; AC_SUBST([REPLACE_ACOSF]) - REPLACE_ASINF=0; AC_SUBST([REPLACE_ASINF]) - REPLACE_ATANF=0; AC_SUBST([REPLACE_ATANF]) - REPLACE_ATAN2F=0; AC_SUBST([REPLACE_ATAN2F]) - REPLACE_CBRTF=0; AC_SUBST([REPLACE_CBRTF]) - REPLACE_CBRTL=0; AC_SUBST([REPLACE_CBRTL]) - REPLACE_CEIL=0; AC_SUBST([REPLACE_CEIL]) - REPLACE_CEILF=0; AC_SUBST([REPLACE_CEILF]) - REPLACE_CEILL=0; AC_SUBST([REPLACE_CEILL]) - REPLACE_COSF=0; AC_SUBST([REPLACE_COSF]) - REPLACE_COSHF=0; AC_SUBST([REPLACE_COSHF]) - REPLACE_EXPF=0; AC_SUBST([REPLACE_EXPF]) - REPLACE_EXPL=0; AC_SUBST([REPLACE_EXPL]) - REPLACE_EXPM1=0; AC_SUBST([REPLACE_EXPM1]) - REPLACE_EXPM1F=0; AC_SUBST([REPLACE_EXPM1F]) - REPLACE_EXPM1L=0; AC_SUBST([REPLACE_EXPM1L]) - REPLACE_EXP2=0; AC_SUBST([REPLACE_EXP2]) - REPLACE_EXP2L=0; AC_SUBST([REPLACE_EXP2L]) - REPLACE_FABSL=0; AC_SUBST([REPLACE_FABSL]) - REPLACE_FLOOR=0; AC_SUBST([REPLACE_FLOOR]) - REPLACE_FLOORF=0; AC_SUBST([REPLACE_FLOORF]) - REPLACE_FLOORL=0; AC_SUBST([REPLACE_FLOORL]) - REPLACE_FMA=0; AC_SUBST([REPLACE_FMA]) - REPLACE_FMAF=0; AC_SUBST([REPLACE_FMAF]) - REPLACE_FMAL=0; AC_SUBST([REPLACE_FMAL]) - REPLACE_FMOD=0; AC_SUBST([REPLACE_FMOD]) - REPLACE_FMODF=0; AC_SUBST([REPLACE_FMODF]) - REPLACE_FMODL=0; AC_SUBST([REPLACE_FMODL]) - REPLACE_FREXPF=0; AC_SUBST([REPLACE_FREXPF]) - REPLACE_FREXP=0; AC_SUBST([REPLACE_FREXP]) - REPLACE_FREXPL=0; AC_SUBST([REPLACE_FREXPL]) - REPLACE_HUGE_VAL=0; AC_SUBST([REPLACE_HUGE_VAL]) - REPLACE_HYPOT=0; AC_SUBST([REPLACE_HYPOT]) - REPLACE_HYPOTF=0; AC_SUBST([REPLACE_HYPOTF]) - REPLACE_HYPOTL=0; AC_SUBST([REPLACE_HYPOTL]) - REPLACE_ILOGB=0; AC_SUBST([REPLACE_ILOGB]) - REPLACE_ILOGBF=0; AC_SUBST([REPLACE_ILOGBF]) - REPLACE_ILOGBL=0; AC_SUBST([REPLACE_ILOGBL]) - REPLACE_ISFINITE=0; AC_SUBST([REPLACE_ISFINITE]) - REPLACE_ISINF=0; AC_SUBST([REPLACE_ISINF]) - REPLACE_ISNAN=0; AC_SUBST([REPLACE_ISNAN]) - REPLACE_LDEXPL=0; AC_SUBST([REPLACE_LDEXPL]) - REPLACE_LOG=0; AC_SUBST([REPLACE_LOG]) - REPLACE_LOGF=0; AC_SUBST([REPLACE_LOGF]) - REPLACE_LOGL=0; AC_SUBST([REPLACE_LOGL]) - REPLACE_LOG10=0; AC_SUBST([REPLACE_LOG10]) - REPLACE_LOG10F=0; AC_SUBST([REPLACE_LOG10F]) - REPLACE_LOG10L=0; AC_SUBST([REPLACE_LOG10L]) - REPLACE_LOG1P=0; AC_SUBST([REPLACE_LOG1P]) - REPLACE_LOG1PF=0; AC_SUBST([REPLACE_LOG1PF]) - REPLACE_LOG1PL=0; AC_SUBST([REPLACE_LOG1PL]) - REPLACE_LOG2=0; AC_SUBST([REPLACE_LOG2]) - REPLACE_LOG2F=0; AC_SUBST([REPLACE_LOG2F]) - REPLACE_LOG2L=0; AC_SUBST([REPLACE_LOG2L]) - REPLACE_LOGB=0; AC_SUBST([REPLACE_LOGB]) - REPLACE_LOGBF=0; AC_SUBST([REPLACE_LOGBF]) - REPLACE_LOGBL=0; AC_SUBST([REPLACE_LOGBL]) - REPLACE_MODF=0; AC_SUBST([REPLACE_MODF]) - REPLACE_MODFF=0; AC_SUBST([REPLACE_MODFF]) - REPLACE_MODFL=0; AC_SUBST([REPLACE_MODFL]) - REPLACE_NAN=0; AC_SUBST([REPLACE_NAN]) - REPLACE_REMAINDER=0; AC_SUBST([REPLACE_REMAINDER]) - REPLACE_REMAINDERF=0; AC_SUBST([REPLACE_REMAINDERF]) - REPLACE_REMAINDERL=0; AC_SUBST([REPLACE_REMAINDERL]) - REPLACE_RINTL=0; AC_SUBST([REPLACE_RINTL]) - REPLACE_ROUND=0; AC_SUBST([REPLACE_ROUND]) - REPLACE_ROUNDF=0; AC_SUBST([REPLACE_ROUNDF]) - REPLACE_ROUNDL=0; AC_SUBST([REPLACE_ROUNDL]) - REPLACE_SIGNBIT=0; AC_SUBST([REPLACE_SIGNBIT]) - REPLACE_SIGNBIT_USING_BUILTINS=0; AC_SUBST([REPLACE_SIGNBIT_USING_BUILTINS]) - REPLACE_SINF=0; AC_SUBST([REPLACE_SINF]) - REPLACE_SINHF=0; AC_SUBST([REPLACE_SINHF]) - REPLACE_SQRTF=0; AC_SUBST([REPLACE_SQRTF]) - REPLACE_SQRTL=0; AC_SUBST([REPLACE_SQRTL]) - REPLACE_TANF=0; AC_SUBST([REPLACE_TANF]) - REPLACE_TANHF=0; AC_SUBST([REPLACE_TANHF]) - REPLACE_TRUNC=0; AC_SUBST([REPLACE_TRUNC]) - REPLACE_TRUNCF=0; AC_SUBST([REPLACE_TRUNCF]) - REPLACE_TRUNCL=0; AC_SUBST([REPLACE_TRUNCL]) -]) - -# gl_LONG_DOUBLE_VS_DOUBLE -# determines whether 'long double' and 'double' have the same representation. -# Sets variable HAVE_SAME_LONG_DOUBLE_AS_DOUBLE to 0 or 1, and defines -# HAVE_SAME_LONG_DOUBLE_AS_DOUBLE accordingly. -# The currently known platforms where this is the case are: -# Linux/HPPA, Minix 3.1.8, AIX 5, AIX 6 and 7 with xlc, MSVC 9. -AC_DEFUN([gl_LONG_DOUBLE_VS_DOUBLE], -[ - AC_CACHE_CHECK([whether long double and double are the same], - [gl_cv_long_double_equals_double], - [AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([[#include ]], - [[typedef int check[sizeof (long double) == sizeof (double) - && LDBL_MANT_DIG == DBL_MANT_DIG - && LDBL_MAX_EXP == DBL_MAX_EXP - && LDBL_MIN_EXP == DBL_MIN_EXP - ? 1 : -1]; - ]])], - [gl_cv_long_double_equals_double=yes], - [gl_cv_long_double_equals_double=no]) - ]) - if test $gl_cv_long_double_equals_double = yes; then - AC_DEFINE([HAVE_SAME_LONG_DOUBLE_AS_DOUBLE], [1], - [Define to 1 if 'long double' and 'double' have the same representation.]) - HAVE_SAME_LONG_DOUBLE_AS_DOUBLE=1 - else - HAVE_SAME_LONG_DOUBLE_AS_DOUBLE=0 - fi - AC_SUBST([HAVE_SAME_LONG_DOUBLE_AS_DOUBLE]) -]) diff --git a/m4/printf-frexp.m4 b/m4/printf-frexp.m4 deleted file mode 100644 index 8f5844a2c1c..00000000000 --- a/m4/printf-frexp.m4 +++ /dev/null @@ -1,38 +0,0 @@ -# printf-frexp.m4 serial 5 -dnl Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl Check how to define printf_frexp() without linking with libm. - -AC_DEFUN([gl_FUNC_PRINTF_FREXP], -[ - AC_REQUIRE([gl_CHECK_FREXP_NO_LIBM]) - if test $gl_cv_func_frexp_no_libm = yes; then - gl_FUNC_FREXP_WORKS - case "$gl_cv_func_frexp_works" in - *yes) - AC_DEFINE([HAVE_FREXP_IN_LIBC], [1], - [Define if the frexp function is available in libc.]) - ;; - esac - fi - - AC_CACHE_CHECK([whether ldexp can be used without linking with libm], - [gl_cv_func_ldexp_no_libm], - [ - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - double x; - int y;]], - [[return ldexp (x, y) < 1;]])], - [gl_cv_func_ldexp_no_libm=yes], - [gl_cv_func_ldexp_no_libm=no]) - ]) - if test $gl_cv_func_ldexp_no_libm = yes; then - AC_DEFINE([HAVE_LDEXP_IN_LIBC], [1], - [Define if the ldexp function is available in libc.]) - fi -]) diff --git a/m4/printf-frexpl.m4 b/m4/printf-frexpl.m4 deleted file mode 100644 index aee170ffd39..00000000000 --- a/m4/printf-frexpl.m4 +++ /dev/null @@ -1,48 +0,0 @@ -# printf-frexpl.m4 serial 10 -dnl Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl Check how to define printf_frexpl() without linking with libm. - -AC_DEFUN([gl_FUNC_PRINTF_FREXPL], -[ - AC_REQUIRE([gl_MATH_H_DEFAULTS]) - AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE]) - - dnl Subset of gl_FUNC_FREXPL_NO_LIBM. - gl_CHECK_FREXPL_NO_LIBM - if test $gl_cv_func_frexpl_no_libm = yes; then - gl_FUNC_FREXPL_WORKS - case "$gl_cv_func_frexpl_works" in - *yes) gl_func_frexpl_no_libm=yes ;; - *) gl_func_frexpl_no_libm=no; REPLACE_FREXPL=1 ;; - esac - else - gl_func_frexpl_no_libm=no - dnl Set REPLACE_FREXPL here because the system may have frexpl in libm. - REPLACE_FREXPL=1 - fi - if test $gl_func_frexpl_no_libm = yes; then - AC_DEFINE([HAVE_FREXPL_IN_LIBC], [1], - [Define if the frexpl function is available in libc.]) - dnl Also check whether it's declared. - dnl Mac OS X 10.3 has frexpl() in libc but doesn't declare it in . - AC_CHECK_DECL([frexpl], , [HAVE_DECL_FREXPL=0], [[#include ]]) - fi - - gl_CHECK_LDEXPL_NO_LIBM - if test $gl_cv_func_ldexpl_no_libm = yes; then - gl_FUNC_LDEXPL_WORKS - case "$gl_cv_func_ldexpl_works" in - *yes) - AC_DEFINE([HAVE_LDEXPL_IN_LIBC], [1], - [Define if the ldexpl function is available in libc.]) - dnl Also check whether it's declared. - dnl Mac OS X 10.3 has ldexpl() in libc but doesn't declare it in . - AC_CHECK_DECL([ldexpl], , [HAVE_DECL_LDEXPL=0], [[#include ]]) - ;; - esac - fi -]) diff --git a/m4/printf-posix.m4 b/m4/printf-posix.m4 deleted file mode 100644 index 9aebf4002d6..00000000000 --- a/m4/printf-posix.m4 +++ /dev/null @@ -1,36 +0,0 @@ -# printf-posix.m4 serial 5 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_PRINTF_POSIX], -[ - AC_REQUIRE([gl_FUNC_PRINTF_IS_POSIX]) - if test $gl_cv_func_printf_posix = no; then - gl_PREREQ_VASNPRINTF_WITH_POSIX_EXTRAS - gl_REPLACE_VASNPRINTF - gl_REPLACE_PRINTF - fi -]) - -dnl Test whether printf is POSIX compliant. -dnl Result is gl_cv_func_printf_posix. -AC_DEFUN([gl_FUNC_PRINTF_IS_POSIX], -[ - AC_REQUIRE([gl_FUNC_VFPRINTF_IS_POSIX]) - gl_cv_func_printf_posix="$gl_cv_func_vfprintf_posix" -]) - -AC_DEFUN([gl_REPLACE_PRINTF], -[ - AC_REQUIRE([gl_STDIO_H_DEFAULTS]) - AC_REQUIRE([gl_ASM_SYMBOL_PREFIX]) - AC_LIBOBJ([printf]) - REPLACE_PRINTF=1 - AC_DEFINE([REPLACE_PRINTF_POSIX], [1], - [Define if printf is overridden by a POSIX compliant gnulib implementation.]) - gl_PREREQ_PRINTF -]) - -AC_DEFUN([gl_PREREQ_PRINTF], [:]) diff --git a/m4/printf.m4 b/m4/printf.m4 deleted file mode 100644 index 8b8f01067fc..00000000000 --- a/m4/printf.m4 +++ /dev/null @@ -1,2158 +0,0 @@ -# printf.m4 serial 84 -dnl Copyright (C) 2003, 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl Test whether the *printf family of functions supports the 'j', 'z', 't', -dnl 'L' size specifiers. (ISO C99, POSIX:2001) -dnl Result is gl_cv_func_printf_sizes_c99. - -AC_DEFUN([gl_PRINTF_SIZES_C99], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([gl_AC_HEADER_STDINT_H]) - AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports size specifiers as in C99], - [gl_cv_func_printf_sizes_c99], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#include -#include -#if HAVE_STDINT_H_WITH_UINTMAX -# include -#endif -#if HAVE_INTTYPES_H_WITH_UINTMAX -# include -#endif -static char buf[100]; -int main () -{ - int result = 0; -#if HAVE_STDINT_H_WITH_UINTMAX || HAVE_INTTYPES_H_WITH_UINTMAX - buf[0] = '\0'; - if (sprintf (buf, "%ju %d", (uintmax_t) 12345671, 33, 44, 55) < 0 - || strcmp (buf, "12345671 33") != 0) - result |= 1; -#else - result |= 1; -#endif - buf[0] = '\0'; - if (sprintf (buf, "%zu %d", (size_t) 12345672, 33, 44, 55) < 0 - || strcmp (buf, "12345672 33") != 0) - result |= 2; - buf[0] = '\0'; - if (sprintf (buf, "%tu %d", (ptrdiff_t) 12345673, 33, 44, 55) < 0 - || strcmp (buf, "12345673 33") != 0) - result |= 4; - buf[0] = '\0'; - if (sprintf (buf, "%Lg %d", (long double) 1.5, 33, 44, 55) < 0 - || strcmp (buf, "1.5 33") != 0) - result |= 8; - return result; -}]])], - [gl_cv_func_printf_sizes_c99=yes], - [gl_cv_func_printf_sizes_c99=no], - [ - case "$host_os" in -changequote(,)dnl - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_printf_sizes_c99="guessing yes";; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_printf_sizes_c99="guessing yes";; - # Guess yes on FreeBSD >= 5. - freebsd[1-4].*) gl_cv_func_printf_sizes_c99="guessing no";; - freebsd* | kfreebsd*) gl_cv_func_printf_sizes_c99="guessing yes";; - midnightbsd*) gl_cv_func_printf_sizes_c99="guessing yes";; - # Guess yes on Mac OS X >= 10.3. - darwin[1-6].*) gl_cv_func_printf_sizes_c99="guessing no";; - darwin*) gl_cv_func_printf_sizes_c99="guessing yes";; - # Guess yes on OpenBSD >= 3.9. - openbsd[1-2].* | openbsd3.[0-8] | openbsd3.[0-8].*) - gl_cv_func_printf_sizes_c99="guessing no";; - openbsd*) gl_cv_func_printf_sizes_c99="guessing yes";; - # Guess yes on Solaris >= 2.10. - solaris2.[1-9][0-9]*) gl_cv_func_printf_sizes_c99="guessing yes";; - solaris*) gl_cv_func_printf_sizes_c99="guessing no";; - # Guess yes on NetBSD >= 3. - netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*) - gl_cv_func_printf_sizes_c99="guessing no";; - netbsd*) gl_cv_func_printf_sizes_c99="guessing yes";; - # Guess yes on Android. - linux*-android*) gl_cv_func_printf_sizes_c99="guessing yes";; -changequote([,])dnl - # Guess yes on MSVC, no on mingw. - mingw*) AC_EGREP_CPP([Known], [ -#ifdef _MSC_VER - Known -#endif - ], - [gl_cv_func_printf_sizes_c99="guessing yes"], - [gl_cv_func_printf_sizes_c99="guessing no"]) - ;; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_printf_sizes_c99="$gl_cross_guess_normal";; - esac - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports the 'w8', 'w16', -dnl 'w32', 'w64', 'wf8', 'wf16', 'wf32', 'wf64' size specifiers. (ISO C23) -dnl Result is gl_cv_func_printf_sizes_c23. - -AC_DEFUN([gl_PRINTF_SIZES_C23], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([gl_AC_HEADER_STDINT_H]) - AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports size specifiers as in C23], - [gl_cv_func_printf_sizes_c23], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#include -#include -#if HAVE_STDINT_H_WITH_UINTMAX -# include -#endif -#if HAVE_INTTYPES_H_WITH_UINTMAX -# include -#endif -static char buf[100]; -int main () -{ - int result = 0; - buf[0] = '\0'; - if (sprintf (buf, "%w8u %d", (uint8_t) 123, 33, 44, 55) < 0 - || strcmp (buf, "123 33") != 0) - result |= 1; - buf[0] = '\0'; - if (sprintf (buf, "%wf8u %d", (uint_fast8_t) 123, 33, 44, 55) < 0 - || strcmp (buf, "123 33") != 0) - result |= 1; - buf[0] = '\0'; - if (sprintf (buf, "%w16u %d", (uint16_t) 12345, 33, 44, 55) < 0 - || strcmp (buf, "12345 33") != 0) - result |= 2; - buf[0] = '\0'; - if (sprintf (buf, "%wf16u %d", (uint_fast16_t) 12345, 33, 44, 55) < 0 - || strcmp (buf, "12345 33") != 0) - result |= 2; - buf[0] = '\0'; - if (sprintf (buf, "%w32u %d", (uint32_t) 12345671, 33, 44, 55) < 0 - || strcmp (buf, "12345671 33") != 0) - result |= 4; - buf[0] = '\0'; - if (sprintf (buf, "%wf32u %d", (uint_fast32_t) 12345671, 33, 44, 55) < 0 - || strcmp (buf, "12345671 33") != 0) - result |= 4; -#if HAVE_STDINT_H_WITH_UINTMAX || HAVE_INTTYPES_H_WITH_UINTMAX - buf[0] = '\0'; - if (sprintf (buf, "%w64u %d", (uint64_t) 12345671, 33, 44, 55) < 0 - || strcmp (buf, "12345671 33") != 0) - result |= 8; - buf[0] = '\0'; - if (sprintf (buf, "%wf64u %d", (uint_fast64_t) 12345671, 33, 44, 55) < 0 - || strcmp (buf, "12345671 33") != 0) - result |= 8; -#else - result |= 8; -#endif - return result; -}]])], - [gl_cv_func_printf_sizes_c23=yes], - [gl_cv_func_printf_sizes_c23=no], - [ - case "$host_os" in - # Guess no on glibc systems. - *-gnu* | gnu*) gl_cv_func_printf_sizes_c23="guessing no";; - # Guess no on musl systems. - *-musl* | midipix*) gl_cv_func_printf_sizes_c23="guessing no";; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_sizes_c23="guessing no";; - # Guess no on native Windows. - mingw*) gl_cv_func_printf_sizes_c23="guessing no";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_printf_sizes_c23="$gl_cross_guess_normal";; - esac - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports 'long double' -dnl arguments together with the 'L' size specifier. (ISO C99, POSIX:2001) -dnl Result is gl_cv_func_printf_long_double. - -AC_DEFUN([gl_PRINTF_LONG_DOUBLE], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports 'long double' arguments], - [gl_cv_func_printf_long_double], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static char buf[10000]; -int main () -{ - int result = 0; - buf[0] = '\0'; - if (sprintf (buf, "%Lf %d", 1.75L, 33, 44, 55) < 0 - || strcmp (buf, "1.750000 33") != 0) - result |= 1; - buf[0] = '\0'; - if (sprintf (buf, "%Le %d", 1.75L, 33, 44, 55) < 0 - || strcmp (buf, "1.750000e+00 33") != 0) - result |= 2; - buf[0] = '\0'; - if (sprintf (buf, "%Lg %d", 1.75L, 33, 44, 55) < 0 - || strcmp (buf, "1.75 33") != 0) - result |= 4; - return result; -}]])], - [gl_cv_func_printf_long_double=yes], - [gl_cv_func_printf_long_double=no], - [case "$host_os" in - # Guess no on BeOS. - beos*) gl_cv_func_printf_long_double="guessing no";; - # Guess yes on Android. - linux*-android*) gl_cv_func_printf_long_double="guessing yes";; - # Guess yes on MSVC, no on mingw. - mingw*) AC_EGREP_CPP([Known], [ -#ifdef _MSC_VER - Known -#endif - ], - [gl_cv_func_printf_long_double="guessing yes"], - [gl_cv_func_printf_long_double="guessing no"]) - ;; - *) gl_cv_func_printf_long_double="guessing yes";; - esac - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports infinite and NaN -dnl 'double' arguments and negative zero arguments in the %f, %e, %g -dnl directives. (ISO C99, POSIX:2001) -dnl Result is gl_cv_func_printf_infinite. - -AC_DEFUN([gl_PRINTF_INFINITE], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports infinite 'double' arguments], - [gl_cv_func_printf_infinite], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static int -strisnan (const char *string, size_t start_index, size_t end_index) -{ - if (start_index < end_index) - { - if (string[start_index] == '-') - start_index++; - if (start_index + 3 <= end_index - && memcmp (string + start_index, "nan", 3) == 0) - { - start_index += 3; - if (start_index == end_index - || (string[start_index] == '(' && string[end_index - 1] == ')')) - return 1; - } - } - return 0; -} -static int -have_minus_zero () -{ - static double plus_zero = 0.0; - double minus_zero = - plus_zero; - return memcmp (&plus_zero, &minus_zero, sizeof (double)) != 0; -} -static char buf[10000]; -static double zero = 0.0; -int main () -{ - int result = 0; - if (sprintf (buf, "%f", 1.0 / zero) < 0 - || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0)) - result |= 1; - if (sprintf (buf, "%f", -1.0 / zero) < 0 - || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0)) - result |= 1; - if (sprintf (buf, "%f", zero / zero) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 2; - if (sprintf (buf, "%e", 1.0 / zero) < 0 - || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0)) - result |= 4; - if (sprintf (buf, "%e", -1.0 / zero) < 0 - || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0)) - result |= 4; - if (sprintf (buf, "%e", zero / zero) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 8; - if (sprintf (buf, "%g", 1.0 / zero) < 0 - || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0)) - result |= 16; - if (sprintf (buf, "%g", -1.0 / zero) < 0 - || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0)) - result |= 16; - if (sprintf (buf, "%g", zero / zero) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 32; - /* This test fails on HP-UX 10.20. */ - if (have_minus_zero ()) - if (sprintf (buf, "%g", - zero) < 0 - || strcmp (buf, "-0") != 0) - result |= 64; - return result; -}]])], - [gl_cv_func_printf_infinite=yes], - [gl_cv_func_printf_infinite=no], - [ - case "$host_os" in -changequote(,)dnl - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_printf_infinite="guessing yes";; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_printf_infinite="guessing yes";; - # Guess yes on FreeBSD >= 6. - freebsd[1-5].*) gl_cv_func_printf_infinite="guessing no";; - freebsd* | kfreebsd*) gl_cv_func_printf_infinite="guessing yes";; - midnightbsd*) gl_cv_func_printf_infinite="guessing yes";; - # Guess yes on Mac OS X >= 10.3. - darwin[1-6].*) gl_cv_func_printf_infinite="guessing no";; - darwin*) gl_cv_func_printf_infinite="guessing yes";; - # Guess yes on HP-UX >= 11. - hpux[7-9]* | hpux10*) gl_cv_func_printf_infinite="guessing no";; - hpux*) gl_cv_func_printf_infinite="guessing yes";; - # Guess yes on NetBSD >= 3. - netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*) - gl_cv_func_printf_infinite="guessing no";; - netbsd*) gl_cv_func_printf_infinite="guessing yes";; - # Guess yes on OpenBSD >= 6.0. - openbsd[1-5].*) gl_cv_func_printf_infinite="guessing no";; - openbsd*) gl_cv_func_printf_infinite="guessing yes";; - # Guess yes on BeOS. - beos*) gl_cv_func_printf_infinite="guessing yes";; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_infinite="guessing no";; -changequote([,])dnl - # Guess yes on MSVC, no on mingw. - mingw*) AC_EGREP_CPP([Known], [ -#ifdef _MSC_VER - Known -#endif - ], - [gl_cv_func_printf_infinite="guessing yes"], - [gl_cv_func_printf_infinite="guessing no"]) - ;; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_printf_infinite="$gl_cross_guess_normal";; - esac - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports infinite and NaN -dnl 'long double' arguments in the %f, %e, %g directives. (ISO C99, POSIX:2001) -dnl Result is gl_cv_func_printf_infinite_long_double. - -AC_DEFUN([gl_PRINTF_INFINITE_LONG_DOUBLE], -[ - AC_REQUIRE([gl_PRINTF_LONG_DOUBLE]) - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([gl_BIGENDIAN]) - AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - dnl The user can set or unset the variable gl_printf_safe to indicate - dnl that he wishes a safe handling of non-IEEE-754 'long double' values. - if test -n "$gl_printf_safe"; then - AC_DEFINE([CHECK_PRINTF_SAFE], [1], - [Define if you wish *printf() functions that have a safe handling of - non-IEEE-754 'long double' values.]) - fi - case "$gl_cv_func_printf_long_double" in - *yes) - AC_CACHE_CHECK([whether printf supports infinite 'long double' arguments], - [gl_cv_func_printf_infinite_long_double], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -]GL_NOCRASH[ -#include -#include -#include -static int -strisnan (const char *string, size_t start_index, size_t end_index) -{ - if (start_index < end_index) - { - if (string[start_index] == '-') - start_index++; - if (start_index + 3 <= end_index - && memcmp (string + start_index, "nan", 3) == 0) - { - start_index += 3; - if (start_index == end_index - || (string[start_index] == '(' && string[end_index - 1] == ')')) - return 1; - } - } - return 0; -} -static char buf[10000]; -static long double zeroL = 0.0L; -int main () -{ - int result = 0; - nocrash_init(); - if (sprintf (buf, "%Lf", 1.0L / zeroL) < 0 - || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0)) - result |= 1; - if (sprintf (buf, "%Lf", -1.0L / zeroL) < 0 - || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0)) - result |= 1; - if (sprintf (buf, "%Lf", zeroL / zeroL) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 1; - if (sprintf (buf, "%Le", 1.0L / zeroL) < 0 - || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0)) - result |= 1; - if (sprintf (buf, "%Le", -1.0L / zeroL) < 0 - || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0)) - result |= 1; - if (sprintf (buf, "%Le", zeroL / zeroL) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 1; - if (sprintf (buf, "%Lg", 1.0L / zeroL) < 0 - || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0)) - result |= 1; - if (sprintf (buf, "%Lg", -1.0L / zeroL) < 0 - || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0)) - result |= 1; - if (sprintf (buf, "%Lg", zeroL / zeroL) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 1; -#if CHECK_PRINTF_SAFE && ((defined __ia64 && LDBL_MANT_DIG == 64) || (defined __x86_64__ || defined __amd64__) || (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_)) && !HAVE_SAME_LONG_DOUBLE_AS_DOUBLE -/* Representation of an 80-bit 'long double' as an initializer for a sequence - of 'unsigned int' words. */ -# ifdef WORDS_BIGENDIAN -# define LDBL80_WORDS(exponent,manthi,mantlo) \ - { ((unsigned int) (exponent) << 16) | ((unsigned int) (manthi) >> 16), \ - ((unsigned int) (manthi) << 16) | ((unsigned int) (mantlo) >> 16), \ - (unsigned int) (mantlo) << 16 \ - } -# else -# define LDBL80_WORDS(exponent,manthi,mantlo) \ - { mantlo, manthi, exponent } -# endif - { /* Quiet NaN. */ - static union { unsigned int word[4]; long double value; } x = - { LDBL80_WORDS (0xFFFF, 0xC3333333, 0x00000000) }; - if (sprintf (buf, "%Lf", x.value) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 2; - if (sprintf (buf, "%Le", x.value) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 2; - if (sprintf (buf, "%Lg", x.value) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 2; - } - { - /* Signalling NaN. */ - static union { unsigned int word[4]; long double value; } x = - { LDBL80_WORDS (0xFFFF, 0x83333333, 0x00000000) }; - if (sprintf (buf, "%Lf", x.value) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 2; - if (sprintf (buf, "%Le", x.value) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 2; - if (sprintf (buf, "%Lg", x.value) < 0 - || !strisnan (buf, 0, strlen (buf))) - result |= 2; - } - { /* Pseudo-NaN. */ - static union { unsigned int word[4]; long double value; } x = - { LDBL80_WORDS (0xFFFF, 0x40000001, 0x00000000) }; - if (sprintf (buf, "%Lf", x.value) <= 0) - result |= 4; - if (sprintf (buf, "%Le", x.value) <= 0) - result |= 4; - if (sprintf (buf, "%Lg", x.value) <= 0) - result |= 4; - } - { /* Pseudo-Infinity. */ - static union { unsigned int word[4]; long double value; } x = - { LDBL80_WORDS (0xFFFF, 0x00000000, 0x00000000) }; - if (sprintf (buf, "%Lf", x.value) <= 0) - result |= 8; - if (sprintf (buf, "%Le", x.value) <= 0) - result |= 8; - if (sprintf (buf, "%Lg", x.value) <= 0) - result |= 8; - } - { /* Pseudo-Zero. */ - static union { unsigned int word[4]; long double value; } x = - { LDBL80_WORDS (0x4004, 0x00000000, 0x00000000) }; - if (sprintf (buf, "%Lf", x.value) <= 0) - result |= 16; - if (sprintf (buf, "%Le", x.value) <= 0) - result |= 16; - if (sprintf (buf, "%Lg", x.value) <= 0) - result |= 16; - } - { /* Unnormalized number. */ - static union { unsigned int word[4]; long double value; } x = - { LDBL80_WORDS (0x4000, 0x63333333, 0x00000000) }; - if (sprintf (buf, "%Lf", x.value) <= 0) - result |= 32; - if (sprintf (buf, "%Le", x.value) <= 0) - result |= 32; - if (sprintf (buf, "%Lg", x.value) <= 0) - result |= 32; - } - { /* Pseudo-Denormal. */ - static union { unsigned int word[4]; long double value; } x = - { LDBL80_WORDS (0x0000, 0x83333333, 0x00000000) }; - if (sprintf (buf, "%Lf", x.value) <= 0) - result |= 64; - if (sprintf (buf, "%Le", x.value) <= 0) - result |= 64; - if (sprintf (buf, "%Lg", x.value) <= 0) - result |= 64; - } -#endif - return result; -}]])], - [gl_cv_func_printf_infinite_long_double=yes], - [gl_cv_func_printf_infinite_long_double=no], - [case "$host_cpu" in - # Guess no on ia64, x86_64, i386. - ia64 | x86_64 | i*86) gl_cv_func_printf_infinite_long_double="guessing no";; - *) - case "$host_os" in -changequote(,)dnl - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_printf_infinite_long_double="guessing yes";; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_printf_infinite_long_double="guessing yes";; - # Guess yes on FreeBSD >= 6. - freebsd[1-5].*) gl_cv_func_printf_infinite_long_double="guessing no";; - freebsd* | kfreebsd*) gl_cv_func_printf_infinite_long_double="guessing yes";; - midnightbsd*) gl_cv_func_printf_infinite_long_double="guessing yes";; - # Guess yes on HP-UX >= 11. - hpux[7-9]* | hpux10*) gl_cv_func_printf_infinite_long_double="guessing no";; - hpux*) gl_cv_func_printf_infinite_long_double="guessing yes";; - # Guess yes on OpenBSD >= 6.0. - openbsd[1-5].*) gl_cv_func_printf_infinite_long_double="guessing no";; - openbsd*) gl_cv_func_printf_infinite_long_double="guessing yes";; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_infinite_long_double="guessing no";; -changequote([,])dnl - # Guess yes on MSVC, no on mingw. - mingw*) AC_EGREP_CPP([Known], [ -#ifdef _MSC_VER - Known -#endif - ], - [gl_cv_func_printf_infinite_long_double="guessing yes"], - [gl_cv_func_printf_infinite_long_double="guessing no"]) - ;; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_printf_infinite_long_double="$gl_cross_guess_normal";; - esac - ;; - esac - ]) - ]) - ;; - *) - gl_cv_func_printf_infinite_long_double="irrelevant" - ;; - esac -]) - -dnl Test whether the *printf family of functions supports the 'a' and 'A' -dnl conversion specifier for hexadecimal output of floating-point numbers. -dnl (ISO C99, POSIX:2001) -dnl Result is gl_cv_func_printf_directive_a. - -AC_DEFUN([gl_PRINTF_DIRECTIVE_A], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports the 'a' and 'A' directives], - [gl_cv_func_printf_directive_a], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static char buf[100]; -static double zero = 0.0; -int main () -{ - int result = 0; - if (sprintf (buf, "%a %d", 3.1416015625, 33, 44, 55) < 0 - || (strcmp (buf, "0x1.922p+1 33") != 0 - && strcmp (buf, "0x3.244p+0 33") != 0 - && strcmp (buf, "0x6.488p-1 33") != 0 - && strcmp (buf, "0xc.91p-2 33") != 0)) - result |= 1; - if (sprintf (buf, "%A %d", -3.1416015625, 33, 44, 55) < 0 - || (strcmp (buf, "-0X1.922P+1 33") != 0 - && strcmp (buf, "-0X3.244P+0 33") != 0 - && strcmp (buf, "-0X6.488P-1 33") != 0 - && strcmp (buf, "-0XC.91P-2 33") != 0)) - result |= 2; - /* This catches a FreeBSD 13.0 bug: it doesn't round. */ - if (sprintf (buf, "%.2a %d", 1.51, 33, 44, 55) < 0 - || (strcmp (buf, "0x1.83p+0 33") != 0 - && strcmp (buf, "0x3.05p-1 33") != 0 - && strcmp (buf, "0x6.0ap-2 33") != 0 - && strcmp (buf, "0xc.14p-3 33") != 0)) - result |= 4; - /* This catches a Mac OS X 10.12.4 (Darwin 16.5) bug: it doesn't round. */ - if (sprintf (buf, "%.0a %d", 1.51, 33, 44, 55) < 0 - || (strcmp (buf, "0x2p+0 33") != 0 - && strcmp (buf, "0x3p-1 33") != 0 - && strcmp (buf, "0x6p-2 33") != 0 - && strcmp (buf, "0xcp-3 33") != 0)) - result |= 4; - /* This catches a FreeBSD 6.1 bug. See - */ - if (sprintf (buf, "%010a %d", 1.0 / zero, 33, 44, 55) < 0 - || buf[0] == '0') - result |= 8; - /* This catches a Mac OS X 10.3.9 (Darwin 7.9) bug. */ - if (sprintf (buf, "%.1a", 1.999) < 0 - || (strcmp (buf, "0x1.0p+1") != 0 - && strcmp (buf, "0x2.0p+0") != 0 - && strcmp (buf, "0x4.0p-1") != 0 - && strcmp (buf, "0x8.0p-2") != 0)) - result |= 16; - /* This catches the same Mac OS X 10.3.9 (Darwin 7.9) bug and also a - glibc 2.4 bug . */ - if (sprintf (buf, "%.1La", 1.999L) < 0 - || (strcmp (buf, "0x1.0p+1") != 0 - && strcmp (buf, "0x2.0p+0") != 0 - && strcmp (buf, "0x4.0p-1") != 0 - && strcmp (buf, "0x8.0p-2") != 0)) - result |= 32; - return result; -}]])], - [gl_cv_func_printf_directive_a=yes], - [gl_cv_func_printf_directive_a=no], - [ - case "$host_os" in - # Guess yes on glibc >= 2.5 systems. - *-gnu* | gnu*) - AC_EGREP_CPP([BZ2908], [ - #include - #ifdef __GNU_LIBRARY__ - #if ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 5) || (__GLIBC__ > 2)) && !defined __UCLIBC__ - BZ2908 - #endif - #endif - ], - [gl_cv_func_printf_directive_a="guessing yes"], - [gl_cv_func_printf_directive_a="guessing no"]) - ;; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_printf_directive_a="guessing yes";; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_directive_a="guessing no";; - # Guess no on native Windows. - mingw*) gl_cv_func_printf_directive_a="guessing no";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_printf_directive_a="$gl_cross_guess_normal";; - esac - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports the 'b' conversion -dnl specifier for binary output of integers. -dnl (ISO C23) -dnl Result is gl_cv_func_printf_directive_b. - -AC_DEFUN([gl_PRINTF_DIRECTIVE_B], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports the 'b' directive], - [gl_cv_func_printf_directive_b], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static char buf[100]; -int main () -{ - int result = 0; - if (sprintf (buf, "%b %d", 12345, 33, 44, 55) < 0 - || strcmp (buf, "11000000111001 33") != 0) - result |= 1; - return result; -}]])], - [gl_cv_func_printf_directive_b=yes], - [gl_cv_func_printf_directive_b=no], - [ - case "$host_os" in - # Guess yes on glibc >= 2.35 systems. - *-gnu* | gnu*) - AC_EGREP_CPP([Lucky], [ - #include - #ifdef __GNU_LIBRARY__ - #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 35) || (__GLIBC__ > 2) - Lucky user - #endif - #endif - ], - [gl_cv_func_printf_directive_uppercase_b="guessing yes"], - [gl_cv_func_printf_directive_uppercase_b="guessing no"]) - ;; - # Guess no on musl systems. - *-musl* | midipix*) gl_cv_func_printf_directive_b="guessing no";; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_directive_b="guessing no";; - # Guess no on native Windows. - mingw*) gl_cv_func_printf_directive_b="guessing no";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_printf_directive_b="$gl_cross_guess_normal";; - esac - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports the 'B' conversion -dnl specifier for binary output of integers. -dnl (GNU, encouraged by ISO C23 § 7.23.6.1) -dnl Result is gl_cv_func_printf_directive_uppercase_b. - -AC_DEFUN([gl_PRINTF_DIRECTIVE_UPPERCASE_B], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports the 'B' directive], - [gl_cv_func_printf_directive_uppercase_b], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static char buf[100]; -int main () -{ - int result = 0; - if (sprintf (buf, "%#B %d", 12345, 33, 44, 55) < 0 - || strcmp (buf, "0B11000000111001 33") != 0) - result |= 1; - return result; -}]])], - [gl_cv_func_printf_directive_uppercase_b=yes], - [gl_cv_func_printf_directive_uppercase_b=no], - [ - case "$host_os" in - # Guess yes on glibc >= 2.35 systems. - *-gnu* | gnu*) - AC_EGREP_CPP([Lucky], [ - #include - #ifdef __GNU_LIBRARY__ - #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 35) || (__GLIBC__ > 2) - Lucky user - #endif - #endif - ], - [gl_cv_func_printf_directive_uppercase_b="guessing yes"], - [gl_cv_func_printf_directive_uppercase_b="guessing no"]) - ;; - # Guess no on musl systems. - *-musl* | midipix*) gl_cv_func_printf_directive_uppercase_b="guessing no";; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_directive_uppercase_b="guessing no";; - # Guess no on native Windows. - mingw*) gl_cv_func_printf_directive_uppercase_b="guessing no";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_printf_directive_uppercase_b="$gl_cross_guess_normal";; - esac - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports the %F format -dnl directive. (ISO C99, POSIX:2001) -dnl Result is gl_cv_func_printf_directive_f. - -AC_DEFUN([gl_PRINTF_DIRECTIVE_F], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports the 'F' directive], - [gl_cv_func_printf_directive_f], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static char buf[100]; -static double zero = 0.0; -int main () -{ - int result = 0; - if (sprintf (buf, "%F %d", 1234567.0, 33, 44, 55) < 0 - || strcmp (buf, "1234567.000000 33") != 0) - result |= 1; - if (sprintf (buf, "%F", 1.0 / zero) < 0 - || (strcmp (buf, "INF") != 0 && strcmp (buf, "INFINITY") != 0)) - result |= 2; - /* This catches a Cygwin 1.5.x bug. */ - if (sprintf (buf, "%.F", 1234.0) < 0 - || strcmp (buf, "1234") != 0) - result |= 4; - return result; -}]])], - [gl_cv_func_printf_directive_f=yes], - [gl_cv_func_printf_directive_f=no], - [ - case "$host_os" in -changequote(,)dnl - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_printf_directive_f="guessing yes";; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_printf_directive_f="guessing yes";; - # Guess yes on FreeBSD >= 6. - freebsd[1-5].*) gl_cv_func_printf_directive_f="guessing no";; - freebsd* | kfreebsd*) gl_cv_func_printf_directive_f="guessing yes";; - midnightbsd*) gl_cv_func_printf_directive_f="guessing yes";; - # Guess yes on Mac OS X >= 10.3. - darwin[1-6].*) gl_cv_func_printf_directive_f="guessing no";; - darwin*) gl_cv_func_printf_directive_f="guessing yes";; - # Guess yes on OpenBSD >= 6.0. - openbsd[1-5].*) gl_cv_func_printf_directive_f="guessing no";; - openbsd*) gl_cv_func_printf_directive_f="guessing yes";; - # Guess yes on Solaris >= 2.10. - solaris2.[1-9][0-9]*) gl_cv_func_printf_directive_f="guessing yes";; - solaris*) gl_cv_func_printf_directive_f="guessing no";; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_directive_f="guessing no";; -changequote([,])dnl - # Guess yes on MSVC, no on mingw. - mingw*) AC_EGREP_CPP([Known], [ -#ifdef _MSC_VER - Known -#endif - ], - [gl_cv_func_printf_directive_f="guessing yes"], - [gl_cv_func_printf_directive_f="guessing no"]) - ;; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_printf_directive_f="$gl_cross_guess_normal";; - esac - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports the %n format -dnl directive. (ISO C99, POSIX:2001) -dnl Result is gl_cv_func_printf_directive_n. - -AC_DEFUN([gl_PRINTF_DIRECTIVE_N], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports the 'n' directive], - [gl_cv_func_printf_directive_n], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#include -#ifdef _MSC_VER -#include -/* See page about "Parameter Validation" on msdn.microsoft.com. - - */ -static void cdecl -invalid_parameter_handler (const wchar_t *expression, - const wchar_t *function, - const wchar_t *file, unsigned int line, - uintptr_t dummy) -{ - exit (1); -} -#endif -static char fmtstring[10]; -static char buf[100]; -int main () -{ - int count = -1; -#ifdef _MSC_VER - _set_invalid_parameter_handler (invalid_parameter_handler); -#endif - /* Copy the format string. Some systems (glibc with _FORTIFY_SOURCE=2) - support %n in format strings in read-only memory but not in writable - memory. */ - strcpy (fmtstring, "%d %n"); - if (sprintf (buf, fmtstring, 123, &count, 33, 44, 55) < 0 - || strcmp (buf, "123 ") != 0 - || count != 4) - return 1; - return 0; -}]])], - [gl_cv_func_printf_directive_n=yes], - [gl_cv_func_printf_directive_n=no], - [case "$host_os" in - # Guess no on glibc when _FORTIFY_SOURCE >= 2. - *-gnu* | gnu*) AC_COMPILE_IFELSE( - [AC_LANG_SOURCE( - [[#if _FORTIFY_SOURCE >= 2 - error fail - #endif - ]])], - [gl_cv_func_printf_directive_n="guessing yes"], - [gl_cv_func_printf_directive_n="guessing no"]) - ;; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_directive_n="guessing no";; - # Guess no on native Windows. - mingw*) gl_cv_func_printf_directive_n="guessing no";; - *) gl_cv_func_printf_directive_n="guessing yes";; - esac - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports the %ls format -dnl directive and in particular, when a precision is specified, whether -dnl the functions stop converting the wide string argument when the number -dnl of bytes that have been produced by this conversion equals or exceeds -dnl the precision. -dnl Result is gl_cv_func_printf_directive_ls. - -AC_DEFUN([gl_PRINTF_DIRECTIVE_LS], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports the 'ls' directive], - [gl_cv_func_printf_directive_ls], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#include -int main () -{ - int result = 0; - char buf[100]; - /* Test whether %ls works at all. - This test fails on OpenBSD 4.0, IRIX 6.5, Solaris 2.6, Haiku, but not on - Cygwin 1.5. */ - { - static const wchar_t wstring[] = { 'a', 'b', 'c', 0 }; - buf[0] = '\0'; - if (sprintf (buf, "%ls", wstring) < 0 - || strcmp (buf, "abc") != 0) - result |= 1; - } - /* This test fails on IRIX 6.5, Solaris 2.6, Cygwin 1.5, Haiku (with an - assertion failure inside libc), but not on OpenBSD 4.0. */ - { - static const wchar_t wstring[] = { 'a', 0 }; - buf[0] = '\0'; - if (sprintf (buf, "%ls", wstring) < 0 - || strcmp (buf, "a") != 0) - result |= 2; - } - /* Test whether precisions in %ls are supported as specified in ISO C 99 - section 7.19.6.1: - "If a precision is specified, no more than that many bytes are written - (including shift sequences, if any), and the array shall contain a - null wide character if, to equal the multibyte character sequence - length given by the precision, the function would need to access a - wide character one past the end of the array." - This test fails on Solaris 10. */ - { - static const wchar_t wstring[] = { 'a', 'b', (wchar_t) 0xfdfdfdfd, 0 }; - buf[0] = '\0'; - if (sprintf (buf, "%.2ls", wstring) < 0 - || strcmp (buf, "ab") != 0) - result |= 8; - } - return result; -}]])], - [gl_cv_func_printf_directive_ls=yes], - [gl_cv_func_printf_directive_ls=no], - [ -changequote(,)dnl - case "$host_os" in - # Guess yes on OpenBSD >= 6.0. - openbsd[1-5].*) gl_cv_func_printf_directive_ls="guessing no";; - openbsd*) gl_cv_func_printf_directive_ls="guessing yes";; - irix*) gl_cv_func_printf_directive_ls="guessing no";; - solaris*) gl_cv_func_printf_directive_ls="guessing no";; - cygwin*) gl_cv_func_printf_directive_ls="guessing no";; - beos* | haiku*) gl_cv_func_printf_directive_ls="guessing no";; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_directive_ls="guessing no";; - # Guess yes on native Windows. - mingw*) gl_cv_func_printf_directive_ls="guessing yes";; - *) gl_cv_func_printf_directive_ls="guessing yes";; - esac -changequote([,])dnl - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports the %lc format -dnl directive and in particular, when the argument is a null wide character, -dnl whether the functions don't produce a NUL byte. -dnl Result is gl_cv_func_printf_directive_lc. - -AC_DEFUN([gl_PRINTF_DIRECTIVE_LC], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports the 'lc' directive correctly], - [gl_cv_func_printf_directive_lc], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#include -int main () -{ - int result = 0; - char buf[100]; - /* This test fails on glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, - macOS 12.5, AIX 7.2, Solaris 11.4. - glibc 2.35 bug: */ - { - buf[0] = '\0'; - if (sprintf (buf, "%lc%lc%lc", (wint_t) 'a', (wint_t) 0, (wint_t) 'z') < 0 - || strcmp (buf, "az") != 0) - result |= 1; - } - return result; -}]])], - [gl_cv_func_printf_directive_lc=yes], - [gl_cv_func_printf_directive_lc=no], - [ -changequote(,)dnl - case "$host_os" in - # Guess yes on musl libc. - *-musl* | midipix*) gl_cv_func_printf_directive_lc="guessing yes";; - # Guess no otherwise. - *) gl_cv_func_printf_directive_lc="guessing no";; - esac -changequote([,])dnl - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports POSIX/XSI format -dnl strings with positions. (POSIX:2001) -dnl Result is gl_cv_func_printf_positions. - -AC_DEFUN_ONCE([gl_PRINTF_POSITIONS], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports POSIX/XSI format strings with positions], - [gl_cv_func_printf_positions], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -/* The string "%2$d %1$d", with dollar characters protected from the shell's - dollar expansion (possibly an autoconf bug). */ -static char format[] = { '%', '2', '$', 'd', ' ', '%', '1', '$', 'd', '\0' }; -static char buf[100]; -int main () -{ - sprintf (buf, format, 33, 55); - return (strcmp (buf, "55 33") != 0); -}]])], - [gl_cv_func_printf_positions=yes], - [gl_cv_func_printf_positions=no], - [ -changequote(,)dnl - case "$host_os" in - netbsd[1-3]* | netbsdelf[1-3]* | netbsdaout[1-3]* | netbsdcoff[1-3]*) - gl_cv_func_printf_positions="guessing no";; - beos*) gl_cv_func_printf_positions="guessing no";; - # Guess yes on Android. - linux*-android*) gl_cv_func_printf_positions="guessing yes";; - # Guess no on native Windows. - mingw* | pw*) gl_cv_func_printf_positions="guessing no";; - *) gl_cv_func_printf_positions="guessing yes";; - esac -changequote([,])dnl - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports POSIX/XSI format -dnl strings with the ' flag for grouping of decimal digits. (POSIX:2001) -dnl Result is gl_cv_func_printf_flag_grouping. - -AC_DEFUN([gl_PRINTF_FLAG_GROUPING], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports the grouping flag], - [gl_cv_func_printf_flag_grouping], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static char buf[100]; -int main () -{ - if (sprintf (buf, "%'d %d", 1234567, 99) < 0 - || buf[strlen (buf) - 1] != '9') - return 1; - return 0; -}]])], - [gl_cv_func_printf_flag_grouping=yes], - [gl_cv_func_printf_flag_grouping=no], - [ -changequote(,)dnl - case "$host_os" in - cygwin*) gl_cv_func_printf_flag_grouping="guessing no";; - netbsd*) gl_cv_func_printf_flag_grouping="guessing no";; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_flag_grouping="guessing no";; - # Guess no on native Windows. - mingw* | pw*) gl_cv_func_printf_flag_grouping="guessing no";; - *) gl_cv_func_printf_flag_grouping="guessing yes";; - esac -changequote([,])dnl - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports the - flag correctly. -dnl (ISO C99.) See -dnl -dnl Result is gl_cv_func_printf_flag_leftadjust. - -AC_DEFUN([gl_PRINTF_FLAG_LEFTADJUST], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports the left-adjust flag correctly], - [gl_cv_func_printf_flag_leftadjust], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static char buf[100]; -int main () -{ - /* Check that a '-' flag is not annihilated by a negative width. */ - if (sprintf (buf, "a%-*sc", -3, "b") < 0 - || strcmp (buf, "ab c") != 0) - return 1; - return 0; -}]])], - [gl_cv_func_printf_flag_leftadjust=yes], - [gl_cv_func_printf_flag_leftadjust=no], - [ -changequote(,)dnl - case "$host_os" in - # Guess yes on HP-UX 11. - hpux11*) gl_cv_func_printf_flag_leftadjust="guessing yes";; - # Guess no on HP-UX 10 and older. - hpux*) gl_cv_func_printf_flag_leftadjust="guessing no";; - # Guess yes on Android. - linux*-android*) gl_cv_func_printf_flag_leftadjust="guessing yes";; - # Guess yes on native Windows. - mingw*) gl_cv_func_printf_flag_leftadjust="guessing yes";; - # Guess yes otherwise. - *) gl_cv_func_printf_flag_leftadjust="guessing yes";; - esac -changequote([,])dnl - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports padding of non-finite -dnl values with the 0 flag correctly. (ISO C99 + TC1 + TC2.) See -dnl -dnl Result is gl_cv_func_printf_flag_zero. - -AC_DEFUN([gl_PRINTF_FLAG_ZERO], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports the zero flag correctly], - [gl_cv_func_printf_flag_zero], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static char buf[100]; -static double zero = 0.0; -int main () -{ - if (sprintf (buf, "%010f", 1.0 / zero, 33, 44, 55) < 0 - || (strcmp (buf, " inf") != 0 - && strcmp (buf, " infinity") != 0)) - return 1; - return 0; -}]])], - [gl_cv_func_printf_flag_zero=yes], - [gl_cv_func_printf_flag_zero=no], - [ -changequote(,)dnl - case "$host_os" in - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_printf_flag_zero="guessing yes";; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_printf_flag_zero="guessing yes";; - # Guess yes on BeOS. - beos*) gl_cv_func_printf_flag_zero="guessing yes";; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_flag_zero="guessing no";; - # Guess no on native Windows. - mingw*) gl_cv_func_printf_flag_zero="guessing no";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_printf_flag_zero="$gl_cross_guess_normal";; - esac -changequote([,])dnl - ]) - ]) -]) - -dnl Test whether the *printf family of functions supports large precisions. -dnl On mingw, precisions larger than 512 are treated like 512, in integer, -dnl floating-point or pointer output. On Solaris 10/x86, precisions larger -dnl than 510 in floating-point output crash the program. On Solaris 10/SPARC, -dnl precisions larger than 510 in floating-point output yield wrong results. -dnl On AIX 7.1, precisions larger than 998 in floating-point output yield -dnl wrong results. On BeOS, precisions larger than 1044 crash the program. -dnl Result is gl_cv_func_printf_precision. - -AC_DEFUN([gl_PRINTF_PRECISION], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf supports large precisions], - [gl_cv_func_printf_precision], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static char buf[5000]; -int main () -{ - int result = 0; -#ifdef __BEOS__ - /* On BeOS, this would crash and show a dialog box. Avoid the crash. */ - return 1; -#endif - if (sprintf (buf, "%.4000d %d", 1, 33, 44) < 4000 + 3) - result |= 1; - if (sprintf (buf, "%.4000f %d", 1.0, 33, 44) < 4000 + 5) - result |= 2; - if (sprintf (buf, "%.511f %d", 1.0, 33, 44) < 511 + 5 - || buf[0] != '1') - result |= 4; - if (sprintf (buf, "%.999f %d", 1.0, 33, 44) < 999 + 5 - || buf[0] != '1') - result |= 4; - return result; -}]])], - [gl_cv_func_printf_precision=yes], - [gl_cv_func_printf_precision=no], - [ -changequote(,)dnl - case "$host_os" in - # Guess no only on Solaris, native Windows, and BeOS systems. - solaris*) gl_cv_func_printf_precision="guessing no" ;; - mingw* | pw*) gl_cv_func_printf_precision="guessing no" ;; - beos*) gl_cv_func_printf_precision="guessing no" ;; - # Guess yes on Android. - linux*-android*) gl_cv_func_printf_precision="guessing yes" ;; - *) gl_cv_func_printf_precision="guessing yes" ;; - esac -changequote([,])dnl - ]) - ]) -]) - -dnl Test whether the *printf family of functions recovers gracefully in case -dnl of an out-of-memory condition, or whether it crashes the entire program. -dnl Result is gl_cv_func_printf_enomem. - -AC_DEFUN([gl_PRINTF_ENOMEM], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([gl_MULTIARCH]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether printf survives out-of-memory conditions], - [gl_cv_func_printf_enomem], - [ - gl_cv_func_printf_enomem="guessing no" - if test "$cross_compiling" = no; then - if test $APPLE_UNIVERSAL_BUILD = 0; then - AC_LANG_CONFTEST([AC_LANG_SOURCE([[ -]GL_NOCRASH[ -#include -#include -#include -#include -#include -int main() -{ - struct rlimit limit; - int ret; - nocrash_init (); - /* Some printf implementations allocate temporary space with malloc. */ - /* On BSD systems, malloc() is limited by RLIMIT_DATA. */ -#ifdef RLIMIT_DATA - if (getrlimit (RLIMIT_DATA, &limit) < 0) - return 77; - if (limit.rlim_max == RLIM_INFINITY || limit.rlim_max > 5000000) - limit.rlim_max = 5000000; - limit.rlim_cur = limit.rlim_max; - if (setrlimit (RLIMIT_DATA, &limit) < 0) - return 77; -#endif - /* On Linux systems, malloc() is limited by RLIMIT_AS. */ -#ifdef RLIMIT_AS - if (getrlimit (RLIMIT_AS, &limit) < 0) - return 77; - if (limit.rlim_max == RLIM_INFINITY || limit.rlim_max > 5000000) - limit.rlim_max = 5000000; - limit.rlim_cur = limit.rlim_max; - if (setrlimit (RLIMIT_AS, &limit) < 0) - return 77; -#endif - /* Some printf implementations allocate temporary space on the stack. */ -#ifdef RLIMIT_STACK - if (getrlimit (RLIMIT_STACK, &limit) < 0) - return 77; - if (limit.rlim_max == RLIM_INFINITY || limit.rlim_max > 5000000) - limit.rlim_max = 5000000; - limit.rlim_cur = limit.rlim_max; - if (setrlimit (RLIMIT_STACK, &limit) < 0) - return 77; -#endif - ret = printf ("%.5000000f", 1.0); - return !(ret == 5000002 || (ret < 0 && errno == ENOMEM)); -} - ]])]) - if AC_TRY_EVAL([ac_link]) && test -s conftest$ac_exeext; then - (./conftest 2>&AS_MESSAGE_LOG_FD - result=$? - _AS_ECHO_LOG([\$? = $result]) - if test $result != 0 && test $result != 77; then result=1; fi - exit $result - ) >/dev/null 2>/dev/null - case $? in - 0) gl_cv_func_printf_enomem="yes" ;; - 77) gl_cv_func_printf_enomem="guessing no" ;; - *) gl_cv_func_printf_enomem="no" ;; - esac - else - gl_cv_func_printf_enomem="guessing no" - fi - rm -fr conftest* - else - dnl A universal build on Apple Mac OS X platforms. - dnl The result would be 'no' in 32-bit mode and 'yes' in 64-bit mode. - dnl But we need a configuration result that is valid in both modes. - gl_cv_func_printf_enomem="guessing no" - fi - fi - if test "$gl_cv_func_printf_enomem" = "guessing no"; then -changequote(,)dnl - case "$host_os" in - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_printf_enomem="guessing yes";; - # Guess yes on Solaris. - solaris*) gl_cv_func_printf_enomem="guessing yes";; - # Guess yes on AIX. - aix*) gl_cv_func_printf_enomem="guessing yes";; - # Guess yes on HP-UX/hppa. - hpux*) case "$host_cpu" in - hppa*) gl_cv_func_printf_enomem="guessing yes";; - *) gl_cv_func_printf_enomem="guessing no";; - esac - ;; - # Guess yes on IRIX. - irix*) gl_cv_func_printf_enomem="guessing yes";; - # Guess yes on OSF/1. - osf*) gl_cv_func_printf_enomem="guessing yes";; - # Guess yes on BeOS. - beos*) gl_cv_func_printf_enomem="guessing yes";; - # Guess yes on Haiku. - haiku*) gl_cv_func_printf_enomem="guessing yes";; - # Guess no on Android. - linux*-android*) gl_cv_func_printf_enomem="guessing no";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_printf_enomem="$gl_cross_guess_normal";; - esac -changequote([,])dnl - fi - ]) -]) - -dnl Test whether the snprintf function exists. (ISO C99, POSIX:2001) -dnl Result is ac_cv_func_snprintf. - -AC_DEFUN([gl_SNPRINTF_PRESENCE], -[ - AC_CHECK_FUNCS_ONCE([snprintf]) -]) - -dnl Test whether the string produced by the snprintf function is always NUL -dnl terminated. (ISO C99, POSIX:2001) -dnl Result is gl_cv_func_snprintf_truncation_c99. - -AC_DEFUN_ONCE([gl_SNPRINTF_TRUNCATION_C99], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_REQUIRE([gl_SNPRINTF_PRESENCE]) - AC_CACHE_CHECK([whether snprintf truncates the result as in C99], - [gl_cv_func_snprintf_truncation_c99], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#if HAVE_SNPRINTF -# define my_snprintf snprintf -#else -# include -static int my_snprintf (char *buf, int size, const char *format, ...) -{ - va_list args; - int ret; - va_start (args, format); - ret = vsnprintf (buf, size, format, args); - va_end (args); - return ret; -} -#endif -static char buf[100]; -int main () -{ - strcpy (buf, "ABCDEF"); - my_snprintf (buf, 3, "%d %d", 4567, 89); - if (memcmp (buf, "45\0DEF", 6) != 0) - return 1; - return 0; -}]])], - [gl_cv_func_snprintf_truncation_c99=yes], - [gl_cv_func_snprintf_truncation_c99=no], - [ -changequote(,)dnl - case "$host_os" in - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on FreeBSD >= 5. - freebsd[1-4].*) gl_cv_func_snprintf_truncation_c99="guessing no";; - freebsd* | kfreebsd*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - midnightbsd*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on Mac OS X >= 10.3. - darwin[1-6].*) gl_cv_func_snprintf_truncation_c99="guessing no";; - darwin*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on OpenBSD >= 3.9. - openbsd[1-2].* | openbsd3.[0-8] | openbsd3.[0-8].*) - gl_cv_func_snprintf_truncation_c99="guessing no";; - openbsd*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on Solaris >= 2.6. - solaris2.[0-5] | solaris2.[0-5].*) - gl_cv_func_snprintf_truncation_c99="guessing no";; - solaris*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on AIX >= 4. - aix[1-3]*) gl_cv_func_snprintf_truncation_c99="guessing no";; - aix*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on HP-UX >= 11. - hpux[7-9]* | hpux10*) gl_cv_func_snprintf_truncation_c99="guessing no";; - hpux*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on IRIX >= 6.5. - irix6.5) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on OSF/1 >= 5. - osf[3-4]*) gl_cv_func_snprintf_truncation_c99="guessing no";; - osf*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on NetBSD >= 3. - netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*) - gl_cv_func_snprintf_truncation_c99="guessing no";; - netbsd*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on BeOS. - beos*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess yes on Android. - linux*-android*) gl_cv_func_snprintf_truncation_c99="guessing yes";; - # Guess no on native Windows. - mingw*) gl_cv_func_snprintf_truncation_c99="guessing no";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_snprintf_truncation_c99="$gl_cross_guess_normal";; - esac -changequote([,])dnl - ]) - ]) -]) - -dnl Test whether the return value of the snprintf function is the number -dnl of bytes (excluding the terminating NUL) that would have been produced -dnl if the buffer had been large enough. (ISO C99, POSIX:2001) -dnl For example, this test program fails on IRIX 6.5: -dnl --------------------------------------------------------------------- -dnl #include -dnl int main() -dnl { -dnl static char buf[8]; -dnl int retval = snprintf (buf, 3, "%d", 12345); -dnl return retval >= 0 && retval < 3; -dnl } -dnl --------------------------------------------------------------------- -dnl Result is gl_cv_func_snprintf_retval_c99. - -AC_DEFUN_ONCE([gl_SNPRINTF_RETVAL_C99], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_REQUIRE([gl_SNPRINTF_PRESENCE]) - AC_CACHE_CHECK([whether snprintf returns a byte count as in C99], - [gl_cv_func_snprintf_retval_c99], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#if HAVE_SNPRINTF -# define my_snprintf snprintf -#else -# include -static int my_snprintf (char *buf, int size, const char *format, ...) -{ - va_list args; - int ret; - va_start (args, format); - ret = vsnprintf (buf, size, format, args); - va_end (args); - return ret; -} -#endif -static char buf[100]; -int main () -{ - strcpy (buf, "ABCDEF"); - if (my_snprintf (buf, 3, "%d %d", 4567, 89) != 7) - return 1; - if (my_snprintf (buf, 0, "%d %d", 4567, 89) != 7) - return 2; - if (my_snprintf (NULL, 0, "%d %d", 4567, 89) != 7) - return 3; - return 0; -}]])], - [gl_cv_func_snprintf_retval_c99=yes], - [gl_cv_func_snprintf_retval_c99=no], - [case "$host_os" in -changequote(,)dnl - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_snprintf_retval_c99="guessing yes";; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_snprintf_retval_c99="guessing yes";; - # Guess yes on FreeBSD >= 5. - freebsd[1-4].*) gl_cv_func_snprintf_retval_c99="guessing no";; - freebsd* | kfreebsd*) gl_cv_func_snprintf_retval_c99="guessing yes";; - midnightbsd*) gl_cv_func_snprintf_retval_c99="guessing yes";; - # Guess yes on Mac OS X >= 10.3. - darwin[1-6].*) gl_cv_func_snprintf_retval_c99="guessing no";; - darwin*) gl_cv_func_snprintf_retval_c99="guessing yes";; - # Guess yes on OpenBSD >= 3.9. - openbsd[1-2].* | openbsd3.[0-8] | openbsd3.[0-8].*) - gl_cv_func_snprintf_retval_c99="guessing no";; - openbsd*) gl_cv_func_snprintf_retval_c99="guessing yes";; - # Guess yes on Solaris >= 2.10. - solaris2.[1-9][0-9]*) gl_cv_func_printf_sizes_c99="guessing yes";; - solaris*) gl_cv_func_printf_sizes_c99="guessing no";; - # Guess yes on AIX >= 4. - aix[1-3]*) gl_cv_func_snprintf_retval_c99="guessing no";; - aix*) gl_cv_func_snprintf_retval_c99="guessing yes";; - # Guess yes on NetBSD >= 3. - netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*) - gl_cv_func_snprintf_retval_c99="guessing no";; - netbsd*) gl_cv_func_snprintf_retval_c99="guessing yes";; - # Guess yes on BeOS. - beos*) gl_cv_func_snprintf_retval_c99="guessing yes";; - # Guess yes on Android. - linux*-android*) gl_cv_func_snprintf_retval_c99="guessing yes";; -changequote([,])dnl - # Guess yes on MSVC, no on mingw. - mingw*) AC_EGREP_CPP([Known], [ -#ifdef _MSC_VER - Known -#endif - ], - [gl_cv_func_snprintf_retval_c99="guessing yes"], - [gl_cv_func_snprintf_retval_c99="guessing no"]) - ;; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_snprintf_retval_c99="$gl_cross_guess_normal";; - esac - ]) - ]) -]) - -dnl Test whether the snprintf function supports the %n format directive -dnl also in truncated portions of the format string. (ISO C99, POSIX:2001) -dnl Result is gl_cv_func_snprintf_directive_n. - -AC_DEFUN([gl_SNPRINTF_DIRECTIVE_N], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_REQUIRE([gl_SNPRINTF_PRESENCE]) - AC_CACHE_CHECK([whether snprintf fully supports the 'n' directive], - [gl_cv_func_snprintf_directive_n], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#if HAVE_SNPRINTF -# define my_snprintf snprintf -#else -# include -static int my_snprintf (char *buf, int size, const char *format, ...) -{ - va_list args; - int ret; - va_start (args, format); - ret = vsnprintf (buf, size, format, args); - va_end (args); - return ret; -} -#endif -static char fmtstring[10]; -static char buf[100]; -int main () -{ - int count = -1; - /* Copy the format string. Some systems (glibc with _FORTIFY_SOURCE=2) - support %n in format strings in read-only memory but not in writable - memory. */ - strcpy (fmtstring, "%d %n"); - my_snprintf (buf, 4, fmtstring, 12345, &count, 33, 44, 55); - if (count != 6) - return 1; - return 0; -}]])], - [gl_cv_func_snprintf_directive_n=yes], - [gl_cv_func_snprintf_directive_n=no], - [ - case "$host_os" in - # Guess no on glibc when _FORTIFY_SOURCE >= 2. - *-gnu* | gnu*) AC_COMPILE_IFELSE( - [AC_LANG_SOURCE( - [[#if _FORTIFY_SOURCE >= 2 - error fail - #endif - ]])], - [gl_cv_func_snprintf_directive_n="guessing yes"], - [gl_cv_func_snprintf_directive_n="guessing no"]) - ;; -changequote(,)dnl - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_snprintf_directive_n="guessing yes";; - # Guess yes on FreeBSD >= 5. - freebsd[1-4].*) gl_cv_func_snprintf_directive_n="guessing no";; - freebsd* | kfreebsd*) gl_cv_func_snprintf_directive_n="guessing yes";; - midnightbsd*) gl_cv_func_snprintf_directive_n="guessing yes";; - # Guess yes on Mac OS X >= 10.3. - darwin[1-6].*) gl_cv_func_snprintf_directive_n="guessing no";; - darwin*) gl_cv_func_snprintf_directive_n="guessing yes";; - # Guess yes on Solaris >= 2.6. - solaris2.[0-5] | solaris2.[0-5].*) - gl_cv_func_snprintf_directive_n="guessing no";; - solaris*) gl_cv_func_snprintf_directive_n="guessing yes";; - # Guess yes on AIX >= 4. - aix[1-3]*) gl_cv_func_snprintf_directive_n="guessing no";; - aix*) gl_cv_func_snprintf_directive_n="guessing yes";; - # Guess yes on IRIX >= 6.5. - irix6.5) gl_cv_func_snprintf_directive_n="guessing yes";; - # Guess yes on OSF/1 >= 5. - osf[3-4]*) gl_cv_func_snprintf_directive_n="guessing no";; - osf*) gl_cv_func_snprintf_directive_n="guessing yes";; - # Guess yes on NetBSD >= 3. - netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*) - gl_cv_func_snprintf_directive_n="guessing no";; - netbsd*) gl_cv_func_snprintf_directive_n="guessing yes";; - # Guess yes on BeOS. - beos*) gl_cv_func_snprintf_directive_n="guessing yes";; - # Guess no on Android. - linux*-android*) gl_cv_func_snprintf_directive_n="guessing no";; - # Guess no on native Windows. - mingw*) gl_cv_func_snprintf_directive_n="guessing no";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_snprintf_directive_n="$gl_cross_guess_normal";; -changequote([,])dnl - esac - ]) - ]) -]) - -dnl Test whether the snprintf function, when passed a size = 1, writes any -dnl output without bounds in this case, behaving like sprintf. This is the -dnl case on Linux libc5. -dnl Result is gl_cv_func_snprintf_size1. - -AC_DEFUN([gl_SNPRINTF_SIZE1], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_REQUIRE([gl_SNPRINTF_PRESENCE]) - AC_CACHE_CHECK([whether snprintf respects a size of 1], - [gl_cv_func_snprintf_size1], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#if HAVE_SNPRINTF -# define my_snprintf snprintf -#else -# include -static int my_snprintf (char *buf, int size, const char *format, ...) -{ - va_list args; - int ret; - va_start (args, format); - ret = vsnprintf (buf, size, format, args); - va_end (args); - return ret; -} -#endif -int main() -{ - static char buf[8] = { 'D', 'E', 'A', 'D', 'B', 'E', 'E', 'F' }; - my_snprintf (buf, 1, "%d", 12345); - return buf[1] != 'E'; -}]])], - [gl_cv_func_snprintf_size1=yes], - [gl_cv_func_snprintf_size1=no], - [case "$host_os" in - # Guess yes on Android. - linux*-android*) gl_cv_func_snprintf_size1="guessing yes" ;; - # Guess yes on native Windows. - mingw*) gl_cv_func_snprintf_size1="guessing yes" ;; - *) gl_cv_func_snprintf_size1="guessing yes" ;; - esac - ]) - ]) -]) - -dnl Test whether the vsnprintf function, when passed a zero size, produces no -dnl output. (ISO C99, POSIX:2001) -dnl For example, snprintf nevertheless writes a NUL byte in this case -dnl on OSF/1 5.1: -dnl --------------------------------------------------------------------- -dnl #include -dnl int main() -dnl { -dnl static char buf[8] = { 'D', 'E', 'A', 'D', 'B', 'E', 'E', 'F' }; -dnl snprintf (buf, 0, "%d", 12345); -dnl return buf[0] != 'D'; -dnl } -dnl --------------------------------------------------------------------- -dnl And vsnprintf writes any output without bounds in this case, behaving like -dnl vsprintf, on HP-UX 11 and OSF/1 5.1: -dnl --------------------------------------------------------------------- -dnl #include -dnl #include -dnl static int my_snprintf (char *buf, int size, const char *format, ...) -dnl { -dnl va_list args; -dnl int ret; -dnl va_start (args, format); -dnl ret = vsnprintf (buf, size, format, args); -dnl va_end (args); -dnl return ret; -dnl } -dnl int main() -dnl { -dnl static char buf[8] = { 'D', 'E', 'A', 'D', 'B', 'E', 'E', 'F' }; -dnl my_snprintf (buf, 0, "%d", 12345); -dnl return buf[0] != 'D'; -dnl } -dnl --------------------------------------------------------------------- -dnl Result is gl_cv_func_vsnprintf_zerosize_c99. - -AC_DEFUN([gl_VSNPRINTF_ZEROSIZE_C99], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether vsnprintf respects a zero size as in C99], - [gl_cv_func_vsnprintf_zerosize_c99], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static int my_snprintf (char *buf, int size, const char *format, ...) -{ - va_list args; - int ret; - va_start (args, format); - ret = vsnprintf (buf, size, format, args); - va_end (args); - return ret; -} -int main() -{ - static char buf[8] = { 'D', 'E', 'A', 'D', 'B', 'E', 'E', 'F' }; - my_snprintf (buf, 0, "%d", 12345); - return buf[0] != 'D'; -}]])], - [gl_cv_func_vsnprintf_zerosize_c99=yes], - [gl_cv_func_vsnprintf_zerosize_c99=no], - [ -changequote(,)dnl - case "$host_os" in - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # Guess yes on FreeBSD >= 5. - freebsd[1-4].*) gl_cv_func_vsnprintf_zerosize_c99="guessing no";; - freebsd* | kfreebsd*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - midnightbsd*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # Guess yes on Mac OS X >= 10.3. - darwin[1-6].*) gl_cv_func_vsnprintf_zerosize_c99="guessing no";; - darwin*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # Guess yes on Cygwin. - cygwin*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # Guess yes on Solaris >= 2.6. - solaris2.[0-5] | solaris2.[0-5].*) - gl_cv_func_vsnprintf_zerosize_c99="guessing no";; - solaris*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # Guess yes on AIX >= 4. - aix[1-3]*) gl_cv_func_vsnprintf_zerosize_c99="guessing no";; - aix*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # Guess yes on IRIX >= 6.5. - irix6.5) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # Guess yes on NetBSD >= 3. - netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*) - gl_cv_func_vsnprintf_zerosize_c99="guessing no";; - netbsd*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # Guess yes on BeOS. - beos*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # Guess yes on Android. - linux*-android*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # Guess yes on native Windows. - mingw* | pw*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_vsnprintf_zerosize_c99="$gl_cross_guess_normal";; - esac -changequote([,])dnl - ]) - ]) -]) - -dnl Test whether the swprintf function works correctly when it produces output -dnl that contains null wide characters. -dnl Result is gl_cv_func_swprintf_works. - -AC_DEFUN([gl_SWPRINTF_WORKS], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CHECK_FUNCS_ONCE([swprintf]) - AC_CACHE_CHECK([whether swprintf works], - [gl_cv_func_swprintf_works], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#ifndef __USE_MINGW_ANSI_STDIO -# define __USE_MINGW_ANSI_STDIO 1 -#endif -#include -#include -int main() -{ - int result = 0; - { /* This test fails on musl libc 1.2.3, FreeBSD, NetBSD, OpenBSD, macOS, AIX. */ - wchar_t buf[5] = { 0xBEEF, 0xBEEF, 0xBEEF, 0xBEEF, 0xBEEF }; - int ret = swprintf (buf, 4, L"%cz", '\0'); - /* Expected result: - ret = 2, buf[0] = 0x0, buf[1] = 0x7a, buf[2] = 0x0, buf[3] = 0xbeef - musl libc 1.2.3: - ret = 2, buf[0] = 0x0, buf[1] = 0x0, buf[2] = 0x0, buf[3] = 0x0 - Reported at . - FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2: - ret = 2, buf[0] = 0x0, buf[1] = 0xbeef, buf[2] = 0xbeef, buf[3] = 0xbeef - */ - if (ret < 0 || buf[1] != 'z') - result |= 1; - } - { /* This test fails on mingw. */ - wchar_t buf[2]; - int ret = swprintf (buf, 2, L"%lc", (wint_t)0); - /* Expected: ret = 1 - mingw: ret = 0 - */ - if (ret != 1) - result |= 2; - } - return result; -}]])], - [gl_cv_func_swprintf_works=yes], - [gl_cv_func_swprintf_works=no], - [case "$host_os" in - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_swprintf_works="guessing yes";; - # Guess no on musl systems. - *-musl* | midipix*) gl_cv_func_swprintf_works="guessing no";; - # Guess no on FreeBSD, NetBSD, OpenBSD, macOS, AIX. - freebsd* | midnightbsd* | netbsd* | openbsd* | darwin* | aix*) - gl_cv_func_swprintf_works="guessing no";; - # Guess no on native Windows. - mingw* | pw*) gl_cv_func_swprintf_works="guessing no";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_swprintf_works="$gl_cross_guess_normal";; - esac - ]) - ]) -]) - -dnl Test whether the *wprintf family of functions supports the 'a' and 'A' -dnl conversion specifier for hexadecimal output of 'long double' numbers. -dnl (ISO C99, POSIX:2001) -dnl Result is gl_cv_func_swprintf_directive_la. - -AC_DEFUN([gl_SWPRINTF_DIRECTIVE_LA], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether swprintf supports the 'La' and 'LA' directives], - [gl_cv_func_swprintf_directive_la], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static wchar_t buf[100]; -int main () -{ - int result = 0; - /* This catches a glibc 2.15 and Haiku 2022 bug. */ - if (swprintf (buf, sizeof (buf) / sizeof (wchar_t), - L"%La %d", 3.1416015625L, 33, 44, 55) < 0 - || (wcscmp (buf, L"0x1.922p+1 33") != 0 - && wcscmp (buf, L"0x3.244p+0 33") != 0 - && wcscmp (buf, L"0x6.488p-1 33") != 0 - && wcscmp (buf, L"0xc.91p-2 33") != 0)) - result |= 1; - return result; -}]])], - [gl_cv_func_swprintf_directive_la=yes], - [gl_cv_func_swprintf_directive_la=no], - [case "$host_os" in - # Guess yes on glibc >= 2.17 systems. - *-gnu* | gnu*) - AC_EGREP_CPP([Unlucky], [ - #include - #ifdef __GNU_LIBRARY__ - #if ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16) || (__GLIBC__ > 2)) && !defined __UCLIBC__ - Unlucky - #endif - #endif - ], - [gl_cv_func_swprintf_directive_la="guessing yes"], - [gl_cv_func_swprintf_directive_la="guessing no"]) - ;; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_swprintf_directive_la="guessing yes";; - # Guess yes on Android. - linux*-android*) gl_cv_func_swprintf_directive_la="guessing yes";; - # Guess no on native Windows. - mingw*) gl_cv_func_swprintf_directive_la="guessing no";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_swprintf_directive_la="$gl_cross_guess_normal";; - esac - ]) - ]) -]) - -dnl Test whether the *wprintf family of functions supports the 'lc' conversion -dnl specifier for all wide characters. -dnl (ISO C11, POSIX:2001) -dnl Result is gl_cv_func_swprintf_directive_lc. - -AC_DEFUN([gl_SWPRINTF_DIRECTIVE_LC], -[ - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - AC_CACHE_CHECK([whether swprintf supports the 'lc' directive], - [gl_cv_func_swprintf_directive_lc], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -static wchar_t buf[100]; -static wint_t L_invalid = (wchar_t) 0x76543210; -int main () -{ - int result = 0; - /* This catches a musl libc 1.2.4, Android bug. - Reported at . */ - if (swprintf (buf, sizeof (buf) / sizeof (wchar_t), - L"%lc %d", L_invalid, 33, 44, 55) < 0) - result |= 1; - return result; -}]])], - [gl_cv_func_swprintf_directive_lc=yes], - [gl_cv_func_swprintf_directive_lc=no], - [case "$host_os" in - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_swprintf_directive_lc="guessing yes";; - # Guess no on musl systems. - *-musl* | midipix*) gl_cv_func_swprintf_directive_lc="guessing no";; - # Guess no on Android. - linux*-android*) gl_cv_func_swprintf_directive_lc="guessing no";; - # Guess yes on native Windows. - mingw*) gl_cv_func_swprintf_directive_lc="guessing yes";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_swprintf_directive_lc="$gl_cross_guess_normal";; - esac - ]) - ]) -]) - -dnl The results of these tests on various platforms are: -dnl -dnl 1 = gl_PRINTF_SIZES_C99 -dnl 2 = gl_PRINTF_SIZES_C23 -dnl 3 = gl_PRINTF_LONG_DOUBLE -dnl 4 = gl_PRINTF_INFINITE -dnl 5 = gl_PRINTF_INFINITE_LONG_DOUBLE -dnl 6 = gl_PRINTF_DIRECTIVE_A -dnl 7 = gl_PRINTF_DIRECTIVE_B -dnl 8 = gl_PRINTF_DIRECTIVE_UPPERCASE_B -dnl 9 = gl_PRINTF_DIRECTIVE_F -dnl 10 = gl_PRINTF_DIRECTIVE_N -dnl 11 = gl_PRINTF_DIRECTIVE_LS -dnl 12 = gl_PRINTF_DIRECTIVE_LC -dnl 13 = gl_PRINTF_POSITIONS -dnl 14 = gl_PRINTF_FLAG_GROUPING -dnl 15 = gl_PRINTF_FLAG_LEFTADJUST -dnl 16 = gl_PRINTF_FLAG_ZERO -dnl 17 = gl_PRINTF_PRECISION -dnl 18 = gl_PRINTF_ENOMEM -dnl 19 = gl_SNPRINTF_PRESENCE -dnl 20 = gl_SNPRINTF_TRUNCATION_C99 -dnl 21 = gl_SNPRINTF_RETVAL_C99 -dnl 22 = gl_SNPRINTF_DIRECTIVE_N -dnl 23 = gl_SNPRINTF_SIZE1 -dnl 24 = gl_VSNPRINTF_ZEROSIZE_C99 -dnl 25 = gl_SWPRINTF_WORKS -dnl 26 = gl_SWPRINTF_DIRECTIVE_LA -dnl 27 = gl_SWPRINTF_DIRECTIVE_LC -dnl -dnl 1 = checking whether printf supports size specifiers as in C99... -dnl 2 = checking whether printf supports size specifiers as in C23... -dnl 3 = checking whether printf supports 'long double' arguments... -dnl 4 = checking whether printf supports infinite 'double' arguments... -dnl 5 = checking whether printf supports infinite 'long double' arguments... -dnl 6 = checking whether printf supports the 'a' and 'A' directives... -dnl 7 = checking whether printf supports the 'b' directive... -dnl 8 = checking whether printf supports the 'B' directive... -dnl 9 = checking whether printf supports the 'F' directive... -dnl 10 = checking whether printf supports the 'n' directive... -dnl 11 = checking whether printf supports the 'ls' directive... -dnl 12 = checking whether printf supports the 'lc' directive correctly... -dnl 13 = checking whether printf supports POSIX/XSI format strings with positions... -dnl 14 = checking whether printf supports the grouping flag... -dnl 15 = checking whether printf supports the left-adjust flag correctly... -dnl 16 = checking whether printf supports the zero flag correctly... -dnl 17 = checking whether printf supports large precisions... -dnl 18 = checking whether printf survives out-of-memory conditions... -dnl 19 = checking for snprintf... -dnl 20 = checking whether snprintf truncates the result as in C99... -dnl 21 = checking whether snprintf returns a byte count as in C99... -dnl 22 = checking whether snprintf fully supports the 'n' directive... -dnl 23 = checking whether snprintf respects a size of 1... -dnl 24 = checking whether vsnprintf respects a zero size as in C99... -dnl 25 = checking whether swprintf works... -dnl 26 = checking whether swprintf supports the 'La' and 'LA' directives... -dnl 27 = checking whether swprintf supports the 'lc' directive... -dnl -dnl . = yes, # = no. -dnl -dnl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 -dnl musl libc 1.2.3 . # . . . . # # . . . . . . . . . . . . . . . . # . # -dnl glibc 2.35 . # . . . . . . . . . # . . . . . . . . . . . . . . . -dnl glibc 2.5 . # . . . . # # . . . # . . . . . . . . . . . . . # . -dnl glibc 2.3.6 . # . . . # # # . . . # . . . . . . . . . . . . . # . -dnl FreeBSD 13.0 . # . . . # # # . . . # . . . . . # . . . . . . # . # -dnl FreeBSD 5.4, 6.1 . # . . . # # # . . . # . . . # . # . . . . . . # ? ? -dnl Mac OS X 10.13.5 . # . . # # # # . # . # . . . . . . . . . # . . # ? ? -dnl Mac OS X 10.5.8 . # . . # # # # . . . # . . . # . . . . . . . . # ? ? -dnl Mac OS X 10.3.9 . # . . . # # # . . . # . . . # . # . . . . . . # ? ? -dnl OpenBSD 6.0, 6.7 . # . . . # # # . . . # . . . . . # . . . . . . # . # -dnl OpenBSD 3.9, 4.0 . # . # # # # # # . # # . # . # . # . . . . . . # ? ? -dnl Cygwin 1.7.0 (2009) . # . . # . # # . . ? ? . . . . . ? . . . . . . ? ? ? -dnl Cygwin 1.5.25 (2008) . # . . # # # # . . # ? . . . . . # . . . . . . ? ? ? -dnl Cygwin 1.5.19 (2006) # # . . # # # # # . # ? . # . # # # . . . . . . ? ? ? -dnl Solaris 11.4 . # . # # # # # . . # # . . . # . . . . . . . . . # . -dnl Solaris 11.3 . # . . . # # # . . # # . . . . . . . . . . . . . # . -dnl Solaris 11.0 . # . # # # # # . . # # . . . # . . . . . . . . ? ? ? -dnl Solaris 10 . # . # # # # # . . # # . . . # # . . . . . . . . # . -dnl Solaris 2.6 ... 9 # # . # # # # # # . # # . . . # # . . . # . . . ? ? ? -dnl Solaris 2.5.1 # # . # # # # # # . # # . . . # . . # # # # # # ? ? ? -dnl AIX 7.1 . # . # # # # # . . . # . . . # # . . . . . . . # . . -dnl AIX 5.2 . # . # # # # # . . . # . . . # . . . . . . . . # ? ? -dnl AIX 4.3.2, 5.1 # # . # # # # # # . . # . . . # . . . . # . . . # ? ? -dnl HP-UX 11.31 . # . . . # # # . . . ? . . . # . . . . # # . . ? ? ? -dnl HP-UX 11.{00,11,23} # # . . . # # # # . . ? . . . # . . . . # # . # ? ? ? -dnl HP-UX 10.20 # # . # . # # # # . ? ? . . # # . . . . # # ? # ? ? ? -dnl IRIX 6.5 # # . # # # # # # . # # . . . # . . . . # . . . # ? ? -dnl OSF/1 5.1 # # . # # # # # # . . ? . . . # . . . . # . . # ? ? ? -dnl OSF/1 4.0d # # . # # # # # # . . ? . . . # . . # # # # # # ? ? ? -dnl NetBSD 9.0 . # . . . # # # . . . # . . . . . . . . . . . . # . # -dnl NetBSD 5.0 . # . . # # # # . . . # . . . # . # . . . . . . # ? ? -dnl NetBSD 4.0 . # ? ? ? ? # # ? . ? # . ? ? ? ? ? . . . ? ? ? # ? ? -dnl NetBSD 3.0 . # . . . # # # # . ? # # # ? # . # . . . . . . # ? ? -dnl Haiku . # . . # # # # # . # ? . . . . . ? . . ? . . . . # . -dnl BeOS # # # . # # # # # . ? ? # . ? . # ? . . ? . . . ? ? ? -dnl Android 4.3 . # . # # # # # # # # ? . # . # . # . . . # . . ? ? ? -dnl old mingw / msvcrt # # # # # # # # # . . ? # # . # # ? . # # # . . # ? ? -dnl MSVC 9 # # # # # # # # # # . ? # # . # # ? # # # # . . # ? ? -dnl mingw 2009-2011 . # # . # . # # . . . ? # # . . . ? . . . . . . # ? ? -dnl mingw-w64 2011 # # # # # # # # # . . ? # # . # # ? . # # # . . # ? ? diff --git a/m4/signbit.m4 b/m4/signbit.m4 deleted file mode 100644 index 2fea73f854c..00000000000 --- a/m4/signbit.m4 +++ /dev/null @@ -1,393 +0,0 @@ -# signbit.m4 serial 21 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_SIGNBIT], -[ - AC_REQUIRE([gl_MATH_H_DEFAULTS]) - AC_REQUIRE([AC_CANONICAL_HOST]) - AC_CACHE_CHECK([for signbit macro], [gl_cv_func_signbit], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -/* If signbit is defined as a function, don't use it, since calling it for - 'float' or 'long double' arguments would involve conversions. - If signbit is not declared at all but exists as a library function, don't - use it, since the prototype may not match. - If signbit is not declared at all but exists as a compiler built-in, don't - use it, since it's preferable to use __builtin_signbit* (no warnings, - no conversions). */ -#ifndef signbit -# error "signbit should be a macro" -#endif -#include -]gl_SIGNBIT_TEST_PROGRAM -])], - [gl_cv_func_signbit=yes], - [gl_cv_func_signbit=no], - [case "$host_os" in - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_signbit="guessing yes" ;; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_signbit="guessing yes" ;; - # Guess yes on native Windows. - mingw*) gl_cv_func_signbit="guessing yes" ;; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_signbit="$gl_cross_guess_normal" ;; - esac - ]) - ]) - dnl GCC >= 4.0 and clang provide three built-ins for signbit. - dnl They can be used without warnings, also in C++, regardless of . - dnl But they may expand to calls to functions, which may or may not be in - dnl libc. - AC_CACHE_CHECK([for signbit compiler built-ins], - [gl_cv_func_signbit_builtins], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#if (__GNUC__ >= 4) || (__clang_major__ >= 4) -# define signbit(x) \ - (sizeof (x) == sizeof (long double) ? __builtin_signbitl (x) : \ - sizeof (x) == sizeof (double) ? __builtin_signbit (x) : \ - __builtin_signbitf (x)) -#else -# error "signbit should be three compiler built-ins" -#endif -#include -]gl_SIGNBIT_TEST_PROGRAM -])], - [gl_cv_func_signbit_builtins=yes], - [gl_cv_func_signbit_builtins=no], - [case "$host_os" in - # Guess yes on glibc systems. - *-gnu* | gnu*) gl_cv_func_signbit_builtins="guessing yes" ;; - # Guess yes on musl systems. - *-musl* | midipix*) gl_cv_func_signbit_builtins="guessing yes" ;; - # Guess yes on mingw, no on MSVC. - mingw*) if test -n "$GCC"; then - gl_cv_func_signbit_builtins="guessing yes" - else - gl_cv_func_signbit_builtins="guessing no" - fi - ;; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_signbit_builtins="$gl_cross_guess_normal" ;; - esac - ]) - ]) - dnl Use the compiler built-ins whenever possible, because they are more - dnl efficient than the system library functions (if they exist). - case "$gl_cv_func_signbit_builtins" in - *yes) - REPLACE_SIGNBIT_USING_BUILTINS=1 - ;; - *) - case "$gl_cv_func_signbit" in - *yes) ;; - *) - dnl REPLACE_SIGNBIT=1 makes sure the signbit[fdl] functions get built. - REPLACE_SIGNBIT=1 - ;; - esac - ;; - esac - dnl On Solaris 10, with CC in C++ mode, signbit is not available although - dnl is with cc in C mode. This cannot be worked around by defining - dnl _XOPEN_SOURCE=600, because the latter does not work in C++ mode on - dnl Solaris 11.0. Therefore use the replacement functions on Solaris. - case "$host_os" in - solaris*) - REPLACE_SIGNBIT=1 - ;; - esac - if test $REPLACE_SIGNBIT = 1; then - gl_FLOAT_SIGN_LOCATION - gl_DOUBLE_SIGN_LOCATION - gl_LONG_DOUBLE_SIGN_LOCATION - if test "$gl_cv_cc_float_signbit" = unknown; then - dnl Test whether copysignf() is declared. - AC_CHECK_DECLS([copysignf], , , [[#include ]]) - if test "$ac_cv_have_decl_copysignf" = yes; then - dnl Test whether copysignf() can be used without libm. - AC_CACHE_CHECK([whether copysignf can be used without linking with libm], - [gl_cv_func_copysignf_no_libm], - [ - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - float x, y;]], - [[return copysignf (x, y) < 0;]])], - [gl_cv_func_copysignf_no_libm=yes], - [gl_cv_func_copysignf_no_libm=no]) - ]) - if test $gl_cv_func_copysignf_no_libm = yes; then - AC_DEFINE([HAVE_COPYSIGNF_IN_LIBC], [1], - [Define if the copysignf function is declared in and available in libc.]) - fi - fi - fi - if test "$gl_cv_cc_double_signbit" = unknown; then - dnl Test whether copysign() is declared. - AC_CHECK_DECLS([copysign], , , [[#include ]]) - if test "$ac_cv_have_decl_copysign" = yes; then - dnl Test whether copysign() can be used without libm. - AC_CACHE_CHECK([whether copysign can be used without linking with libm], - [gl_cv_func_copysign_no_libm], - [ - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - double x, y;]], - [[return copysign (x, y) < 0;]])], - [gl_cv_func_copysign_no_libm=yes], - [gl_cv_func_copysign_no_libm=no]) - ]) - if test $gl_cv_func_copysign_no_libm = yes; then - AC_DEFINE([HAVE_COPYSIGN_IN_LIBC], [1], - [Define if the copysign function is declared in and available in libc.]) - fi - fi - fi - if test "$gl_cv_cc_long_double_signbit" = unknown; then - dnl Test whether copysignl() is declared. - AC_CHECK_DECLS([copysignl], , , [[#include ]]) - if test "$ac_cv_have_decl_copysignl" = yes; then - dnl Test whether copysignl() can be used without libm. - AC_CACHE_CHECK([whether copysignl can be used without linking with libm], - [gl_cv_func_copysignl_no_libm], - [ - AC_LINK_IFELSE( - [AC_LANG_PROGRAM( - [[#include - long double x, y;]], - [[return copysignl (x, y) < 0;]])], - [gl_cv_func_copysignl_no_libm=yes], - [gl_cv_func_copysignl_no_libm=no]) - ]) - if test $gl_cv_func_copysignl_no_libm = yes; then - AC_DEFINE([HAVE_COPYSIGNL_IN_LIBC], [1], - [Define if the copysignl function is declared in and available in libc.]) - fi - fi - fi - fi -]) - -AC_DEFUN([gl_SIGNBIT_TEST_PROGRAM], [[ -/* Global variables. - Needed because GCC 4 constant-folds __builtin_signbitl (literal) - but cannot constant-fold __builtin_signbitl (variable). */ -float vf; -double vd; -long double vl; -int main () -{ -/* HP cc on HP-UX 10.20 has a bug with the constant expression -0.0. - So we use -p0f and -p0d instead. */ -float p0f = 0.0f; -float m0f = -p0f; -double p0d = 0.0; -double m0d = -p0d; -/* On HP-UX 10.20, negating 0.0L does not yield -0.0L. - So we use another constant expression instead. - But that expression does not work on other platforms, such as when - cross-compiling to PowerPC on Mac OS X 10.5. */ -long double p0l = 0.0L; -#if defined __hpux || defined __sgi -long double m0l = -LDBL_MIN * LDBL_MIN; -#else -long double m0l = -p0l; -#endif - int result = 0; - if (signbit (vf)) /* link check */ - vf++; - { - float plus_inf = 1.0f / p0f; - float minus_inf = -1.0f / p0f; - if (!(!signbit (255.0f) - && signbit (-255.0f) - && !signbit (p0f) - && (memcmp (&m0f, &p0f, sizeof (float)) == 0 || signbit (m0f)) - && !signbit (plus_inf) - && signbit (minus_inf))) - result |= 1; - } - if (signbit (vd)) /* link check */ - vd++; - { - double plus_inf = 1.0 / p0d; - double minus_inf = -1.0 / p0d; - if (!(!signbit (255.0) - && signbit (-255.0) - && !signbit (p0d) - && (memcmp (&m0d, &p0d, sizeof (double)) == 0 || signbit (m0d)) - && !signbit (plus_inf) - && signbit (minus_inf))) - result |= 2; - } - if (signbit (vl)) /* link check */ - vl++; - { - long double plus_inf = 1.0L / p0l; - long double minus_inf = -1.0L / p0l; - if (signbit (255.0L)) - result |= 4; - if (!signbit (-255.0L)) - result |= 4; - if (signbit (p0l)) - result |= 8; - if (!(memcmp (&m0l, &p0l, sizeof (long double)) == 0 || signbit (m0l))) - result |= 16; - if (signbit (plus_inf)) - result |= 32; - if (!signbit (minus_inf)) - result |= 64; - } - return result; -} -]]) - -AC_DEFUN([gl_FLOAT_SIGN_LOCATION], -[ - gl_FLOATTYPE_SIGN_LOCATION([float], [gl_cv_cc_float_signbit], [f], [FLT]) -]) - -AC_DEFUN([gl_DOUBLE_SIGN_LOCATION], -[ - gl_FLOATTYPE_SIGN_LOCATION([double], [gl_cv_cc_double_signbit], [], [DBL]) -]) - -AC_DEFUN([gl_LONG_DOUBLE_SIGN_LOCATION], -[ - gl_FLOATTYPE_SIGN_LOCATION([long double], [gl_cv_cc_long_double_signbit], [L], [LDBL]) -]) - -AC_DEFUN([gl_FLOATTYPE_SIGN_LOCATION], -[ - AC_CACHE_CHECK([where to find the sign bit in a '$1'], - [$2], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include -#define NWORDS \ - ((sizeof ($1) + sizeof (unsigned int) - 1) / sizeof (unsigned int)) -typedef union { $1 value; unsigned int word[NWORDS]; } - memory_float; -static memory_float plus = { 1.0$3 }; -static memory_float minus = { -1.0$3 }; -int main () -{ - size_t j, k, i; - unsigned int m; - FILE *fp = fopen ("conftest.out", "w"); - if (fp == NULL) - return 1; - /* Find the different bit. */ - k = 0; m = 0; - for (j = 0; j < NWORDS; j++) - { - unsigned int x = plus.word[j] ^ minus.word[j]; - if ((x & (x - 1)) || (x && m)) - { - /* More than one bit difference. */ - fprintf (fp, "unknown"); - fclose (fp); - return 2; - } - if (x) - { - k = j; - m = x; - } - } - if (m == 0) - { - /* No difference. */ - fprintf (fp, "unknown"); - fclose (fp); - return 3; - } - /* Now m = plus.word[k] ^ ~minus.word[k]. */ - if (plus.word[k] & ~minus.word[k]) - { - /* Oh? The sign bit is set in the positive and cleared in the negative - numbers? */ - fprintf (fp, "unknown"); - fclose (fp); - return 4; - } - for (i = 0; ; i++) - if ((m >> i) & 1) - break; - fprintf (fp, "word %d bit %d", (int) k, (int) i); - if (fclose (fp) != 0) - return 5; - return 0; -} - ]])], - [$2=`cat conftest.out`], - [$2="unknown"], - [ - dnl When cross-compiling, we don't know. It depends on the - dnl ABI and compiler version. There are too many cases. - $2="unknown" - ]) - rm -f conftest.out - ]) - case "$]$2[" in - word*bit*) - word=`echo "$]$2[" | sed -e 's/word //' -e 's/ bit.*//'` - bit=`echo "$]$2[" | sed -e 's/word.*bit //'` - AC_DEFINE_UNQUOTED([$4][_SIGNBIT_WORD], [$word], - [Define as the word index where to find the sign of '$1'.]) - AC_DEFINE_UNQUOTED([$4][_SIGNBIT_BIT], [$bit], - [Define as the bit index in the word where to find the sign of '$1'.]) - ;; - esac -]) - -# Expands to code that defines a function signbitf(float). -# It extracts the sign bit of a non-NaN value. -AC_DEFUN([gl_FLOAT_SIGNBIT_CODE], -[ - gl_FLOATTYPE_SIGNBIT_CODE([float], [f], [f]) -]) - -# Expands to code that defines a function signbitd(double). -# It extracts the sign bit of a non-NaN value. -AC_DEFUN([gl_DOUBLE_SIGNBIT_CODE], -[ - gl_FLOATTYPE_SIGNBIT_CODE([double], [d], []) -]) - -# Expands to code that defines a function signbitl(long double). -# It extracts the sign bit of a non-NaN value. -AC_DEFUN([gl_LONG_DOUBLE_SIGNBIT_CODE], -[ - gl_FLOATTYPE_SIGNBIT_CODE([long double], [l], [L]) -]) - -AC_DEFUN([gl_FLOATTYPE_SIGNBIT_CODE], -[[ -static int -signbit$2 ($1 value) -{ - typedef union { $1 f; unsigned char b[sizeof ($1)]; } float_union; - static float_union plus_one = { 1.0$3 }; /* unused bits are zero here */ - static float_union minus_one = { -1.0$3 }; /* unused bits are zero here */ - /* Compute the sign bit mask as the XOR of plus_one and minus_one. */ - float_union u; - unsigned int i; - u.f = value; - for (i = 0; i < sizeof ($1); i++) - if (u.b[i] & (plus_one.b[i] ^ minus_one.b[i])) - return 1; - return 0; -} -]]) diff --git a/m4/size_max.m4 b/m4/size_max.m4 deleted file mode 100644 index 0763366dfc3..00000000000 --- a/m4/size_max.m4 +++ /dev/null @@ -1,75 +0,0 @@ -# size_max.m4 serial 12 -dnl Copyright (C) 2003, 2005-2006, 2008-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl From Bruno Haible. - -AC_PREREQ([2.61]) - -AC_DEFUN([gl_SIZE_MAX], -[ - AC_CHECK_HEADERS([stdint.h]) - dnl First test whether the system already has SIZE_MAX. - AC_CACHE_CHECK([for SIZE_MAX], [gl_cv_size_max], [ - gl_cv_size_max=no - AC_EGREP_CPP([Found it], [ -#include -#if HAVE_STDINT_H -#include -#endif -#ifdef SIZE_MAX -Found it -#endif -], [gl_cv_size_max=yes]) - if test $gl_cv_size_max != yes; then - dnl Define it ourselves. Here we assume that the type 'size_t' is not wider - dnl than the type 'unsigned long'. Try hard to find a definition that can - dnl be used in a preprocessor #if, i.e. doesn't contain a cast. - AC_COMPUTE_INT([size_t_bits_minus_1], [sizeof (size_t) * CHAR_BIT - 1], - [#include -#include ], [size_t_bits_minus_1=]) - AC_COMPUTE_INT([fits_in_uint], [sizeof (size_t) <= sizeof (unsigned int)], - [#include ], [fits_in_uint=]) - if test -n "$size_t_bits_minus_1" && test -n "$fits_in_uint"; then - if test $fits_in_uint = 1; then - dnl Even though SIZE_MAX fits in an unsigned int, it must be of type - dnl 'unsigned long' if the type 'size_t' is the same as 'unsigned long'. - AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[#include - extern size_t foo; - extern unsigned long foo; - ]], - [[]])], - [fits_in_uint=0]) - fi - dnl We cannot use 'expr' to simplify this expression, because 'expr' - dnl works only with 'long' integers in the host environment, while we - dnl might be cross-compiling from a 32-bit platform to a 64-bit platform. - if test $fits_in_uint = 1; then - gl_cv_size_max="(((1U << $size_t_bits_minus_1) - 1) * 2 + 1)" - else - gl_cv_size_max="(((1UL << $size_t_bits_minus_1) - 1) * 2 + 1)" - fi - else - dnl Shouldn't happen, but who knows... - gl_cv_size_max='((size_t)~(size_t)0)' - fi - fi - ]) - if test "$gl_cv_size_max" != yes; then - AC_DEFINE_UNQUOTED([SIZE_MAX], [$gl_cv_size_max], - [Define as the maximum value of type 'size_t', if the system doesn't define it.]) - fi - dnl Don't redefine SIZE_MAX in config.h if config.h is re-included after - dnl . Remember that the #undef in AH_VERBATIM gets replaced with - dnl #define by AC_DEFINE_UNQUOTED. - AH_VERBATIM([SIZE_MAX], -[/* Define as the maximum value of type 'size_t', if the system doesn't define - it. */ -#ifndef SIZE_MAX -# undef SIZE_MAX -#endif]) -]) diff --git a/m4/stdint_h.m4 b/m4/stdint_h.m4 deleted file mode 100644 index 70349f6cb21..00000000000 --- a/m4/stdint_h.m4 +++ /dev/null @@ -1,27 +0,0 @@ -# stdint_h.m4 serial 9 -dnl Copyright (C) 1997-2004, 2006, 2008-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -dnl From Paul Eggert. - -# Define HAVE_STDINT_H_WITH_UINTMAX if exists, -# doesn't clash with , and declares uintmax_t. - -AC_DEFUN([gl_AC_HEADER_STDINT_H], -[ - AC_CACHE_CHECK([for stdint.h], [gl_cv_header_stdint_h], - [AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM( - [[#include - #include ]], - [[uintmax_t i = (uintmax_t) -1; return !i;]])], - [gl_cv_header_stdint_h=yes], - [gl_cv_header_stdint_h=no])]) - if test $gl_cv_header_stdint_h = yes; then - AC_DEFINE_UNQUOTED([HAVE_STDINT_H_WITH_UINTMAX], [1], - [Define if exists, doesn't clash with , - and declares uintmax_t. ]) - fi -]) diff --git a/m4/vasnprintf.m4 b/m4/vasnprintf.m4 deleted file mode 100644 index df87b9e7986..00000000000 --- a/m4/vasnprintf.m4 +++ /dev/null @@ -1,462 +0,0 @@ -# vasnprintf.m4 serial 50 -dnl Copyright (C) 2002-2004, 2006-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_VASNPRINTF], -[ - AC_CHECK_FUNCS_ONCE([vasnprintf]) - if test $ac_cv_func_vasnprintf = no; then - gl_REPLACE_VASNPRINTF - fi -]) - -AC_DEFUN([gl_REPLACE_VASNPRINTF], -[ - AC_CHECK_FUNCS_ONCE([vasnprintf]) - AC_LIBOBJ([vasnprintf]) - AC_LIBOBJ([printf-args]) - AC_LIBOBJ([printf-parse]) - AC_LIBOBJ([asnprintf]) - if test $ac_cv_func_vasnprintf = yes; then - AC_DEFINE([REPLACE_VASNPRINTF], [1], - [Define if vasnprintf exists but is overridden by gnulib.]) - fi - gl_PREREQ_PRINTF_ARGS - gl_PREREQ_PRINTF_PARSE - gl_PREREQ_VASNPRINTF - gl_PREREQ_ASNPRINTF -]) - -AC_DEFUN([gl_FUNC_VASNWPRINTF], -[ - AC_LIBOBJ([printf-args]) - gl_PREREQ_PRINTF_ARGS - gl_PREREQ_PRINTF_PARSE - gl_PREREQ_VASNWPRINTF - gl_PREREQ_ASNPRINTF -]) - -# Prerequisites of lib/printf-args.h, lib/printf-args.c. -AC_DEFUN([gl_PREREQ_PRINTF_ARGS], -[ - AC_REQUIRE([gt_TYPE_WCHAR_T]) - AC_REQUIRE([gt_TYPE_WINT_T]) -]) - -# Prerequisites of lib/printf-parse.h, lib/printf-parse.c. -# Prerequisites of lib/wprintf-parse.h, lib/wprintf-parse.c. -AC_DEFUN([gl_PREREQ_PRINTF_PARSE], -[ - AC_REQUIRE([gl_FEATURES_H]) - AC_REQUIRE([gt_TYPE_WCHAR_T]) - AC_REQUIRE([gt_TYPE_WINT_T]) - AC_REQUIRE([AC_TYPE_SIZE_T]) - AC_CHECK_TYPE([ptrdiff_t], , - [AC_DEFINE([ptrdiff_t], [long], - [Define as the type of the result of subtracting two pointers, if the system doesn't define it.]) - ]) - AC_REQUIRE([gt_AC_TYPE_INTMAX_T]) -]) - -# Prerequisites of lib/vasnprintf.c if !WIDE_CHAR_VERSION. -AC_DEFUN_ONCE([gl_PREREQ_VASNPRINTF], -[ - AC_CHECK_FUNCS([snprintf strnlen wcrtomb]) - dnl Use the _snprintf function only if it is declared (because on NetBSD it - dnl is defined as a weak alias of snprintf; we prefer to use the latter). - AC_CHECK_DECLS([_snprintf], , , [[#include ]]) - dnl We can avoid a lot of code by assuming that snprintf's return value - dnl conforms to ISO C99. So check that. - AC_REQUIRE([gl_SNPRINTF_RETVAL_C99]) - case "$gl_cv_func_snprintf_retval_c99" in - *yes) - AC_DEFINE([HAVE_SNPRINTF_RETVAL_C99], [1], - [Define if the return value of the snprintf function is the number of - of bytes (excluding the terminating NUL) that would have been produced - if the buffer had been large enough.]) - ;; - esac - dnl Additionally, the use of %n can be eliminated by assuming that snprintf - dnl always produces NUL-terminated strings (no truncation). - AC_REQUIRE([gl_SNPRINTF_TRUNCATION_C99]) - case "$gl_cv_func_snprintf_truncation_c99" in - *yes) - AC_DEFINE([HAVE_SNPRINTF_TRUNCATION_C99], [1], - [Define if the string produced by the snprintf function is always NUL - terminated.]) - ;; - esac - gl_PREREQ_VASNXPRINTF -]) - -# Prerequisites of lib/vasnwprintf.c. -AC_DEFUN_ONCE([gl_PREREQ_VASNWPRINTF], -[ - AC_CHECK_FUNCS_ONCE([swprintf wcsnlen mbrtowc]) - AC_CHECK_DECLS([_snwprintf], , , [[#include ]]) - AC_CHECK_DECLS([wcsnlen], , , [[#include ]]) - gl_SWPRINTF_WORKS - case "$gl_cv_func_swprintf_works" in - *yes) - AC_DEFINE([HAVE_WORKING_SWPRINTF], [1], - [Define if the swprintf function works correctly when it produces output - that contains null wide characters.]) - ;; - esac - gl_MBRTOWC_C_LOCALE - case "$gl_cv_func_mbrtowc_C_locale_sans_EILSEQ" in - *yes) - AC_CACHE_CHECK([whether swprintf in the C locale is free of encoding errors], - [gl_cv_func_swprintf_C_locale_sans_EILSEQ], - [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#ifndef __USE_MINGW_ANSI_STDIO -# define __USE_MINGW_ANSI_STDIO 1 -#endif -#include -#include -int main() -{ - int result = 0; - { /* This test fails on glibc 2.35, musl libc 1.2.4, FreeBSD 13.2, NetBSD 9.3, - OpenBSD 7.2, Cygwin 2.9.0. - Reported at . */ - wchar_t buf[12]; - int ret = swprintf (buf, 12, L"%c", '\377'); - if (ret < 0) - result |= 1; - } - return result; -}]])], - [gl_cv_func_swprintf_C_locale_sans_EILSEQ=yes], - [gl_cv_func_swprintf_C_locale_sans_EILSEQ=no], - [case "$host_os" in - # Guess no on glibc systems. - *-gnu* | gnu*) gl_cv_func_swprintf_C_locale_sans_EILSEQ="guessing yes";; - # Guess no on musl systems. - *-musl* | midipix*) gl_cv_func_swprintf_C_locale_sans_EILSEQ="guessing no";; - # If we don't know, obey --enable-cross-guesses. - *) gl_cv_func_swprintf_C_locale_sans_EILSEQ="$gl_cross_guess_normal";; - esac - ]) - ]) - ;; - esac - if case "$gl_cv_func_mbrtowc_C_locale_sans_EILSEQ" in - *yes) false ;; - *) true ;; - esac \ - || case "$gl_cv_func_swprintf_C_locale_sans_EILSEQ" in - *yes) false ;; - *) true ;; - esac; then - AC_DEFINE([NEED_WPRINTF_DIRECTIVE_C], [1], - [Define if the vasnwprintf implementation needs special code for - the 'c' directive.]) - fi - gl_SWPRINTF_DIRECTIVE_LA - case "$gl_cv_func_swprintf_directive_la" in - *yes) ;; - *) - AC_DEFINE([NEED_WPRINTF_DIRECTIVE_LA], [1], - [Define if the vasnwprintf implementation needs special code for - the 'a' directive with 'long double' arguments.]) - ;; - esac - gl_SWPRINTF_DIRECTIVE_LC - case "$gl_cv_func_swprintf_directive_lc" in - *yes) ;; - *) - AC_DEFINE([NEED_WPRINTF_DIRECTIVE_LC], [1], - [Define if the vasnwprintf implementation needs special code for - the 'lc' directive.]) - ;; - esac - gl_MUSL_LIBC - gl_PREREQ_VASNXPRINTF -]) - -# Common prerequisites of lib/vasnprintf.c and lib/vasnwprintf.c. -AC_DEFUN_ONCE([gl_PREREQ_VASNXPRINTF], -[ - AC_REQUIRE([AC_FUNC_ALLOCA]) - AC_REQUIRE([gt_TYPE_WCHAR_T]) - AC_REQUIRE([gt_TYPE_WINT_T]) - AC_CHECK_FUNCS([wcslen]) - dnl Knowing DBL_EXPBIT0_WORD and DBL_EXPBIT0_BIT enables an optimization - dnl in the code for NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE. - AC_REQUIRE([gl_DOUBLE_EXPONENT_LOCATION]) -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting 'long double' -# arguments. -AC_DEFUN_ONCE([gl_PREREQ_VASNPRINTF_LONG_DOUBLE], -[ - AC_REQUIRE([gl_PRINTF_LONG_DOUBLE]) - case "$gl_cv_func_printf_long_double" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_LONG_DOUBLE], [1], - [Define if the vasnprintf implementation needs special code for - 'long double' arguments.]) - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting infinite 'double' -# arguments. -AC_DEFUN([gl_PREREQ_VASNPRINTF_INFINITE_DOUBLE], -[ - AC_REQUIRE([gl_PRINTF_INFINITE]) - case "$gl_cv_func_printf_infinite" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_INFINITE_DOUBLE], [1], - [Define if the vasnprintf implementation needs special code for - infinite 'double' arguments.]) - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting infinite 'long double' -# arguments. -AC_DEFUN([gl_PREREQ_VASNPRINTF_INFINITE_LONG_DOUBLE], -[ - AC_REQUIRE([gl_PRINTF_INFINITE_LONG_DOUBLE]) - dnl There is no need to set NEED_PRINTF_INFINITE_LONG_DOUBLE if - dnl NEED_PRINTF_LONG_DOUBLE is already set. - AC_REQUIRE([gl_PREREQ_VASNPRINTF_LONG_DOUBLE]) - case "$gl_cv_func_printf_long_double" in - *yes) - case "$gl_cv_func_printf_infinite_long_double" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_INFINITE_LONG_DOUBLE], [1], - [Define if the vasnprintf implementation needs special code for - infinite 'long double' arguments.]) - ;; - esac - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting the 'a' directive. -AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_A], -[ - AC_REQUIRE([gl_PRINTF_DIRECTIVE_A]) - case "$gl_cv_func_printf_directive_a" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_DIRECTIVE_A], [1], - [Define if the vasnprintf implementation needs special code for - the 'a' and 'A' directives.]) - gl_CHECK_FUNCS_ANDROID([nl_langinfo], [[#include ]]) - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting the 'b' directive. -AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_B], -[ - AC_REQUIRE([gl_PRINTF_DIRECTIVE_B]) - case "$gl_cv_func_printf_directive_b" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_DIRECTIVE_B], [1], - [Define if the vasnprintf implementation needs special code for - the 'b' directive.]) - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting the 'F' directive. -AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_F], -[ - AC_REQUIRE([gl_PRINTF_DIRECTIVE_F]) - case "$gl_cv_func_printf_directive_f" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_DIRECTIVE_F], [1], - [Define if the vasnprintf implementation needs special code for - the 'F' directive.]) - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting the 'ls' directive. -AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_LS], -[ - AC_REQUIRE([gl_PRINTF_DIRECTIVE_LS]) - case "$gl_cv_func_printf_directive_ls" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_DIRECTIVE_LS], [1], - [Define if the vasnprintf implementation needs special code for - the 'ls' directive.]) - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting the 'lc' directive. -AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_LC], -[ - AC_REQUIRE([gl_PRINTF_DIRECTIVE_LC]) - case "$gl_cv_func_printf_directive_lc" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_DIRECTIVE_LC], [1], - [Define if the vasnprintf implementation needs special code for - the 'lc' directive.]) - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting the ' flag. -AC_DEFUN([gl_PREREQ_VASNPRINTF_FLAG_GROUPING], -[ - AC_REQUIRE([gl_PRINTF_FLAG_GROUPING]) - case "$gl_cv_func_printf_flag_grouping" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_FLAG_GROUPING], [1], - [Define if the vasnprintf implementation needs special code for the - ' flag.]) - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting the '-' flag. -AC_DEFUN([gl_PREREQ_VASNPRINTF_FLAG_LEFTADJUST], -[ - AC_REQUIRE([gl_PRINTF_FLAG_LEFTADJUST]) - case "$gl_cv_func_printf_flag_leftadjust" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_FLAG_LEFTADJUST], [1], - [Define if the vasnprintf implementation needs special code for the - '-' flag.]) - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting the 0 flag. -AC_DEFUN([gl_PREREQ_VASNPRINTF_FLAG_ZERO], -[ - AC_REQUIRE([gl_PRINTF_FLAG_ZERO]) - case "$gl_cv_func_printf_flag_zero" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_FLAG_ZERO], [1], - [Define if the vasnprintf implementation needs special code for the - 0 flag.]) - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting large precisions. -AC_DEFUN([gl_PREREQ_VASNPRINTF_PRECISION], -[ - AC_REQUIRE([gl_PRINTF_PRECISION]) - case "$gl_cv_func_printf_precision" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_UNBOUNDED_PRECISION], [1], - [Define if the vasnprintf implementation needs special code for - supporting large precisions without arbitrary bounds.]) - AC_DEFINE([NEED_PRINTF_DOUBLE], [1], - [Define if the vasnprintf implementation needs special code for - 'double' arguments.]) - AC_DEFINE([NEED_PRINTF_LONG_DOUBLE], [1], - [Define if the vasnprintf implementation needs special code for - 'long double' arguments.]) - ;; - esac -]) - -# Extra prerequisites of lib/vasnprintf.c for surviving out-of-memory -# conditions. -AC_DEFUN([gl_PREREQ_VASNPRINTF_ENOMEM], -[ - AC_REQUIRE([gl_PRINTF_ENOMEM]) - case "$gl_cv_func_printf_enomem" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_ENOMEM], [1], - [Define if the vasnprintf implementation needs special code for - surviving out-of-memory conditions.]) - AC_DEFINE([NEED_PRINTF_DOUBLE], [1], - [Define if the vasnprintf implementation needs special code for - 'double' arguments.]) - AC_DEFINE([NEED_PRINTF_LONG_DOUBLE], [1], - [Define if the vasnprintf implementation needs special code for - 'long double' arguments.]) - ;; - esac -]) - -# Prerequisites of lib/vasnprintf.c including all extras for POSIX compliance. -AC_DEFUN([gl_PREREQ_VASNPRINTF_WITH_POSIX_EXTRAS], -[ - AC_REQUIRE([gl_PREREQ_VASNPRINTF]) - gl_PREREQ_VASNPRINTF_LONG_DOUBLE - gl_PREREQ_VASNPRINTF_INFINITE_DOUBLE - gl_PREREQ_VASNPRINTF_INFINITE_LONG_DOUBLE - gl_PREREQ_VASNPRINTF_DIRECTIVE_A - gl_PREREQ_VASNPRINTF_DIRECTIVE_B - gl_PREREQ_VASNPRINTF_DIRECTIVE_F - gl_PREREQ_VASNPRINTF_DIRECTIVE_LS - gl_PREREQ_VASNPRINTF_DIRECTIVE_LC - gl_PREREQ_VASNPRINTF_FLAG_GROUPING - gl_PREREQ_VASNPRINTF_FLAG_LEFTADJUST - gl_PREREQ_VASNPRINTF_FLAG_ZERO - gl_PREREQ_VASNPRINTF_PRECISION - gl_PREREQ_VASNPRINTF_ENOMEM -]) - -# Extra prerequisites of lib/vasnprintf.c for supporting the 'B' directive. -AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_UPPERCASE_B], -[ - AC_REQUIRE([gl_PRINTF_DIRECTIVE_UPPERCASE_B]) - case "$gl_cv_func_printf_directive_uppercase_b" in - *yes) - ;; - *) - AC_DEFINE([NEED_PRINTF_DIRECTIVE_UPPERCASE_B], [1], - [Define if the vasnprintf implementation needs special code for - the 'B' directive.]) - ;; - esac -]) - -# Prerequisites of lib/vasnprintf.c including all extras for POSIX compliance -# and GNU compatibility. -AC_DEFUN([gl_PREREQ_VASNPRINTF_WITH_GNU_EXTRAS], -[ - gl_PREREQ_VASNPRINTF_WITH_POSIX_EXTRAS - AC_DEFINE([SUPPORT_GNU_PRINTF_DIRECTIVES], [1], - [Define if the vasnprintf implementation should support GNU compatible - printf directives.]) - gl_PREREQ_VASNPRINTF_DIRECTIVE_UPPERCASE_B -]) - -# Prerequisites of lib/asnprintf.c. -# Prerequisites of lib/asnwprintf.c. -AC_DEFUN([gl_PREREQ_ASNPRINTF], -[ -]) diff --git a/m4/vasprintf-posix.m4 b/m4/vasprintf-posix.m4 deleted file mode 100644 index 3c7a6540bd1..00000000000 --- a/m4/vasprintf-posix.m4 +++ /dev/null @@ -1,113 +0,0 @@ -# vasprintf-posix.m4 serial 17 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_VASPRINTF_POSIX], -[ - AC_REQUIRE([gl_FUNC_VASPRINTF_IS_POSIX]) - if test $gl_cv_func_vasprintf_posix = no; then - gl_PREREQ_VASNPRINTF_WITH_POSIX_EXTRAS - gl_REPLACE_VASNPRINTF - gl_REPLACE_VASPRINTF - fi -]) - -dnl Test whether vasprintf exists and is POSIX compliant. -dnl Result is gl_cv_func_vasprintf_posix. -AC_DEFUN([gl_FUNC_VASPRINTF_IS_POSIX], -[ - AC_REQUIRE([gl_PRINTF_SIZES_C99]) - AC_REQUIRE([gl_PRINTF_SIZES_C23]) - AC_REQUIRE([gl_PRINTF_LONG_DOUBLE]) - AC_REQUIRE([gl_PRINTF_INFINITE]) - AC_REQUIRE([gl_PRINTF_INFINITE_LONG_DOUBLE]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_A]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_B]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_F]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_N]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_LS]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_LC]) - AC_REQUIRE([gl_PRINTF_POSITIONS]) - AC_REQUIRE([gl_PRINTF_FLAG_GROUPING]) - AC_REQUIRE([gl_PRINTF_FLAG_LEFTADJUST]) - AC_REQUIRE([gl_PRINTF_FLAG_ZERO]) - AC_REQUIRE([gl_PRINTF_PRECISION]) - AC_REQUIRE([gl_PRINTF_ENOMEM]) - gl_cv_func_vasprintf_posix=no - AC_CHECK_FUNCS([vasprintf]) - case "$gl_cv_func_printf_sizes_c99" in - *yes) - case "$gl_cv_func_printf_sizes_c23" in - *yes) - case "$gl_cv_func_printf_long_double" in - *yes) - case "$gl_cv_func_printf_infinite" in - *yes) - case "$gl_cv_func_printf_infinite_long_double" in - *yes) - case "$gl_cv_func_printf_directive_a" in - *yes) - case "$gl_cv_func_printf_directive_b" in - *yes) - case "$gl_cv_func_printf_directive_f" in - *yes) - case "$gl_cv_func_printf_directive_n" in - *yes) - case "$gl_cv_func_printf_directive_ls" in - *yes) - case "$gl_cv_func_printf_directive_lc" in - *yes) - case "$gl_cv_func_printf_positions" in - *yes) - case "$gl_cv_func_printf_flag_grouping" in - *yes) - case "$gl_cv_func_printf_flag_leftadjust" in - *yes) - case "$gl_cv_func_printf_flag_zero" in - *yes) - case "$gl_cv_func_printf_precision" in - *yes) - case "$gl_cv_func_printf_enomem" in - *yes) - if test $ac_cv_func_vasprintf = yes; then - # vasprintf exists and is - # already POSIX compliant. - gl_cv_func_vasprintf_posix=yes - fi - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac -]) diff --git a/m4/vasprintf.m4 b/m4/vasprintf.m4 deleted file mode 100644 index 6e6156a7549..00000000000 --- a/m4/vasprintf.m4 +++ /dev/null @@ -1,46 +0,0 @@ -# vasprintf.m4 serial 6 -dnl Copyright (C) 2002-2003, 2006-2007, 2009-2023 Free Software Foundation, -dnl Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_VASPRINTF], -[ - AC_CHECK_FUNCS([vasprintf]) - if test $ac_cv_func_vasprintf = no; then - gl_REPLACE_VASPRINTF - fi -]) - -AC_DEFUN([gl_REPLACE_VASPRINTF], -[ - AC_LIBOBJ([vasprintf]) - AC_LIBOBJ([asprintf]) - AC_REQUIRE([gl_STDIO_H_DEFAULTS]) - if test $ac_cv_func_vasprintf = yes; then - REPLACE_VASPRINTF=1 - else - HAVE_VASPRINTF=0 - fi - gl_PREREQ_VASPRINTF_H - gl_PREREQ_VASPRINTF - gl_PREREQ_ASPRINTF -]) - -# Prerequisites of the vasprintf portion of lib/stdio.h. -AC_DEFUN([gl_PREREQ_VASPRINTF_H], -[ - dnl Persuade glibc to declare asprintf() and vasprintf(). - AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) -]) - -# Prerequisites of lib/vasprintf.c. -AC_DEFUN([gl_PREREQ_VASPRINTF], -[ -]) - -# Prerequisites of lib/asprintf.c. -AC_DEFUN([gl_PREREQ_ASPRINTF], -[ -]) diff --git a/m4/vfprintf-posix.m4 b/m4/vfprintf-posix.m4 deleted file mode 100644 index 6b51c50adab..00000000000 --- a/m4/vfprintf-posix.m4 +++ /dev/null @@ -1,122 +0,0 @@ -# vfprintf-posix.m4 serial 18 -dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_VFPRINTF_POSIX], -[ - AC_REQUIRE([gl_FUNC_VFPRINTF_IS_POSIX]) - if test $gl_cv_func_vfprintf_posix = no; then - gl_PREREQ_VASNPRINTF_WITH_POSIX_EXTRAS - gl_REPLACE_VASNPRINTF - gl_REPLACE_VFPRINTF - fi -]) - -dnl Test whether vfprintf is POSIX compliant. -dnl Result is gl_cv_func_vfprintf_posix. -AC_DEFUN([gl_FUNC_VFPRINTF_IS_POSIX], -[ - AC_REQUIRE([gl_PRINTF_SIZES_C99]) - AC_REQUIRE([gl_PRINTF_SIZES_C23]) - AC_REQUIRE([gl_PRINTF_LONG_DOUBLE]) - AC_REQUIRE([gl_PRINTF_INFINITE]) - AC_REQUIRE([gl_PRINTF_INFINITE_LONG_DOUBLE]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_A]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_B]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_F]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_N]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_LS]) - AC_REQUIRE([gl_PRINTF_DIRECTIVE_LC]) - AC_REQUIRE([gl_PRINTF_POSITIONS]) - AC_REQUIRE([gl_PRINTF_FLAG_GROUPING]) - AC_REQUIRE([gl_PRINTF_FLAG_LEFTADJUST]) - AC_REQUIRE([gl_PRINTF_FLAG_ZERO]) - AC_REQUIRE([gl_PRINTF_PRECISION]) - AC_REQUIRE([gl_PRINTF_ENOMEM]) - gl_cv_func_vfprintf_posix=no - case "$gl_cv_func_printf_sizes_c99" in - *yes) - case "$gl_cv_func_printf_sizes_c23" in - *yes) - case "$gl_cv_func_printf_long_double" in - *yes) - case "$gl_cv_func_printf_infinite" in - *yes) - case "$gl_cv_func_printf_infinite_long_double" in - *yes) - case "$gl_cv_func_printf_directive_a" in - *yes) - case "$gl_cv_func_printf_directive_b" in - *yes) - case "$gl_cv_func_printf_directive_f" in - *yes) - case "$gl_cv_func_printf_directive_n" in - *yes) - case "$gl_cv_func_printf_directive_ls" in - *yes) - case "$gl_cv_func_printf_directive_lc" in - *yes) - case "$gl_cv_func_printf_positions" in - *yes) - case "$gl_cv_func_printf_flag_grouping" in - *yes) - case "$gl_cv_func_printf_flag_leftadjust" in - *yes) - case "$gl_cv_func_printf_flag_zero" in - *yes) - case "$gl_cv_func_printf_precision" in - *yes) - case "$gl_cv_func_printf_enomem" in - *yes) - # vfprintf exists and is - # already POSIX compliant. - gl_cv_func_vfprintf_posix=yes - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac - ;; - esac -]) - -AC_DEFUN([gl_REPLACE_VFPRINTF], -[ - AC_REQUIRE([gl_STDIO_H_DEFAULTS]) - AC_LIBOBJ([vfprintf]) - REPLACE_VFPRINTF=1 - AC_DEFINE([REPLACE_VFPRINTF_POSIX], [1], - [Define if vfprintf is overridden by a POSIX compliant gnulib implementation.]) - gl_PREREQ_VFPRINTF -]) - -AC_DEFUN([gl_PREREQ_VFPRINTF], [:]) diff --git a/m4/xsize.m4 b/m4/xsize.m4 deleted file mode 100644 index 649db9c5eab..00000000000 --- a/m4/xsize.m4 +++ /dev/null @@ -1,12 +0,0 @@ -# xsize.m4 serial 5 -dnl Copyright (C) 2003-2004, 2008-2023 Free Software Foundation, Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_XSIZE], -[ - dnl Prerequisites of lib/xsize.h. - AC_REQUIRE([gl_SIZE_MAX]) - AC_CHECK_HEADERS([stdint.h]) -]) diff --git a/msdos/sedlibmk.inp b/msdos/sedlibmk.inp index 09f98f8e0a2..4300be47d0b 100644 --- a/msdos/sedlibmk.inp +++ b/msdos/sedlibmk.inp @@ -436,7 +436,6 @@ s/= @GL_GENERATE_STDDEF_H_CONDITION@/= 1/ s/= @GL_GENERATE_STDINT_H_CONDITION@/= 1/ s/= @GL_GENERATE_LIMITS_H_CONDITION@/= 1/ s/= @GL_GENERATE_ERRNO_H_CONDITION@/= / -s/= @GL_GENERATE_FLOAT_H_CONDITION@/= / s/= @GL_GENERATE_GETOPT_CDEFS_H_CONDITION@/= 1/ s/= @GL_GENERATE_GETOPT_H_CONDITION@/= 1/ s/= @GL_GENERATE_GMP_H_CONDITION@/= 1/ @@ -464,7 +463,6 @@ OMIT_GNULIB_MODULE_euidaccess = true\ OMIT_GNULIB_MODULE_faccessat = true\ OMIT_GNULIB_MODULE_fcntl = true\ OMIT_GNULIB_MODULE_fdopendir = true\ -OMIT_GNULIB_MODULE_float = true\ OMIT_GNULIB_MODULE_fstatat = true\ OMIT_GNULIB_MODULE_fsync = true\ OMIT_GNULIB_MODULE_getline = true\ @@ -480,8 +478,6 @@ OMIT_GNULIB_MODULE_math = true\ OMIT_GNULIB_MODULE_nanosleep = true\ OMIT_GNULIB_MODULE_open = true\ OMIT_GNULIB_MODULE_pipe2 = true\ -OMIT_GNULIB_MODULE_printf-posix = true\ -OMIT_GNULIB_MODULE_printf-frexpl = true\ OMIT_GNULIB_MODULE_pselect = true\ OMIT_GNULIB_MODULE_putenv = true\ OMIT_GNULIB_MODULE_qcopy-acl = true\ @@ -494,7 +490,6 @@ OMIT_GNULIB_MODULE_symlink = true\ OMIT_GNULIB_MODULE_sys_select = true\ OMIT_GNULIB_MODULE_sys_time = true\ OMIT_GNULIB_MODULE_crypto\/md5 = true\ -OMIT_GNULIB_MODULE_vprintf-posix = true /^arg-nonnull\.h:/,/^[ ][ ]*mv /c\ arg-nonnull.h: $(top_srcdir)/build-aux/snippet/arg-nonnull.h\ sed -n -e '/GL_ARG_NONNULL/,$$p' < $(top_srcdir)/build-aux/snippet/arg-nonnull.h > $@ commit 072a8a434eb0f39f6e4a0c956d3d8cf44b00cff8 Merge: 563df3218e9 18e7bc87521 Author: Po Lu Date: Mon Aug 7 07:56:44 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 18e7bc87521e3c48b819cfe4a113f532ba905561 Author: Stefan Kangas Date: Sun Aug 6 20:39:10 2023 +0200 Mark Emacs 21 compat aliases `lm-*-mark` obsolete * lisp/emacs-lisp/lisp-mnt.el (lm-section-mark, lm-code-mark) (lm-commentary-mark, lm-history-mark): Mark Emacs 21 compatibility aliases obsolete. Update all callers to use the new name. diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el index c5e69d5ef56..aadd6480086 100644 --- a/lisp/emacs-lisp/checkdoc.el +++ b/lisp/emacs-lisp/checkdoc.el @@ -2382,7 +2382,7 @@ checkdoc-file-comments-engine err (or ;; * Commentary Section - (if (and (not (lm-commentary-mark)) + (if (and (not (lm-commentary-start)) ;; No need for a commentary section in test files. (not (string-match (rx (or (seq (or "-test.el" "-tests.el") string-end) @@ -2419,10 +2419,10 @@ checkdoc-file-comments-engine (if (or (not checkdoc-force-history-flag) (file-exists-p "ChangeLog") (file-exists-p "../ChangeLog") - (lm-history-mark)) + (lm-history-start)) nil (progn - (goto-char (or (lm-commentary-mark) (point-min))) + (goto-char (or (lm-commentary-start) (point-min))) (cond ((re-search-forward "write\\s-+to\\s-+the\\s-+Free Software Foundation, Inc." @@ -2443,7 +2443,7 @@ checkdoc-file-comments-engine err (or ;; * Code section - (if (not (lm-code-mark)) + (if (not (lm-code-start)) (let ((cont t) pos) (goto-char (point-min)) @@ -2494,7 +2494,7 @@ checkdoc-file-comments-engine ;; Let's spellcheck the commentary section. This is the only ;; section that is easy to pick out, and it is also the most ;; visible section (with the finder). - (let ((cm (lm-commentary-mark))) + (let ((cm (lm-commentary-start))) (when cm (save-excursion (goto-char cm) diff --git a/lisp/emacs-lisp/lisp-mnt.el b/lisp/emacs-lisp/lisp-mnt.el index 1fa1297e787..67c9db29b7f 100644 --- a/lisp/emacs-lisp/lisp-mnt.el +++ b/lisp/emacs-lisp/lisp-mnt.el @@ -1,7 +1,6 @@ ;;; lisp-mnt.el --- utility functions for Emacs Lisp maintainers -*- lexical-binding:t -*- -;; Copyright (C) 1992, 1994, 1997, 2000-2023 Free Software Foundation, -;; Inc. +;; Copyright (C) 1992-2023 Free Software Foundation, Inc. ;; Author: Eric S. Raymond ;; Maintainer: emacs-devel@gnu.org @@ -52,7 +51,7 @@ ;; ;; * Copyright line, which looks more or less like this: ;; -;; ;; Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. +;; ;; Copyright (C) 1999-2001 Free Software Foundation, Inc. ;; ;; * A blank line ;; @@ -187,7 +186,6 @@ lm-section-start (goto-char (point-min)) (if (re-search-forward (lm-get-header-re header 'section) nil t) (line-beginning-position (if after 2)))))) -(defalias 'lm-section-mark 'lm-section-start) (defun lm-section-end (header) "Return the buffer location of the end of a given section. @@ -230,12 +228,10 @@ lm-section-end (defun lm-code-start () "Return the buffer location of the `Code' start marker." (lm-section-start "Code")) -(defalias 'lm-code-mark 'lm-code-start) (defun lm-commentary-start () "Return the buffer location of the `Commentary' start marker." (lm-section-start lm-commentary-header)) -(defalias 'lm-commentary-mark 'lm-commentary-start) (defun lm-commentary-end () "Return the buffer location of the `Commentary' section end." @@ -244,7 +240,6 @@ lm-commentary-end (defun lm-history-start () "Return the buffer location of the `History' start marker." (lm-section-start lm-history-header)) -(defalias 'lm-history-mark 'lm-history-start) (defun lm-copyright-mark () "Return the buffer location of the `Copyright' line." @@ -258,7 +253,7 @@ lm-header "Return the contents of the header named HEADER." (goto-char (point-min)) (let ((case-fold-search t)) - (when (and (re-search-forward (lm-get-header-re header) (lm-code-mark) t) + (when (and (re-search-forward (lm-get-header-re header) (lm-code-start) t) ;; RCS ident likes format "$identifier: data$" (looking-at (if (save-excursion @@ -402,7 +397,7 @@ lm-last-modified-date (when (progn (goto-char (point-min)) (re-search-forward "\\$[I]d: [^ ]+ [^ ]+ \\([^/]+\\)/\\([^/]+\\)/\\([^ ]+\\) " - (lm-code-mark) t)) + (lm-code-start) t)) (let ((dd (match-string 3)) (mm (match-string 2)) (yyyy (match-string 1))) @@ -420,7 +415,7 @@ lm-version This can be found in an RCS or SCCS header." (lm-with-file file (or (lm-header "version") - (let ((header-max (lm-code-mark))) + (let ((header-max (lm-code-start))) (goto-char (point-min)) (cond ;; Look for an RCS header @@ -557,11 +552,11 @@ lm-verify "`Keywords:' tag missing") ((not (lm-keywords-finder-p)) "`Keywords:' has no valid finder keywords (see `finder-known-keywords')") - ((not (lm-commentary-mark)) + ((not (lm-commentary-start)) "Can't find a `Commentary' section marker") - ((not (lm-history-mark)) + ((not (lm-history-start)) "Can't find a `History' section marker") - ((not (lm-code-mark)) + ((not (lm-code-start)) "Can't find a `Code' section marker") ((progn (goto-char (point-max)) @@ -631,6 +626,11 @@ lm-report-bug (message "%s" (substitute-command-keys "Type \\[mail-send] to send bug report.")))) +(define-obsolete-function-alias 'lm-section-mark #'lm-section-start "30.1") +(define-obsolete-function-alias 'lm-code-mark #'lm-code-start "30.1") +(define-obsolete-function-alias 'lm-commentary-mark #'lm-commentary-start "30.1") +(define-obsolete-function-alias 'lm-history-mark #'lm-history-start "30.1") + (provide 'lisp-mnt) ;;; lisp-mnt.el ends here commit 4a973ed2bfb1da91a457a49a3a4089589fdf2d5f Author: Basil L. Contovounesios Date: Sun Aug 6 20:10:16 2023 +0200 ; Pacify new nadvice-tests byte-compiler warnings. diff --git a/test/lisp/emacs-lisp/nadvice-tests.el b/test/lisp/emacs-lisp/nadvice-tests.el index f6bd5733ba3..7dfa936214a 100644 --- a/test/lisp/emacs-lisp/nadvice-tests.el +++ b/test/lisp/emacs-lisp/nadvice-tests.el @@ -65,8 +65,9 @@ advice-tests-advice (defun sm-test2 (x) (+ x 4)) (declare-function sm-test2 nil) (should (equal (sm-test2 6) 10)) - (defadvice sm-test2 (around sm-test activate) - ad-do-it (setq ad-return-value (* ad-return-value 5))) + (with-suppressed-warnings ((obsolete defadvice)) + (defadvice sm-test2 (around sm-test activate) + ad-do-it (setq ad-return-value (* ad-return-value 5)))) (should (equal (sm-test2 6) 50)) (ad-deactivate 'sm-test2) (should (equal (sm-test2 6) 10)) @@ -81,8 +82,9 @@ advice-tests-advice (should (equal (sm-test2 6) 20)) (should (equal (null (get 'sm-test2 'defalias-fset-function)) t)) - (defadvice sm-test4 (around wrap-with-toto activate) - ad-do-it (setq ad-return-value `(toto ,ad-return-value))) + (with-suppressed-warnings ((obsolete defadvice)) + (defadvice sm-test4 (around wrap-with-toto activate) + ad-do-it (setq ad-return-value `(toto ,ad-return-value)))) (defmacro sm-test4 (x) `(call-test4 ,x)) (should (equal (macroexpand '(sm-test4 56)) '(toto (call-test4 56)))) (defmacro sm-test4 (x) `(call-testq ,x)) @@ -90,8 +92,9 @@ advice-tests-advice ;; This used to signal an error (bug#12858). (autoload 'sm-test6 "foo") - (defadvice sm-test6 (around test activate) - ad-do-it)) + (with-suppressed-warnings ((obsolete defadvice)) + (defadvice sm-test6 (around test activate) + ad-do-it))) (ert-deftest advice-tests-combination () "Combining old style and new style advices." @@ -100,8 +103,9 @@ advice-tests-combination (should (equal (sm-test5 6) 10)) (advice-add 'sm-test5 :around (lambda (f y) (* (funcall f y) 5))) (should (equal (sm-test5 6) 50)) - (defadvice sm-test5 (around test activate) - ad-do-it (setq ad-return-value (+ ad-return-value 0.1))) + (with-suppressed-warnings ((obsolete defadvice)) + (defadvice sm-test5 (around test activate) + ad-do-it (setq ad-return-value (+ ad-return-value 0.1)))) (should (equal (sm-test5 5) 45.1)) (ad-deactivate 'sm-test5) (should (equal (sm-test5 6) 50)) @@ -174,18 +178,20 @@ advice-test-call-interactively (ert-deftest advice-test-interactive () "Check handling of interactive spec." (defun sm-test8 (a) (interactive "p") a) - (defadvice sm-test8 (before adv1 activate) nil) - (defadvice sm-test8 (before adv2 activate) (interactive "P") nil) + (with-suppressed-warnings ((obsolete defadvice)) + (defadvice sm-test8 (before adv1 activate) nil) + (defadvice sm-test8 (before adv2 activate) (interactive "P") nil)) (should (equal (interactive-form 'sm-test8) '(interactive "P")))) (ert-deftest advice-test-preactivate () (should (equal (null (get 'sm-test9 'defalias-fset-function)) t)) (defun sm-test9 (a) (interactive "p") a) (should (equal (null (get 'sm-test9 'defalias-fset-function)) t)) - (defadvice sm-test9 (before adv1 pre act protect compile) nil) - (should (equal (null (get 'sm-test9 'defalias-fset-function)) nil)) - (defadvice sm-test9 (before adv2 pre act protect compile) - (interactive "P") nil) + (with-suppressed-warnings ((obsolete defadvice)) + (defadvice sm-test9 (before adv1 pre act protect compile) nil) + (should (equal (null (get 'sm-test9 'defalias-fset-function)) nil)) + (defadvice sm-test9 (before adv2 pre act protect compile) + (interactive "P") nil)) (should (equal (interactive-form 'sm-test9) '(interactive "P")))) (ert-deftest advice-test-multiples () commit 1cc20535f8730f49cd5d012313c1eaf0627d7216 Author: Paul Eggert Date: Sun Aug 6 09:08:56 2023 -0700 Stop using printf %n * src/emacs.c (shut_down_emacs): Don’t use printf’s "%n" format. Android, MS-Windows, and OpenBSD don’t support it, and it’s easy enough to do its equivalent by hand. diff --git a/src/emacs.c b/src/emacs.c index 80a013b68df..5a036554a87 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -2959,24 +2959,31 @@ shut_down_emacs (int sig, Lisp_Object stuff) reset_all_sys_modes (); if (sig && sig != SIGTERM) { - static char const fmt[] = "Fatal error %d: %n%s\n"; #ifdef HAVE_HAIKU if (haiku_debug_on_fatal_error) debugger ("Fatal error in Emacs"); #endif - char buf[max ((sizeof fmt - sizeof "%d%n%s\n" + /* Output a "Fatal error NUM: DESC\n" diagnostic with a single write, + but use multiple writes if the diagnosic is absurdly long + and likely couldn't be written atomically anyway. */ + static char const fmt[] = "Fatal error %d: "; + char buf[max ((sizeof fmt - sizeof "%d" + INT_STRLEN_BOUND (int) + 1), min (PIPE_BUF, MAX_ALLOCA))]; char const *sig_desc = safe_strsignal (sig); - int nlen; - int buflen = snprintf (buf, sizeof buf, fmt, sig, &nlen, sig_desc); - if (0 <= buflen && buflen < sizeof buf) - emacs_write (STDERR_FILENO, buf, buflen); + size_t sig_desclen = strlen (sig_desc); + int nlen = sprintf (buf, fmt, sig); + if (nlen + sig_desclen < sizeof buf - 1) + { + char *p = mempcpy (buf + nlen, sig_desc, sig_desclen); + *p++ = '\n'; + emacs_write (STDERR_FILENO, buf, p - buf); + } else { emacs_write (STDERR_FILENO, buf, nlen); - emacs_write (STDERR_FILENO, sig_desc, strlen (sig_desc)); - emacs_write (STDERR_FILENO, fmt + sizeof fmt - 2, 1); + emacs_write (STDERR_FILENO, sig_desc, sig_desclen); + emacs_write (STDERR_FILENO, "\n", 1); } } } commit 400df210ce0cc1ee0113b14a5ad92764d148c620 Author: Eli Zaretskii Date: Sun Aug 6 17:03:26 2023 +0300 Fix last change of 'delete-file' * src/fileio.c (Fdelete_file_internal): Expand file name here, as all primitives must. (internal_delete_file): Adjust to the fact that Fdelete_file was renamed. * lisp/files.el (delete-file): Don't expand-file-name here, as the called primitives already do. Fix typo in doc string. diff --git a/lisp/files.el b/lisp/files.el index 84a8c308b09..cc6e860319e 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -6354,7 +6354,7 @@ files--force (defun delete-file (filename &optional trash) "Delete file named FILENAME. If it is a symlink, remove the symlink. -If file has multiple names, it continues to exist with the other names.q +If file has multiple names, it continues to exist with the other names. TRASH non-nil means to trash the file instead of deleting, provided `delete-by-moving-to-trash' is non-nil. @@ -6367,7 +6367,7 @@ delete-file (null current-prefix-arg))) (if (and (file-directory-p filename) (not (file-symlink-p filename))) (signal 'file-error (list "Removing old name: is a directory" filename))) - (let* ((filename (expand-file-name filename)) (handler (find-file-name-handler filename 'delete-file))) + (let* ((handler (find-file-name-handler filename 'delete-file))) (cond (handler (funcall handler 'delete-file filename trash)) ((and delete-by-moving-to-trash trash) (move-file-to-trash filename)) (t (delete-file-internal filename))))) diff --git a/src/fileio.c b/src/fileio.c index e49a4a3836b..18879aa8fa3 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -2463,12 +2463,14 @@ DEFUN ("delete-directory-internal", Fdelete_directory_internal, } DEFUN ("delete-file-internal", Fdelete_file_internal, Sdelete_file_internal, 1, 1, 0, - doc: /* Delete file named FILENAME. If it is a symlink, remove the symlink. + doc: /* Delete file named FILENAME; internal use only. +If it is a symlink, remove the symlink. If file has multiple names, it continues to exist with the other names. */) (Lisp_Object filename) { Lisp_Object encoded_file; + filename = Fexpand_file_name (filename, Qnil); encoded_file = ENCODE_FILE (filename); if (unlink (SSDATA (encoded_file)) != 0 && errno != ENOENT) @@ -2492,7 +2494,7 @@ internal_delete_file (Lisp_Object filename) { Lisp_Object tem; - tem = internal_condition_case_2 (Fdelete_file, filename, Qnil, + tem = internal_condition_case_2 (Fdelete_file_internal, filename, Qt, internal_delete_file_1); return NILP (tem); } commit 563df3218e91816b2a4cb442220db12ca9598a03 Author: Po Lu Date: Sun Aug 6 21:53:44 2023 +0800 ; ChangeLog.android: update. diff --git a/ChangeLog.android b/ChangeLog.android index 82ab75b40c1..45d245336ec 100644 --- a/ChangeLog.android +++ b/ChangeLog.android @@ -1,5 +1,8 @@ 2023-08-06 Po Lu + * java/org/gnu/emacs/EmacsService.java (readDirectoryEntry): Fix + potential NULL dereference. + * java/org/gnu/emacs/EmacsSafThread.java (openDocument1): If initially opening with rwt, verify the file descriptor is really writable; if not, resort to rw and truncating the file descriptor commit e4f3a96709052731cc75de19cf7cab94d892bcea Author: Po Lu Date: Sun Aug 6 21:53:13 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsService.java (readDirectoryEntry): Fix potential NULL dereference. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 036bc9cf098..d91d8f66009 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1522,7 +1522,8 @@ In addition, arbitrary runtime exceptions (such as return entry; } - if (type.equals (Document.MIME_TYPE_DIR)) + if (type != null + && type.equals (Document.MIME_TYPE_DIR)) entry.d_type = 1; entry.d_name = name; return entry; commit 7ffc5f86e4636f9837a46ea75bee7475caaf5c04 Merge: 7873369338e 10a7615b5d4 Author: Po Lu Date: Sun Aug 6 21:45:44 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 7873369338ee0159ca285153fd4592cbcff65d7a Author: Po Lu Date: Sun Aug 6 21:45:29 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsNative.java: Declare ftruncate. * java/org/gnu/emacs/EmacsSafThread.java (openDocument1): If initially opening with rwt, verify the file descriptor is really writable; if not, resort to rw and truncating the file descriptor by hand instead. * src/androidvfs.c (NATIVE_NAME (ftruncate)): New function. Truncate file descriptor and return whether that was successful. diff --git a/ChangeLog.android b/ChangeLog.android index 689482d2f1a..82ab75b40c1 100644 --- a/ChangeLog.android +++ b/ChangeLog.android @@ -1,11 +1,20 @@ 2023-08-06 Po Lu + * java/org/gnu/emacs/EmacsSafThread.java (openDocument1): If + initially opening with rwt, verify the file descriptor is really + writable; if not, resort to rw and truncating the file descriptor + by hand instead. + + * src/androidvfs.c (NATIVE_NAME (ftruncate)): New function. + Truncate file descriptor and return whether that was successful. + * src/androidvfs.c (android_saf_tree_chmod): Repair file access permissions allowed within FLAGS. 2023-08-05 Po Lu * doc/lispref/commands.texi (Touchscreen Events): Fix typo. + * lisp/subr.el (y-or-n-p): Don't call set-text-conversion-style when not present. diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 7d72a9f192e..fae0ba98f86 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -274,6 +274,10 @@ public static native void blitRect (Bitmap src, Bitmap dest, int x1, operations. */ public static native void safPostRequest (); + /* Detect and return FD is writable. FD may be truncated to 0 bytes + in the process. */ + public static native boolean ftruncate (int fd); + static { /* Older versions of Android cannot link correctly with shared diff --git a/java/org/gnu/emacs/EmacsSafThread.java b/java/org/gnu/emacs/EmacsSafThread.java index 29cd3fa6bc7..3ae3c0839ce 100644 --- a/java/org/gnu/emacs/EmacsSafThread.java +++ b/java/org/gnu/emacs/EmacsSafThread.java @@ -24,6 +24,7 @@ import java.util.Iterator; import java.io.FileNotFoundException; +import java.io.IOException; import android.content.ContentResolver; import android.database.Cursor; @@ -1597,6 +1598,42 @@ In addition, arbitrary runtime exceptions (such as = resolver.openFileDescriptor (documentUri, mode, signal); + /* If a writable file descriptor is requested and TRUNCATE is set, + then probe the file descriptor to detect if it is actually + readable. If not, close this file descriptor and reopen it + with MODE set to rw; some document providers granting access to + Samba shares don't implement rwt, but these document providers + invariably truncate the file opened even when the mode is + merely rw. + + This may be ascribed to a mix-up in Android's documentation + regardin DocumentsProvider: the `openDocument' function is only + documented to accept r or rw, whereas the default + implementation of the `openFile' function (which documents rwt) + delegates to `openDocument'. */ + + if (write && truncate && fileDescriptor != null + && !EmacsNative.ftruncate (fileDescriptor.getFd ())) + { + try + { + fileDescriptor.closeWithError ("File descriptor requested" + + " is not writable"); + } + catch (IOException e) + { + Log.w (TAG, "Leaking unclosed file descriptor " + e); + } + + fileDescriptor + = resolver.openFileDescriptor (documentUri, "rw", signal); + + /* Try to truncate fileDescriptor just to stay on the safe + side. */ + if (fileDescriptor != null) + EmacsNative.ftruncate (fileDescriptor.getFd ()); + } + /* Every time a document is opened, remove it from the file status cache. */ toplevel = getCache (treeUri); diff --git a/src/androidvfs.c b/src/androidvfs.c index dc5097f463e..d6daff481b0 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -5605,7 +5605,7 @@ android_saf_file_open (struct android_vnode *vnode, int flags, /* Open a parcel file descriptor according to flags. */ method = service_class.open_document; - trunc = flags & O_TRUNC; + trunc = (flags & O_TRUNC); write = ((flags & O_RDWR) == O_RDWR || (flags & O_WRONLY)); inside_saf_critical_section = true; descriptor @@ -6121,6 +6121,12 @@ NATIVE_NAME (safPostRequest) (JNIEnv *env, jobject object) sem_post (&saf_completion_sem); } +JNIEXPORT jboolean JNICALL +NATIVE_NAME (ftruncate) (JNIEnv *env, jobject object, jint fd) +{ + return ftruncate (fd, 0) != -1; +} + #ifdef __clang__ #pragma clang diagnostic pop #else /* GNUC */ commit 10a7615b5d45bcd909bb03d67423b337dfe93b1e Author: Eric S. Raymond Date: Sun Aug 6 07:00:22 2023 -0400 Separate filename-deletion mechanism from policy. src/fileio.c: (delete-file-internal) Renamed from delete-file, parallel to delete-directory-internal; policy code moved to Lisp. src/files.el: (delete-file) New function, holds policy logic. calls delete-file-internal. This is a pure refactoring step, delete-file's behavior is unchanged. But the C core is a little simpler now. diff --git a/lisp/files.el b/lisp/files.el index f8867432000..84a8c308b09 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -6352,6 +6352,26 @@ files--force (apply fn args) (file-missing (or no-such (signal (car err) (cdr err)))))) +(defun delete-file (filename &optional trash) + "Delete file named FILENAME. If it is a symlink, remove the symlink. +If file has multiple names, it continues to exist with the other names.q +TRASH non-nil means to trash the file instead of deleting, provided +`delete-by-moving-to-trash' is non-nil. + +When called interactively, TRASH is t if no prefix argument is given. +With a prefix argument, TRASH is nil." + (interactive (list (read-file-name + (if (and delete-by-moving-to-trash (null current-prefix-arg)) + "Move file to trash: " "Delete file: ") + nil default-directory (confirm-nonexistent-file-or-buffer)) + (null current-prefix-arg))) + (if (and (file-directory-p filename) (not (file-symlink-p filename))) + (signal 'file-error (list "Removing old name: is a directory" filename))) + (let* ((filename (expand-file-name filename)) (handler (find-file-name-handler filename 'delete-file))) + (cond (handler (funcall handler 'delete-file filename trash)) + ((and delete-by-moving-to-trash trash) (move-file-to-trash filename)) + (t (delete-file-internal filename))))) + (defun delete-directory (directory &optional recursive trash) "Delete the directory named DIRECTORY. Does not follow symlinks. If RECURSIVE is non-nil, delete files in DIRECTORY as well, with diff --git a/src/fileio.c b/src/fileio.c index 63f4e698528..e49a4a3836b 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -2462,38 +2462,13 @@ DEFUN ("delete-directory-internal", Fdelete_directory_internal, return Qnil; } -DEFUN ("delete-file", Fdelete_file, Sdelete_file, 1, 2, - "(list (read-file-name \ - (if (and delete-by-moving-to-trash (null current-prefix-arg)) \ - \"Move file to trash: \" \"Delete file: \") \ - nil default-directory (confirm-nonexistent-file-or-buffer)) \ - (null current-prefix-arg))", +DEFUN ("delete-file-internal", Fdelete_file_internal, Sdelete_file_internal, 1, 1, 0, doc: /* Delete file named FILENAME. If it is a symlink, remove the symlink. -If file has multiple names, it continues to exist with the other names. -TRASH non-nil means to trash the file instead of deleting, provided -`delete-by-moving-to-trash' is non-nil. - -When called interactively, TRASH is t if no prefix argument is given. -With a prefix argument, TRASH is nil. */) - (Lisp_Object filename, Lisp_Object trash) +If file has multiple names, it continues to exist with the other names. */) + (Lisp_Object filename) { - Lisp_Object handler; Lisp_Object encoded_file; - if (!NILP (Ffile_directory_p (filename)) - && NILP (Ffile_symlink_p (filename))) - xsignal2 (Qfile_error, - build_string ("Removing old name: is a directory"), - filename); - filename = Fexpand_file_name (filename, Qnil); - - handler = Ffind_file_name_handler (filename, Qdelete_file); - if (!NILP (handler)) - return call3 (handler, Qdelete_file, filename, trash); - - if (delete_by_moving_to_trash && !NILP (trash)) - return call1 (Qmove_file_to_trash, filename); - encoded_file = ENCODE_FILE (filename); if (unlink (SSDATA (encoded_file)) != 0 && errno != ENOENT) @@ -2725,7 +2700,7 @@ DEFUN ("rename-file", Frename_file, Srename_file, 2, 3, if (dirp) call2 (Qdelete_directory, file, Qt); else - Fdelete_file (file, Qnil); + call2 (Qdelete_file, file, Qnil); return unbind_to (count, Qnil); } @@ -6346,7 +6321,7 @@ syms_of_fileio (void) DEFSYM (Qcopy_file, "copy-file"); DEFSYM (Qmake_directory_internal, "make-directory-internal"); DEFSYM (Qmake_directory, "make-directory"); - DEFSYM (Qdelete_file, "delete-file"); + DEFSYM (Qdelete_file_internal, "delete-file-internal"); DEFSYM (Qfile_name_case_insensitive_p, "file-name-case-insensitive-p"); DEFSYM (Qrename_file, "rename-file"); DEFSYM (Qadd_name_to_file, "add-name-to-file"); @@ -6610,6 +6585,9 @@ do (file-exists-p FILENAME) and FILENAME is handled by HANDLER, then delete_by_moving_to_trash = 0; DEFSYM (Qdelete_by_moving_to_trash, "delete-by-moving-to-trash"); + /* Lisp function for interactive file delete with trashing */ + DEFSYM (Qdelete_file, "delete-file"); + /* Lisp function for moving files to trash. */ DEFSYM (Qmove_file_to_trash, "move-file-to-trash"); @@ -6641,7 +6619,7 @@ do (file-exists-p FILENAME) and FILENAME is handled by HANDLER, then defsubr (&Scopy_file); defsubr (&Smake_directory_internal); defsubr (&Sdelete_directory_internal); - defsubr (&Sdelete_file); + defsubr (&Sdelete_file_internal); defsubr (&Sfile_name_case_insensitive_p); defsubr (&Srename_file); defsubr (&Sadd_name_to_file); commit 2924541b8f17e31eea58b231d1af7c0c8844deff Author: Michael Albinus Date: Sun Aug 6 13:50:11 2023 +0200 ; Fix last patch diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index a92f047a42e..a6c430fb1c0 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -7409,12 +7409,12 @@ tramp-test41-special-characters (cond ((or (tramp--test-ange-ftp-p) (tramp--test-container-p) (tramp--test-gvfs-p) + (tramp--test-openbsd-p) (tramp--test-rclone-p) (tramp--test-sudoedit-p) (tramp--test-windows-nt-or-smb-p)) "foo bar baz") ((or (tramp--test-adb-p) - (tramp--test-openbsd-p) (eq system-type 'cygwin)) " foo bar baz ") ((tramp--test-sh-no-ls--dired-p) commit 2c04adca4d3c123cc29b4150758a17c23101290c Author: Stefan Kangas Date: Sun Aug 6 12:14:40 2023 +0200 ; Silence byte-compiler * test/src/treesit-tests.el (treesit-pattern-expand): Declare. diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el index 34540b463cd..65994ce608f 100644 --- a/test/src/treesit-tests.el +++ b/test/src/treesit-tests.el @@ -34,6 +34,7 @@ (declare-function treesit-parser-buffer "treesit.c") (declare-function treesit-parser-language "treesit.c") +(declare-function treesit-pattern-expand "treesit.c") (declare-function treesit-query-expand "treesit.c") (declare-function treesit-query-compile "treesit.c") (declare-function treesit-query-capture "treesit.c") commit 75fa36635d8a090933123d923dd9f76261bcd2af Merge: 5f24ca55538 3135007bf16 Author: Michael Albinus Date: Sun Aug 6 11:44:43 2023 +0200 Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs commit 5f24ca555383e039d93a852d239d5f2c8781d6c8 Author: Michael Albinus Date: Sun Aug 6 11:43:48 2023 +0200 Suppress some Tramp tests for OpenBSD * test/lisp/net/tramp-tests.el (tramp--test-openbsd-p): New defun. (tramp-test41-special-characters, tramp-test42-utf8): Use it. (Bug#64935) diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index d97785ee481..a92f047a42e 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -7083,6 +7083,12 @@ tramp--test-mock-p (string-equal "mock" (file-remote-p ert-remote-temporary-file-directory 'method))) +(defun tramp--test-openbsd-p () + "Check, whether the remote host runs OpenBSD." + ;; We must refill the cache. `file-truename' does it. + (file-truename ert-remote-temporary-file-directory) + (ignore-errors (tramp-check-remote-uname tramp-test-vec "OpenBSD"))) + (defun tramp--test-out-of-band-p () "Check, whether an out-of-band method is used." (tramp-method-out-of-band-p tramp-test-vec 1)) @@ -7408,6 +7414,7 @@ tramp-test41-special-characters (tramp--test-windows-nt-or-smb-p)) "foo bar baz") ((or (tramp--test-adb-p) + (tramp--test-openbsd-p) (eq system-type 'cygwin)) " foo bar baz ") ((tramp--test-sh-no-ls--dired-p) @@ -7485,7 +7492,8 @@ tramp-test42-utf8 "Автостопом по гала́ктике" ;; Use codepoints without a name. See Bug#31272. ;; Works on some Android systems only. - (unless (tramp--test-adb-p) "™›šbung") + (unless (or (tramp--test-adb-p) (tramp--test-openbsd-p)) + "™›šbung") ;; Use codepoints from Supplementary Multilingual Plane (U+10000 ;; to U+1FFFF). "🌈🍒👋") commit 3135007bf16b67943eb0c9ba72b06356c9424f31 Author: Stefan Kangas Date: Sun Aug 6 11:35:02 2023 +0200 Mark Emacs 20 color support compat aliases obsolete * lisp/faces.el (x-defined-colors, x-color-defined-p) (x-color-values, x-display-color-p): Make Emacs 20 compat aliases obsolete. Update one caller to use the new names. * doc/lispref/frames.texi (Color Names): Do not document above obsolete aliases. diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 368def90d85..42f1a42c73b 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -4461,20 +4461,12 @@ Color Names color on any kind of display, and you will get some result---typically, the closest it can do. To determine whether a frame can really display a certain color, use @code{color-supported-p} (see below). - -@findex x-color-defined-p -This function used to be called @code{x-color-defined-p}, -and that name is still supported as an alias. @end defun @defun defined-colors &optional frame This function returns a list of the color names that are defined and supported on frame @var{frame} (default, the selected frame). If @var{frame} does not support colors, the value is @code{nil}. - -@findex x-defined-colors -This function used to be called @code{x-defined-colors}, -and that name is still supported as an alias. @end defun @defun color-supported-p color &optional frame background-p @@ -4526,10 +4518,6 @@ Color Names @var{frame} is omitted or @code{nil}, the information is returned for the selected frame's display. If the frame cannot display colors, the value is @code{nil}. - -@findex x-color-values -This function used to be called @code{x-color-values}, -and that name is still supported as an alias. @end defun @defun color-name-to-rgb color &optional frame @@ -4714,10 +4702,7 @@ Display Feature Testing @end defun @defun display-color-p &optional display -@findex x-display-color-p This function returns @code{t} if the screen is a color screen. -It used to be called @code{x-display-color-p}, and that name -is still supported as an alias. @end defun @defun display-grayscale-p &optional display diff --git a/etc/NEWS b/etc/NEWS index 0dd663edcc6..9cd672dd1b9 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -701,6 +701,11 @@ Instead, the Lisp reader approximates an infinity with the nearest finite value, and a NaN with some other non-numeric object that provokes an error if used numerically. ++++ +** X color support compatibility aliases are now marked obsolete. +The compatibility aliases 'x-defined-colors', 'x-color-defined-p', +'x-color-values', and 'x-display-color-p' are now obsolete. + * Lisp Changes in Emacs 30.1 diff --git a/lisp/emulation/viper-cmd.el b/lisp/emulation/viper-cmd.el index c0aa9dd7b46..78114db0972 100644 --- a/lisp/emulation/viper-cmd.el +++ b/lisp/emulation/viper-cmd.el @@ -4724,15 +4724,15 @@ reporter-prompt-for-summary-p (defun viper-submit-report () "Submit bug report on Viper." (interactive) - (defvar x-display-color-p) + (defvar display-color-p) (defvar viper-frame-parameters) (defvar viper-minibuffer-emacs-face) (defvar viper-minibuffer-vi-face) (defvar viper-minibuffer-insert-face) (let ((reporter-prompt-for-summary-p t) - (x-display-color-p (if (viper-window-display-p) - (x-display-color-p) - 'non-x)) + (display-color-p (if (viper-window-display-p) + (display-color-p) + 'non-x)) (viper-frame-parameters (frame-parameters (selected-frame))) (viper-minibuffer-emacs-face (if (viper-has-face-support-p) (facep @@ -4790,7 +4790,7 @@ viper-submit-report 'viper-expert-level 'major-mode 'window-system - 'x-display-color-p + 'display-color-p 'viper-frame-parameters 'viper-minibuffer-vi-face 'viper-minibuffer-insert-face diff --git a/lisp/faces.el b/lisp/faces.el index dad16966f48..3ad6fe59557 100644 --- a/lisp/faces.el +++ b/lisp/faces.el @@ -1850,7 +1850,6 @@ face-user-default-spec ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Frame-type independent color support. -;;; We keep the old x-* names as aliases for back-compatibility. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun defined-colors (&optional frame) @@ -1862,7 +1861,6 @@ defined-colors (if (display-graphic-p frame) (xw-defined-colors frame) (mapcar 'car (tty-color-alist frame)))) -(defalias 'x-defined-colors 'defined-colors) (defun defined-colors-with-face-attributes (&optional frame foreground) "Return a list of colors supported for a particular FRAME. @@ -1946,7 +1944,6 @@ color-defined-p (if (display-graphic-p frame) (xw-color-defined-p color frame) (numberp (tty-color-translate color frame))))) -(defalias 'x-color-defined-p 'color-defined-p) (declare-function xw-color-values "xfns.c" (color &optional frame)) @@ -1974,8 +1971,6 @@ color-values (t (tty-color-values color frame)))) -(defalias 'x-color-values 'color-values) - (declare-function xw-display-color-p "xfns.c" (&optional terminal)) (defun display-color-p (&optional display) @@ -1986,7 +1981,6 @@ display-color-p (if (display-graphic-p display) (xw-display-color-p display) (tty-display-color-p display))) -(defalias 'x-display-color-p 'display-color-p) (declare-function x-display-grayscale-p "xfns.c" (&optional terminal)) @@ -3221,6 +3215,10 @@ x-resolve-font-name (define-obsolete-function-alias 'face-background-pixmap #'face-stipple "29.1") (define-obsolete-function-alias 'set-face-background-pixmap #'set-face-stipple "29.1") +(define-obsolete-function-alias 'x-defined-colors #'defined-colors "30.1") +(define-obsolete-function-alias 'x-color-defined-p #'color-defined-p "30.1") +(define-obsolete-function-alias 'x-color-values #'color-values "30.1") +(define-obsolete-function-alias 'x-display-color-p #'display-color-p "30.1") (provide 'faces) commit 67650c9c11a0afe33e2a27a8669b6925a6606212 Author: Stefan Kangas Date: Sun Aug 6 11:19:27 2023 +0200 Mark unused macro defun-gmm obsolete * lisp/gnus/gmm-utils.el (defun-gmm): Mark as obsolete. diff --git a/lisp/gnus/gmm-utils.el b/lisp/gnus/gmm-utils.el index 2fc89681ba5..c0651e3dbfc 100644 --- a/lisp/gnus/gmm-utils.el +++ b/lisp/gnus/gmm-utils.el @@ -200,7 +200,8 @@ defun-gmm "Create function NAME. If FUNCTION exists, then NAME becomes an alias for FUNCTION. Otherwise, create function NAME with ARG-LIST and BODY." - (declare (indent defun)) + (declare (obsolete nil "30.1") + (indent defun)) (let ((defined-p (fboundp function))) (if defined-p `(defalias ',name ',function) commit 20df82fd43aa2d02a9613a073e1bc27647b20576 Author: Eli Zaretskii Date: Sun Aug 6 12:10:37 2023 +0300 ; * admin/notes/unicode (char-width-table): Update instructions. diff --git a/admin/notes/unicode b/admin/notes/unicode index 31c850af8fd..b4f23f68def 100644 --- a/admin/notes/unicode +++ b/admin/notes/unicode @@ -72,7 +72,10 @@ characters are those marked with W or F in that file. Zero-width characters are not taken from EastAsianWidth.txt, they are those whose Unicode General Category property is one of Mn, Me, or Cf, and also Hangul jungseong and jongseong characters (a.k.a. "Jamo medial vowels" -and "Jamo final consonants"). +and "Jamo final consonants"). The list of "ambiguous-width +characters" recorded in the ambiguous-width-chars table (around line +1400 of characters.el) should also be checked against the latest +Unicode data in EastAsianWidth.txt. Any new scripts added by UnicodeData.txt will also need updates to script-representative-chars defined in fontset.el, and also the list commit 669a4b96c374801faa137f43b4497b2ed6511104 Author: Po Lu Date: Sun Aug 6 11:46:15 2023 +0800 Update Android port * src/androidvfs.c (android_saf_tree_chmod): Repair file access permissions allowed within FLAGS. diff --git a/ChangeLog.android b/ChangeLog.android index d973dbcaf18..689482d2f1a 100644 --- a/ChangeLog.android +++ b/ChangeLog.android @@ -1,3 +1,8 @@ +2023-08-06 Po Lu + + * src/androidvfs.c (android_saf_tree_chmod): Repair file access + permissions allowed within FLAGS. + 2023-08-05 Po Lu * doc/lispref/commands.texi (Touchscreen Events): Fix typo. diff --git a/src/androidvfs.c b/src/androidvfs.c index 0d99116c75c..dc5097f463e 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -5101,17 +5101,17 @@ android_saf_tree_mkdir (struct android_vnode *vnode, mode_t mode) android_saf_tree_chmod (struct android_vnode *vnode, mode_t mode, int flags) { - /* Return EACCESS should MODE contain unusual bits besides S_IFDIR | - S_IRUSR | S_IXUSR. */ + /* Return EACCESS should MODE contain unusual bits besides the + standard file access permissions. */ - if (mode & ~(S_IFDIR | S_IRUSR | S_IXUSR)) + if (mode & ~0777) { errno = EACCES; return -1; } /* Otherwise, no further action is necessary, as SAF nodes already - pretend to be S_IFDIR | S_IRUSR | S_IXUSR. */ + pretend to be S_IRUSR | S_IWUSR. */ return 0; } commit 2867f624847c5dbb60544462212611b5e0eb1ed8 Merge: 937b6e6e8b8 1bf92d91950 Author: Po Lu Date: Sun Aug 6 07:38:10 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 937b6e6e8b8460c868d3e0692588c04ccedfd2e7 Author: Po Lu Date: Sat Aug 5 17:17:30 2023 +0800 ; ChangeLog.android: Update. diff --git a/ChangeLog.android b/ChangeLog.android index 4fad3d872cf..d973dbcaf18 100644 --- a/ChangeLog.android +++ b/ChangeLog.android @@ -1,3 +1,9 @@ +2023-08-05 Po Lu + + * doc/lispref/commands.texi (Touchscreen Events): Fix typo. + * lisp/subr.el (y-or-n-p): Don't call set-text-conversion-style + when not present. + 2023-08-04 Po Lu * ChangeLog.android: New file. commit 3e194e50ca136925074f721bb9ce6d290cab68dc Author: Po Lu Date: Sat Aug 5 17:17:12 2023 +0800 * doc/lispref/commands.texi (Touchscreen Events): Fix typo. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 56d7b8c649b..2991799e668 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2065,7 +2065,7 @@ Touchscreen Events (@pxref{Motion Events}), and a final @code{touchscreen-end} event translated to a @code{mouse-1} or @code{drag-mouse-1} event (unless the @code{touchscreen-end} event indicates that the touch sequence has -been intercepted by another program.) This is referred to ``simple +been intercepted by another program.) This is dubbed ``simple translation'', and produces a simple correspondence between touchpoint motion and mouse motion. commit 8a955727cff48616cc420a8568a0b4ff76cbbea7 Author: Po Lu Date: Sat Aug 5 17:16:16 2023 +0800 ; Update Android port * lisp/subr.el (y-or-n-p): Don't call set-text-conversion-style when not present. diff --git a/lisp/subr.el b/lisp/subr.el index 3e4808c5471..58ec642dd92 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3799,7 +3799,8 @@ y-or-n-p ;; If the minibuffer is already active, the ;; selected window might not change. Disable ;; text conversion by hand. - (set-text-conversion-style text-conversion-style) + (when (fboundp 'set-text-conversion-style) + (set-text-conversion-style text-conversion-style)) (read-from-minibuffer prompt nil keymap nil (or y-or-n-p-history-variable t))))) commit a5bbc51f474568ad2e4511a08614ac8ec4ca2481 Merge: e17cd1d8a10 60e5f212182 Author: Po Lu Date: Sat Aug 5 09:06:04 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit e17cd1d8a10729f40b028d94244e7bc3dfca0541 Author: Po Lu Date: Sat Aug 5 09:05:47 2023 +0800 ; * nt/mingw-cfg.site: Restore the MS Windows build. diff --git a/nt/mingw-cfg.site b/nt/mingw-cfg.site index 68e264fde4c..f78ee525bf1 100644 --- a/nt/mingw-cfg.site +++ b/nt/mingw-cfg.site @@ -175,6 +175,7 @@ gl_cv_func_nanosleep=yes enable_xattr=no # Don't build gnulib printf either. gl_cv_func_printf_sizes_c99=yes +gl_cv_func_printf_sizes_c23=yes gl_cv_func_printf_long_double=yes gl_cv_func_printf_infinite_long_double=yes gl_cv_func_printf_directive_a=yes commit defee1c948bc89f9743ebb072d31fc2ec324f8fc Author: Po Lu Date: Fri Aug 4 17:56:31 2023 +0800 Try to restore the MS Windows build * nt/mingw-cfg.site: Impede building Gnulib's vasnprintf* code. diff --git a/nt/mingw-cfg.site b/nt/mingw-cfg.site index e8b4711f548..68e264fde4c 100644 --- a/nt/mingw-cfg.site +++ b/nt/mingw-cfg.site @@ -178,9 +178,11 @@ gl_cv_func_printf_sizes_c99=yes gl_cv_func_printf_long_double=yes gl_cv_func_printf_infinite_long_double=yes gl_cv_func_printf_directive_a=yes +gl_cv_func_printf_directive_b=yes gl_cv_func_printf_directive_f=yes gl_cv_func_printf_directive_n=yes gl_cv_func_printf_directive_ls=yes +gl_cv_func_printf_directive_lc=yes gl_cv_func_printf_positions=yes gl_cv_func_printf_flag_grouping=yes gl_cv_func_printf_flag_leftadjust=yes commit 5ca25a13185d12c9053c4f8c6df4c57846f26e17 Author: Po Lu Date: Fri Aug 4 17:41:20 2023 +0800 ; Last-minute updates to Android port * lisp/electric.el: * lisp/help-macro.el (make-help-screen): * lisp/subr.el (read-char-choice-with-read-key): * src/buffer.c (init_buffer_once): * src/dispextern.h (No_Cursor): * src/keyboard.c (read_char): * src/process.c (Fprocess_send_eof): Fix commentary or unnecessary whitespace changes. diff --git a/lisp/electric.el b/lisp/electric.el index 8b379ed16e2..cef5326852c 100644 --- a/lisp/electric.el +++ b/lisp/electric.el @@ -294,7 +294,6 @@ electric-indent-just-newline ;;;###autoload (define-key global-map "\C-j" 'electric-newline-and-maybe-indent) - ;;;###autoload (defun electric-newline-and-maybe-indent () "Insert a newline. diff --git a/lisp/help-macro.el b/lisp/help-macro.el index 0905d1946f3..fac3419f184 100644 --- a/lisp/help-macro.el +++ b/lisp/help-macro.el @@ -166,7 +166,7 @@ make-help-screen (error nil)) (let ((cursor-in-echo-area t) (overriding-local-map local-map)) - (frame-toggle-on-screen-keyboard nil nil) + (frame-toggle-on-screen-keyboard (selected-frame) nil) (setq key (read-key-sequence (format "Type one of listed options%s: " (if (pos-visible-in-window-p diff --git a/lisp/subr.el b/lisp/subr.el index 89278241777..3e4808c5471 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3348,7 +3348,7 @@ read-char-choice-with-read-key (unless (get-text-property 0 'face prompt) (setq prompt (propertize prompt 'face 'minibuffer-prompt))) ;; Display the on screen keyboard if it exists. - (frame-toggle-on-screen-keyboard nil t) + (frame-toggle-on-screen-keyboard (selected-frame) nil) (setq char (let ((inhibit-quit inhibit-keyboard-quit)) (read-key prompt))) (and show-help (buffer-live-p (get-buffer helpbuf)) diff --git a/src/buffer.c b/src/buffer.c index 9bcace37e9b..9facc4b7ab8 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -4791,9 +4791,7 @@ init_buffer_once (void) #ifdef HAVE_TREE_SITTER bset_ts_parser_list (&buffer_defaults, Qnil); #endif -#ifdef HAVE_TEXT_CONVERSION bset_text_conversion_style (&buffer_defaults, Qnil); -#endif bset_cursor_in_non_selected_windows (&buffer_defaults, Qt); bset_enable_multibyte_characters (&buffer_defaults, Qt); diff --git a/src/dispextern.h b/src/dispextern.h index cf67121809f..3a4d6095f73 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -57,7 +57,6 @@ #define No_Cursor (None) /* Accommodate X's usage of None as a null resource ID. */ #define No_Cursor (NULL) #else -/* Android doesn't support cursors and also uses handles. */ #define No_Cursor 0 #endif diff --git a/src/keyboard.c b/src/keyboard.c index 445f39ea140..78c88a2859b 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -3053,9 +3053,6 @@ read_char (int commandflag, Lisp_Object map, struct buffer *prev_buffer = current_buffer; last_input_event = c; - /* All a `text-conversion' event does is prevent Emacs from - staying idle. It is not useful. */ - call4 (Qcommand_execute, tem, Qnil, Fvector (1, &last_input_event), Qt); if (CONSP (c) && !NILP (Fmemq (XCAR (c), Vwhile_no_input_ignore_events)) diff --git a/src/process.c b/src/process.c index 90885ad318a..7738f1e4bcc 100644 --- a/src/process.c +++ b/src/process.c @@ -7252,7 +7252,7 @@ DEFUN ("process-send-eof", Fprocess_send_eof, Sprocess_send_eof, 0, 1, 0, #if !defined WINDOWSNT && defined HAVE_TCDRAIN if (tcdrain (XPROCESS (proc)->outfd) != 0) report_file_error ("Failed tcdrain", Qnil); -#endif /* not WINDOWSNT */ +#endif /* not WINDOWSNT && not TCDRAIN */ /* Do nothing on Windows because writes are blocking. */ } else commit 6bfff65778796d997813719a1c18e27b6bd49203 Author: Po Lu Date: Fri Aug 4 15:00:18 2023 +0800 ; Prepare Android port for inclusion in Emacs * ChangeLog.android: New file. diff --git a/ChangeLog.android b/ChangeLog.android new file mode 100644 index 00000000000..4fad3d872cf --- /dev/null +++ b/ChangeLog.android @@ -0,0 +1,7926 @@ +2023-08-04 Po Lu + + * ChangeLog.android: New file. + +2023-08-04 Po Lu + + Optimize creation of multibyte menu items on Android + * src/androidvfs.c (android_verify_jni_string): Move to + android.c. + * src/android.c (android_verify_jni_string): New function. + (android_build_string): Forgo encoding menu text if TEXT is a + multibyte string that's also a valid JNI string. + * src/android.h: Update prototypes. + + Avoid encoding commonplace characters in tree names + * java/org/gnu/emacs/EmacsService.java (getDocumentTrees): Don't + encode some characters that need not be escaped within file + names. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-08-03 Po Lu + + * src/fileio.c (check_vfs_filename): Revert earlier change. + + Merge remote-tracking branch 'origin/master' into feature/android + + Isolate fchmodat within the Android VFS layer + * src/android.h: Update prototypes. + * src/androidvfs.c (unix_vfs_ops, android_unix_chmod, afs_vfs_ops) + (android_afs_chmod, content_vfs_ops, android_content_chmod) + (authority_vfs_ops, android_authority_chmod, saf_root_vfs_ops) + (android_saf_root_chmod, saf_tree_vfs_ops, android_saf_tree_chmod) + (saf_file_vfs_ops, saf_new_vfs_ops, android_saf_new_chmod) + (root_vfs_ops): Add `chmod' to the list of functions implemented + by each vnode. + (android_fchmodat): New function. + * src/fileio.c (Fset_file_modes): Use `emacs_fchmodat'. + * src/lisp.h: + * src/sysdep.c (emacs_fchmodat): Delegate to android_fchmodat on + Android. + + Update Android port + * java/org/gnu/emacs/EmacsSafThread.java (CacheToplevel): + (EmacsSafThread): + (DocIdEntry): + (getCache): + (pruneCache): + (cacheDirectoryFromCursor): + (run): + (documentIdFromName1): + (statDocument1): + (openDocumentDirectory1): + (openDocument1): Introduce a file status cache and populate + it with files within directories as they are opened. + * java/org/gnu/emacs/EmacsService.java (createDocument): + (createDirectory): + (moveDocument): Invalidate the file status cache wherever + needed. + * src/fileio.c (check_vfs_filename): + (Fset_file_modes): Permit `set-file-modes' to silently fail + on asset and content files. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-08-02 Po Lu + + Fix reporting of key events containing SYM and META + * doc/emacs/android.texi (Android)::(What is Android?): + (Android Startup, Android File System, Android Environment) + (Android Windowing, Android Fonts, Android Troubleshooting): + Improve section titles. + (Android Windowing): Describe the relation between keyboard + modifiers reported by Android and those in key events. + * java/org/gnu/emacs/EmacsWindow.java (onKeyDown, onKeyUp): + Clear META_SYM_ON and META_META_MASK when retrieving ASCII + characters. + * src/androidgui.h: Add ANDROID_META_MASK. + * src/androidterm.c (android_android_to_emacs_modifiers) + (android_emacs_to_android_modifiers): Transform META to Alt, and + vice versa. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-08-01 Po Lu + + Update Android port + * doc/emacs/android.texi (Android File System): Describe how to + access real files named /assets or /contents if so required. + * java/org/gnu/emacs/EmacsService.java (validAuthority): + * src/android.c (android_init_emacs_service): + * src/android.h: New function. + * src/androidvfs.c (android_saf_valid_authority_p): New + function. Wrap the Java function. + (android_saf_root_stat, android_saf_root_access): Don't return + success if no authority by vp->authority's name exists. + (android_saf_tree_from_name): Check validity of string data + before giving it to JNI. + + Micro-optimize PUSHW/PUSHB + * src/sfnt.c (CHECK_STACK_AVAILABLE): New macro. + (PUSH): + (PUSH_UNCHECKED): Always define to unchecked versions, + even if TEST. + (PUSH2_UNCHECKED): New macro. + (NPUSHB): + (NPUSHW): + (PUSHB): + (PUSHW): Check the number of remaining stack elements + once. + (stack_overflow_test_args): Expect zero stack arguments. + + * src/android.c (ANDROID_THROW): Remove unused macro. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-31 Po Lu + + Update Android port + * src/sfnt.c (ISECT): Micro-optimize this instruction. + * src/sfntfont.c (sfnt_parse_style): Avoid GC safety problem. + + Update Android port + * src/sfntfont.c (sfnt_parse_style): Fix misworded commentary. + + Initialize Android API level earlier + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): + * java/org/gnu/emacs/EmacsNoninteractive.java (main): + * java/org/gnu/emacs/EmacsService.java (run): + * java/org/gnu/emacs/EmacsThread.java (run): + * src/android.c (initEmacs, setEmacsParams): Set + `android_api_level' within setEmacsParams, not in initEmacs. + * src/androidvfs.c: Pacify compiler warnings. + + Implement cross-directory SAF rename operations + * java/org/gnu/emacs/EmacsService.java (renameDocument): Don't + catch UnsupportedOperationException; handle ENOSYS in + android_saf_rename_document instead. + (moveDocument): New function. + * lisp/subr.el (y-or-n-p): Always change the text conversion + style. + * src/android.c (android_init_emacs_service) + (android_exception_check_4): New function. + * src/android.h: Update Java function table. + * src/androidvfs.c (android_saf_rename_document): Handle ENOSYS + here by setting errno to EXDEV. + (android_saf_move_document): New function. + (android_document_id_from_name): Take const `dir_name'. + (android_saf_tree_rename): Use delete-move-rename to implement + cross-directory renames. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-30 Po Lu + + Partially implement rename operations on SAF files + * java/org/gnu/emacs/EmacsSafThread.java + (postInvalidateCacheDir): + * java/org/gnu/emacs/EmacsService.java (renameDocument): New + functions. + * src/android.c (android_init_emacs_service): + * src/android.h (struct android_emacs_service): Link to new JNI + function. + * src/androidvfs.c (android_saf_rename_document): New function. + (android_saf_tree_rename): Implement in terms of that function + if possible. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-29 Po Lu + + Correct directory permissions reported for VFS files + * java/org/gnu/emacs/EmacsSafThread.java (statDocument1): + * src/androidvfs.c (android_afs_stat, android_content_stat) + (android_saf_root_stat): Report search permissions for files. + + Update Android port + * src/androidvfs.c: Improve commentary. + +2023-07-29 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsSafThread.java (postInvalidateCache): + New argument cacheName. Remove that file from the cache. + (accessDocument1): Consult the storage cache as well. + * java/org/gnu/emacs/EmacsService.java (deleteDocument): New + argument NAME. + + * src/android.c (android_init_emacs_service): Add new argument. + * src/androidvfs.c (android_saf_delete_document) + (android_saf_tree_rmdir, android_saf_file_unlink): Pass name of + file being deleted to `deleteDocument'. + +2023-07-29 Po Lu + + ; Update Android port + * src/androidvfs.c (android_saf_exception_check): Describe + exceptions earlier. + + Update Android port + * java/org/gnu/emacs/EmacsSafThread.java (DocIdEntry): + (getCacheEntry): + (CacheEntry): + (documentIdFromName1): Fix earlier change. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * java/org/gnu/emacs/EmacsSafThread.java (DocIdEntry) + (getCacheEntry, CacheEntry): Use `uptimeMillis' as the basis for + cache expiration. + + Update Android port + * java/org/gnu/emacs/EmacsSafThread.java (EmacsSafThread, getCache) + (pruneCache1, pruneCache, cacheChild, cacheDirectoryFromCursor) + (documentIdFromName1, openDocumentDirectory1): Implement the + cache referred to by the commentary. + * java/org/gnu/emacs/EmacsService.java (deleteDocument): + Invalidate the cache upon document removal. + * src/androidvfs.c (android_saf_exception_check) + (android_document_id_from_name): Correctly preserve or set errno + in error cases. + +2023-07-28 Po Lu + + Fix SAF query + * java/org/gnu/emacs/EmacsSafThread.java (documentIdFromName1): + Fix query argument placeholder string. + + Update Android port + * src/androidvfs.c (android_document_id_from_name): Don't return + 0 if an SAF exception occurs. + + Avoid crashes when the primary clip is empty + * src/androidselect.c (Fandroid_get_clipboard): Don't return + data if clipboard is empty. Reported by Johan Widén + . + +2023-07-28 Po Lu + + Allow quitting from Android content provider operations + * doc/emacs/android.texi (Android Document Providers): Say that + quitting is now possible. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + functions `safSyncAndReadInput', `safync' and `safPostRequest'. + * java/org/gnu/emacs/EmacsSafThread.java: New file. Move + cancel-able SAF operations here. + * java/org/gnu/emacs/EmacsService.java (EmacsService): Allow + quitting from most SAF operations. + * src/androidvfs.c (android_saf_exception_check): Return EINTR + if OperationCanceledException is received. + (android_saf_stat, android_saf_access) + (android_document_id_from_name, android_saf_tree_opendir_1) + (android_saf_file_open): Don't allow reentrant calls from async + input handlers. + (NATIVE_NAME): Implement new synchronization primitives for JNI. + (android_vfs_init): Initialize new class. + + * src/dired.c (open_directory): Handle EINTR from opendir. + * src/sysdep.c: Describe which operations may return EINTR on + Android. + +2023-07-28 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsDirectoryEntry.java + (EmacsDirectoryEntry): Make class final. + * java/org/gnu/emacs/EmacsService.java (accessDocument) + (openDocumentDirectory, openDocument, createDocument): Throw + access and IO error exceptions instead of catching them. + (createDirectory, deleteDocument): New functions. + * src/android.c (android_init_emacs_service): Add new functions. + * src/android.h (struct android_emacs_service): Likewise. + * src/androidvfs.c (android_saf_exception_check): New function. + Translate between Java exceptions and errno values. + (android_saf_stat, android_saf_access, android_saf_delete_document) + (struct android_saf_tree_vnode, android_document_id_from_name) + (android_saf_tree_name, android_saf_tree_rmdir) + (android_saf_tree_opendir_1, android_saf_tree_opendir) + (android_saf_file_open, android_saf_file_unlink) + (android_saf_new_open, android_saf_new_mkdir): Implement missing + VFS operations and derive errno values from the type of any + exceptions thrown. + (android_vfs_init): Initialize exception classes. + (android_mkdir, android_fstat): Remove trailing whitespace. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-27 Po Lu + + Update Android port + * doc/emacs/android.texi (Android Document Providers): Improve + wording of paragraph clarifying limits on subprocesses. + * java/org/gnu/emacs/EmacsService.java (getDocumentTrees): Use + Java standard US-ASCII coding standard instead of the + undocumented ``ASCII'' alias. + (decodeFileName): Remove unused function. + (documentIdFromName): + * src/android.c (android_init_emacs_service): Take a String for + NAME instead of a byte array. + * src/androidvfs.c (android_verify_jni_string): New function. + (android_document_id_from_name): Verify that STRING is a valid + Modified UTF-8 string. + + Update Android port + * src/androidvfs.c (android_afs_initial): + (android_content_get_directory_name): + (android_saf_tree_name): + (android_saf_tree_from_name): + (android_vfs_init): Silence compiler warnings. + + Update Android port + * src/android.c (android_run_in_emacs_thread): Behave more + robustly if SIGIO arrives too late Emacs for Emacs to check for + signals, but too early to preempt a long running syscall. + + Avoid crashes in some edge cases + * java/org/gnu/emacs/EmacsActivity.java (onActivityResult): + Avoid crashes in some edge cases. + + Avoid dereference of a freed vnode's operations table + * src/androidvfs.c (android_renameat_noreplace): + (android_rename): Free vdst using vdst->ops, not vp->ops. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-27 Po Lu + + Update Android port + * configure.ac (ANDROID_STUBIFY): Add androidvfs.o when building + libemacs.so. + * doc/emacs/android.texi (Android): Add `Android Document Providers'. + (Android Startup): Update the location of the content identifier + directory. + (Android File System): Describe access to document provider + directories. + (Android Document Providers): New node. + * doc/emacs/emacs.texi (Top): Update the menu for the Android + appendix. + * java/Makefile.in (filename, install_temp/assets/build_info): Make + directory-tree depend on build_info. + * java/org/gnu/emacs/EmacsActivity.java (onActivityResult): New + function. When a document tree is accepted, persist access to it. + * java/org/gnu/emacs/EmacsDirectoryEntry.java (EmacsDirectoryEntry): + New struct. + * java/org/gnu/emacs/EmacsOpenActivity.java (checkReadableOrCopy): Use + EmacsService.buildContentName. + * java/org/gnu/emacs/EmacsService.java (getEmacsView, openContentUri) + (checkContentUri): Remove excessive debug logging. + (buildContentName, getDocumentAuthorities, requestDirectoryAccess) + (getDocumentTrees, decodeFileName, documentIdFromName, getTreeUri) + (statDocument, accessDocument, openDocumentDirectory, readDirectoryEntry) + (openDocument, createDocument): New functions. + + * lib-src/asset-directory-tool.c: Improve commentary by illustrating + the difference between directory and ordinary files. + + * src/android.c (ANDROID_THROW, enum android_fd_table_entry_flags) + (struct android_emacs_service, android_extract_long) + (android_scan_directory_tree, android_is_directory) + (android_get_asset_name, android_url_encode, android_content_name_p) + (android_get_content_name, android_check_content_access, android_fstat) + (android_fstatat, android_file_access_p, android_hack_asset_fd_fallback) + (android_detect_ashmem, android_hack_asset_fd, android_close_on_exec) + (android_open, android_close, android_fclose, android_create_lib_link) + (android_faccessat, struct android_dir, android_opendir, android_dirfd) + (android_readdir, android_closedir, android_lookup_asset_directory_fd) + (android_exception_check_3, android_get_current_api_level) + (android_open_asset, android_close_asset, android_asset_read_quit) + (android_asset_read, android_asset_lseek, android_asset_fstat): Move + content and asset related functions to androidvfs.c. + (android_init_emacs_service): Obtain handles for new JNI functions. + (initEmacsParams): Initialize the VFS layer. + (android_request_directory_access): New function. + (android_display_toast): Remove unused function. + + * src/android.h (android_get_current_api_level): Assume that + this function never returns less than __ANDROID_API__. + (struct android_emacs_service): Move `struct + android_emacs_service' here. + + * src/androidfns.c (Fandroid_request_directory_access): New + interactive function. + (syms_of_androidfns): Register new subr. + + * src/androidvfs.c (struct android_vdir, struct android_vops) + (struct android_vnode, struct android_special_vnode) + (enum android_vnode_type, struct android_cursor_class) + (struct emacs_directory_entry_class) + (struct android_parcel_file_descriptor_class) + (android_init_cursor_class, android_init_entry_class) + (android_init_fd_class, android_vfs_canonicalize_name) + (struct android_unix_vnode, struct android_unix_vdir, unix_vfs_ops) + (android_unix_name, android_unix_vnode, android_unix_open) + (android_unix_close, android_unix_unlink, android_unix_symlink) + (android_unix_rmdir, android_unix_rename, android_unix_stat) + (android_unix_access, android_unix_mkdir, android_unix_readdir) + (android_unix_closedir, android_unix_dirfd, android_unix_opendir) + (android_extract_long, android_scan_directory_tree) + (android_is_directory, android_init_assets) + (android_hack_asset_fd_fallback, android_detect_ashmem) + (android_hack_asset_fd, struct android_afs_vnode) + (struct android_afs_vdir, struct android_afs_open_fd, afs_vfs_ops) + (android_afs_name, android_afs_initial, android_close_on_exec) + (android_afs_open, android_afs_close, android_afs_unlink) + (android_afs_symlink, android_afs_rmdir, android_afs_rename) + (android_afs_stat, android_afs_access, android_afs_mkdir) + (android_afs_readdir, android_afs_closedir, android_afs_dirfd) + (android_afs_opendir, android_afs_get_directory_name) + (struct android_content_vdir, content_vfs_ops) + (content_directory_contents, android_content_name) + (android_content_open, android_content_close) + (android_content_unlink, android_content_symlink) + (android_content_rmdir, android_content_rename) + (android_content_stat, android_content_access) + (android_content_mkdir, android_content_readdir) + (android_content_closedir, android_content_dirfd) + (android_content_opendir, android_content_get_directory_name) + (android_content_initial, android_get_content_name) + (android_check_content_access, struct android_authority_vnode) + (authority_vfs_ops, android_authority_name, android_authority_open) + (android_authority_close, android_authority_unlink) + (android_authority_symlink, android_authority_rmdir) + (android_authority_rename, android_authority_stat) + (android_authority_access, android_authority_mkdir) + (android_authority_opendir, android_authority_initial) + (struct android_saf_root_vnode, struct android_saf_root_vdir) + (saf_root_vfs_ops, android_saf_root_name, android_saf_root_open) + (android_saf_root_close, android_saf_root_unlink) + (android_saf_root_symlink, android_saf_root_rmdir) + (android_saf_root_rename, android_saf_root_stat) + (android_saf_root_access, android_saf_root_mkdir) + (android_saf_root_readdir, android_saf_root_closedir) + (android_saf_root_dirfd, android_saf_root_opendir) + (android_saf_root_initial, android_saf_root_get_directory) + (android_saf_stat, android_saf_access) + (struct android_saf_tree_vnode, struct android_saf_tree_vdir) + (saf_tree_vfs_ops, android_document_id_from_name) + (android_saf_tree_name, android_saf_tree_open) + (android_saf_tree_close, android_saf_tree_unlink) + (android_saf_tree_symlink, android_saf_tree_rmdir) + (android_saf_tree_rename, android_saf_tree_stat) + (android_saf_tree_access, android_saf_tree_mkdir) + (android_saf_tree_opendir_1, android_saf_tree_readdir) + (android_saf_tree_closedir, android_saf_tree_dirfd) + (android_saf_tree_opendir, android_saf_tree_from_name) + (android_saf_tree_get_directory, android_saf_file_vnode) + (saf_file_vfs_ops, android_saf_file_name, android_saf_file_open) + (android_saf_file_unlink, android_saf_file_rmdir) + (android_saf_file_opendir, android_close_parcel_fd) + (android_saf_new_vnode, android_saf_new_name, android_saf_new_open) + (android_saf_new_unlink, android_saf_new_symlink) + (android_saf_new_rmdir, android_saf_new_rename) + (android_saf_new_stat, android_saf_new_access) + (android_saf_new_mkdir, android_saf_new_opendir, root_vfs_ops) + (special_vnodes, android_root_name, android_name_file) + (android_vfs_init, android_open, android_unlink, android_symlink) + (android_rmdir, android_mkdir, android_renameat_noreplace) + (android_rename, android_fstat, android_fstatat_1, android_fstatat) + (android_faccessat, android_fdopen, android_close, android_fclose) + (android_open_asset, android_close_asset, android_asset_read_quit) + (android_asset_read, android_asset_lseek, android_asset_fstat) + (android_opendir, android_dirfd, android_readdir) + (android_closedir): Move file system emulation routines here. + Introduce a new ``VFS'' layer for translating between + Emacs-specific file names and the various disparate interfaces + for accessing files on Android. + + * src/callproc.c (delete_temp_file): + * src/charset.c (load_charset_map_from_file): + * src/dired.c: + * src/emacs.c (Fkill_emacs): + * src/fileio.c (check_mutable_filename, Fcopy_file) + (Fmake_directory_internal, Fdelete_directory_internal) + (Fdelete_file, Frename_file, Fadd_name_to_file) + (Fmake_symbolic_link, file_accessible_directory_p, Fset_file_modes) + (Fset_file_times, write_region): + * src/filelock.c (get_boot_time, rename_lock_file) + (create_lock_file, current_lock_owner, unlock_file): + * src/image.c (slurp_file, png_load_body, jpeg_load_body): + * src/keyboard.c (Fopen_dribble_file): + * src/lisp.h: + * src/lread.c (Fload): + * src/process.c (handle_child_signal): + * src/sysdep.c (init_standard_fds, emacs_fopen, emacs_fdopen) + (emacs_unlink, emacs_symlink, emacs_rmdir, emacs_mkdir) + (emacs_renameat_noreplace, emacs_rename): + * src/term.c (Fresume_tty, init_tty): Use and add new wrappers + for fopen, fdopen, unlink, symlink, rmdir, mkdir, + renameat_norepalce and rename. + +2023-07-26 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-24 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-23 Po Lu + + Facilitate locating the app library directory + * doc/emacs/android.texi (Android File System): Document where + the app library directory can probably be found. + * src/android.c (android_create_lib_link): New function. Try to + symlink `lib' in the directory holding the files directory to + the app library directory. + (setEmacsParams): Call that function if Emacs is being + initialized from an application context. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-22 Po Lu + + Try harder to keep the initial word selected + * lisp/touch-screen.el (touch-screen-drag): If + touch-screen-word-select, also keep the initial word within the + region while scrolling. + + Fix window box computation for menu bar windows + * src/window.h (WINDOW_MENU_BAR_P): Check for external menu bars + using HAVE_WINDOW_SYSTEM && HAVE_EXT_MENU_BAR instead of hard + coding X without Xt or GTK. + + Update Android port + * doc/lispref/commands.texi (Key Sequence Input): Describe which + events receive imaginary prefix keys. + * lisp/touch-screen.el (touch-screen-translate-touch): Consider + `vertical-line' a virtual function key. + (function-key-map): Translate events on vertical window borders. + + * etc/NEWS: Announce `current-key-remap-sequence'. + + Fix default value of scroll bar frame parameters on Android + * src/androidfns.c (Fx_create_frame): Default + Qvertical_scroll_bars to Qnil, but set scroll-bar-width and + scroll-bar-height. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-21 Po Lu + + Improve touch screen and text conversion behavior of many commands + * doc/lispref/commands.texi (Key Sequence Input): Document new + argument to `read-key-sequence' etc. + * lisp/help-macro.el (make-help-screen): + * lisp/subr.el (read-key, read-char-choice-with-read-key): + Disable text conversion and display the OSK before reading a key + sequence. + * lisp/touch-screen.el (touch-screen-window-selection-changed): + Only cancel the minibuffer OSK timer. + (touch-screen-handle-point-up): Update comment accordingly. + * src/keyboard.c (command_loop_1, read_menu_command) + (read_key_sequence, read_key_sequence_vs, Fread_key_sequence) + (Fread_key_sequence_vector): New arg DISABLE_TEXT_CONVERSION. + All callers changed. + + Correctly translate touchscreen-up events outside a frame + * lisp/touch-screen.el (touch-screen-translate-touch): Check if + a prefix is specified separately from prefix being non-nil. + Accept `nil' as an imaginary prefix key. + (function-key-map): Register translation functions on the tab + bar, tab lines and internal border. + + Improve touch screen scrolling support + * lisp/touch-screen.el (touch-screen-preview-select): Avoid + unnecessary redisplays. + (touch-screen-drag): Scroll at window margins using window + scrolling functions instead of relying on redisplay to recenter + the window around point. + + Update Android port + * doc/emacs/input.texi (Touchscreens): Document + `touch-screen-preview-select'. + * doc/lispref/commands.texi (Touchscreen Events): Fix typo in + the descriptions of two touch screen events. + * lisp/dired.el (dired-insert-set-properties): Adjust for + changes to file end computation. + * lisp/minibuffer.el (clear-minibuffer-message): Don't clear + minibuffer message if dragging. + * lisp/touch-screen.el (touch-screen-current-tool): Fix doc + string. + (touch-screen-preview-select): New function. + (touch-screen-drag): Call it if point changes. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-20 Po Lu + + Update Android port + * exec/trace.c (handle_readlinkat): Adjust commentary to match + behavior. + * src/android.c (android_get_keysym_name): NULL terminate + *name_return. + + Update some menu definitions for Android + * lisp/international/mule-cmds.el (set-coding-system-map): Don't + display `set-terminal-coding-system' on Android. + + * lisp/cus-edit.el (custom-display): Add `android' display type. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/android.c (struct android_event_queue): Don't make + unnecessarily volatile. + + Update Android port + * lisp/touch-screen.el (touch-screen-handle-touch): Don't + restart dragging if point is at ZV and the tap falls on a + different row. + + Use context menu header titles on Android + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): + New field `title'. + (addSubmenu): New arg TITLE. Set that field. + (expandTo): Set MENU's header title if it's a context menu. + * src/androidmenu.c (android_init_emacs_context_menu): Adjust + signature of `createContextMenu'. + (android_menu_show): Use TITLE instead of pane titles if there's + only one pane. + + Merge remote-tracking branch 'origin/master' into feature/android + + Introduce a `dired-click-select' mode + * doc/emacs/dired.texi (Marks vs Flags): Document command bound + to `touchscreen-hold'. + * doc/lispref/commands.texi (Touchscreen Events): Describe + `touch-screen-inhibit-drag'. + * etc/NEWS: Improve description of changes to touch screen + support. + * lisp/dired-aux.el (dired-do-chxxx, dired-do-chmod) + (dired-do-print, dired-do-shell-command, dired-do-compress-to) + (dired-do-create-files, dired-do-rename, dired-do-isearch) + (dired-do-isearch-regexp, dired-do-search) + (dired-do-query-replace-regexp, dired-do-find-regexp) + (dired-vc-next-action): Disable ``click to select'' after + running this command. + * lisp/dired.el (dired-insert-set-properties): Attach + click-to-select keymap to file names if necessary. + (dired-mode-map): Bind `touchscreen-hold' to click to select + mode. + (dired-post-do-command): New function. + (dired-do-delete): Call it. + (dired-mark-for-click, dired-enable-click-to-select-mode): New + functions. + (dired-click-to-select-mode): New minor mode. + * lisp/touch-screen.el (touch-screen-current-tool): Fix doc + string. + (touch-screen-inhibit-drag): New function. + +2023-07-19 Po Lu + + * src/sfnt.c (sfnt_infer_deltas): Improve commentary. + + Improve behavior of `restart-drag' + * lisp/touch-screen.el (touch-screen-handle-point-up): If what + is `restart-drag' (meaning that a drag has been restarted but + the touchpoint has not moved), translate the release into a + regular mouse click to deactivate the region. + + Update Android port + * build-aux/makecounter.sh (curcount): Rename `counter' to + `emacs_shortlisp_counter'. + * doc/emacs/input.texi (Touchscreens): Document + `touch-screen-extend-selection'. + * doc/lispref/commands.texi (Touchscreen Events): Document + `touchscreen-restart-drag'. + * lisp/touch-screen.el (touch-screen-extend-selection): New user + option. + (touch-screen-restart-drag): New function. + (touch-screen-handle-point-update): Handle `restart-drag' + gestures. + (touch-screen-handle-touch): Check if the prerequisites for + extending a previous drag gesture are met, and generate such + events if so. + (touch-screen-translate-touch): Update doc string. + * src/Makefile.in (otherobj): Remove BUILD_COUNTER_OBJ. + ($(lispsource)/international/charprop.el): + (%.elc): Don't depend on bootstrap-emacs if cross configuring + for Android. + (libemacs.so): Directly depend on and link with + BUILD_COUNTER_OBJ. + + Make sure Android builds are redumped upon changes to shortlisp + * build-aux/makecounter.sh: New script. + * src/Makefile.in (abs_top_builddir): New variable. + (BUILD_COUNTER_OBJ): Define to build-counter.o + if compiling for Android. + (build-counter.c): New target. Generate this file using + makecounter.sh upon changes to lisp.mk or shortlisp. + (lisp.mk): Make and load relative to abs_top_builddir. + (emacs$(EXEEXT)): Adjust acordingly. + (mostlyclean): Remove build-counter.c. + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-18 Po Lu + + Fix typos in touch-screen.el + * lisp/touch-screen.el (touch-screen-handle-point-update) + (touch-screen-handle-point-up): Fix typos. + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + + * lisp/touch-screen.el (touch-screen-handle-point-update): Fix typo. + + Avoid splurious menu-bar nil events + * src/keyboard.c (make_lispy_event): Return nil if no menu item + is found. + + Update Android port + * lisp/touch-screen.el (touch-screen-hold) + (touch-screen-handle-point-up): Don't select inactive minibuffer + windows. + (touch-screen-handle-point-update): Improve detection of left + and right edges. + + Update Android port + * lisp/touch-screen.el (touch-screen-handle-touch): Fix + treatment of stray update events. + + Don't enable scroll-bar-mode by default on Android + * src/frame.c (syms_of_frame): Default to nil if HAVE_ANDROID. + + * src/keyboard.c (make_lispy_event): Fix botched merge. + + Update Android port + * doc/lispref/commands.texi (Touchscreen Events): Describe + treatment of canceled touch sequences during touch event + translation. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Update JNI + prototypes. + * java/org/gnu/emacs/EmacsWindow.java (motionEvent): Set + cancelation flag in events sent where appropriate. + * lisp/touch-screen.el (touch-screen-handle-point-update): + Improve treatment of horizontal scrolling near window edges. + (touch-screen-handle-touch): Don't handle point up if the touch + sequence has been canceled. + * src/android.c (sendTouchDown, sendTouchUp, sendTouchMove): New + argument `flags'. + * src/androidgui.h (enum android_touch_event_flags): New enum. + (struct android_touch_event): New field `flags'. + * src/androidterm.c (handle_one_android_event): Report + cancelation in TOUCHSCREEN_END_EVENTs. + * src/keyboard.c (make_lispy_event): Fix botched merge. + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-17 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * doc/lispref/commands.texi (Touchscreen Events): Document + meaning of `mouse-1-menu-command'. + * lisp/mouse.el (minor-mode-menu-from-indicator): New arg EVENT. + Give it to popup-menu. + (mouse-minor-mode-menu): Use posn specified within EVENT. + * lisp/touch-screen.el (touch-screen-handle-touch): Fix + interactive translation. Treat commands labeled + `mouse-1-menu-command' like ordinary keymaps. + + Update Android port + * doc/lispref/commands.texi (Touchscreen Events): Document + changes to simple translation. + * lisp/touch-screen.el (touch-screen-handle-point-up): Generate + `down-mouse-1' if the current gesture is `mouse-1-menu'. + (touch-screen-handle-touch): If binding is a keymap, set state + to `mouse-1-menu' and wait for point to be released before + generating down-mouse-1. + + Improve word selection behavior + * lisp/tab-bar.el (tab-bar-map): Don't bind touch-screen-drag. + * lisp/touch-screen.el (touch-screen-drag): Extend the region + based on the position of the mark, not the position of point + relative to EVENT. + (touch-screen-translate-touch): Don't generate virtual function + keys for non-mouse events. + (function-key-map): Remove redundant definitions. + * src/keyboard.c (read_key_sequence): Don't generate *-bar + prefix keys for mock input (such as input from function key + translation.) + + Improve touch screen support + * doc/emacs/input.texi (Touchscreens): Document the new feature + for people who have trouble dragging to word boundaries. + * lisp/touch-screen.el (touch-screen-word-select): New + defcustom. + (touch-screen-word-select-bounds) + (touch-screen-word-select-initial-word): New variable + definitions. + (touch-screen-hold): If `touch-screen-word-select', select the + word around EVENT. + (touch-screen-drag): If `touch-screen-word-select', extend the + region to the next word boundary if the character under point + constitutes a word. + (touch-screen-handle-point-update, touch-screen-handle-touch) + (touch-screen-translate-touch): Fix doc strings and fill + comments. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-16 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsService.java (displayToast): + * src/android.c (android_init_emacs_service): Remove unused + function. + * lisp/touch-screen.el (touch-screen-handle-point-up): Correctly + compute posn point. + (touch-screen-translate-touch): Update doc string. + (function-key-map): Define touch screen translation functions + within the internal border. + + Update Android port + * doc/lispref/commands.texi (Touchscreen Events): Improve + documentation. + * lisp/tab-bar.el (tab-bar-map): Bind `[tab-bar + touchscreen-hold]'. + * lisp/touch-screen.el (touch-screen-hold, touch-screen-drag): + New functions. + (touch-screen-handle-timeout): Generate a `touchscreen-hold' + event instead. + (touch-screen-handle-point-update): Generate a + `touchscreen-drag' event upon dragging. + (touch-screen-translate-touch): Cancel touch screen timer upon + exit. + * src/keyboard.c (access_keymap_keyremap): Take unsigned int + start and end instead. + + Improve touch-screen support + * doc/emacs/emacs.texi (Top): + * doc/emacs/input.texi (Other Input Devices): Correctly + capitalize subsection name. + (Touchscreens): Document additional translation. + * doc/lispref/commands.texi (Touchscreen Events): Document that + `touchscreen-end' events now have prefix keys. Also, describe + mouse emulation and `touchscreen-scroll' events. + * doc/lispref/keymaps.texi (Translation Keymaps): Document + `current-key-remap-sequence'. + * lisp/touch-screen.el (touch-screen-translate-prompt): New + function. + (touch-screen-scroll): New command. Bind to + `touchscreen-scroll'. + (touch-screen-handle-point-update, touch-screen-handle-point-up) + (touch-screen-handle-touch): Refactor to actually translate + touch screen event sequences, as opposed to looking up commands + and executing them. + (touch-screen-translate-touch): New function. Bind in + function-key-map to all touch screen events. + (touch-screen-drag-mode-line-1, touch-screen-drag-mode-line) + (touch-screen-tap-header-line): Remove special commands for + dragging the mode line and clicking on the header line. + * lisp/wid-edit.el (widget-button-click): Adjust accordingly. + * src/keyboard.c (access_keymap_keyremap): Bind + `current-key-remap-sequence' to the key sequence being remapped. + (keyremap_step): Give fkey->start and fkey->end to + access_keymap_keyremap. + (head_table): Add imaginary prefix to touchscreen-end events as + well. + (syms_of_keyboard): New variable Vcurrent_key_remap_sequence. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-15 Po Lu + + Update Android port + * doc/emacs/android.texi (Android): Add new node to menu. + (Android Environment): Add footnote pointing to new node. + (Android Software): New node. + * doc/emacs/emacs.texi (Top): Add new node to menu. + * java/AndroidManifest.xml.in (manifest): Fix location of + sharedUserId property. + * java/INSTALL: Improve documentation of shared user ID + support. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-14 Po Lu + + Make --with-shared-user-id work + * configure.ac (ANDROID_SHARED_USER_NAME): New variable. + Substitute it. + * java/AndroidManifest.xml.in: Set `sharedUserLabel' if a shared + user ID is enabled. + * java/res/values/strings.xml (shared_user_name): New string + resource. + + Update Android port + * src/android.c (android_blit_copy): Don't check for overflow + where not required. + + Merge remote-tracking branch 'origin/master' into feature/android + + Clean up Android debug code + * java/org/gnu/emacs/EmacsInputConnection.java + (getSurroundingText): Don't print debug information if DEBUG_IC + is off. + + * lisp/calc/calc.el (calc): Fix typo. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-13 Po Lu + + Add a Doc View tool bar + * etc/NEWS: Announce the new tool bar. + * etc/images/last-page.xpm: + * etc/images/last-page.pbm: New images. Mirrored from + next-page.xpm. + * lisp/doc-view.el (doc-view-menu): Use `doc-view-new-search' + instead of an anonymous function. + (doc-view-tool-bar-map): New keymap. + (doc-view-search): Update the tool bar after initiating a + search. + (doc-view-new-search): New command. + (doc-view-mode): Set the tool bar map appropriately. + + Improve workaround for partial texture updates on Android + * java/AndroidManifest.xml.in: + * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Don't + change hardware acceleration state. + * java/org/gnu/emacs/EmacsNative.java (notifyPixelsChanged): New + function. + * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): + New field `bitmapChanged'. + (copyToFrontBuffer): Signal that the bitmap has changed. + (onDraw): If the bitmap has changed, increment the generation + ID. + * src/android.c (JNICALL): Implement new function. + +2023-07-13 Po Lu + + Disable hardware acceleration on Android + It serves no purpose and causes tearing. Uploading the bitmap + to the GPU takes about as long as it does to incrementally + update the surface in software. + + * java/AndroidManifest.xml.in: Disable hardware acceleration. + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): Make + lastClosedMenu static. + * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Enable + hardware acceleration within alert dialogs. + * java/org/gnu/emacs/EmacsSurfaceView.java (onDraw): Describe + why hardware acceleration is disabled. + * java/org/gnu/emacs/EmacsWindow.java (run): Remove redundant + call. + +2023-07-13 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-12 Po Lu + + * src/android.c (android_run_select_thread): Fix typo. + + Update Android port + * src/android.c (android_run_select_thread): Correctly return + the set of ready read and write descriptors on __ANDROID_API__ < + 16 systems. + (android_select): Clear the set of ready file descriptors if + events from the event queue are present despite pselect failing. + + Fix keyboard state translation on Android + * src/androidterm.c (android_android_to_emacs_modifiers) + (android_emacs_to_android_modifiers): Fix statement precedence + bugs. + + * src/doc.c (doc_close): Remove unused macro. + + Update Android port + * java/org/gnu/emacs/EmacsWindow.java (whatButtonWasIt): Handle + back and forward buttons along with styluses. + * src/doc.c (close_file_unwind_android_fd): New function. + (get_doc_string, Fsnarf_documentation): Don't create a temporary + fd if it can be avoided. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-11 Po Lu + + Fix doc file generation on Android + * .gitignore: Ignore cross/etc/DOC. + * configure.ac: Make the directory `cross/etc'. + * cross/Makefile.in (CLEAN_SUBDIRS): Clean files inside `etc' as + well. + * java/Makefile.in (install_temp): Copy cross/etc/DOC to the + package if it is available. + * src/Makefile.in (SOME_MACHINE_OBJECTS): Add androidselect.c, + sfntfont-android.c and sfntfont.c. + (libemacs.so): Depend on $(etc)/DOC. + + Update Android port + * src/sfnt.c (sfnt_fill_span): Correctly clip span to raster + width, ensuring that the last pixel is filled. + (main): Adjust test sizes. + + Update Android port + * java/org/gnu/emacs/EmacsView.java (onGenericMotionEvent): Call + onGenericMotionEvent. + * java/org/gnu/emacs/EmacsWindow.java (Coordinate): New fields + `button' and `id'. + (): Add new arguments to the construtor. + (whatButtonWasIt): Return 0 if the button state has not changed. + (buttonForEvent): New function. + (figureChange): Return the Coordinate object associated to + EVENT. Determine whether or not EVENT was accompanied by a + change to the button state, and ascertain which button that was. + (motionEvent): New function. + (onGenericMotionEvent, onTouchEvent): Factor out touch and mouse + event delivery to motionEvent. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-10 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsService.java (browseUrl): New argument + SEND. Choose from a list of applications that want to share the + URL if true. + * lisp/net/browse-url.el (browse-url-android-share): New user + option. + (browse-url-default-android-browser): Respect said user option. + * src/android.c (android_init_emacs_service) + (android_browse_url): Expose new option. + * src/android.h: Update prototypes. + * src/androidselect.c (Fandroid_browse_url): Likewise. + + ; Update from Gnulib + * lib/vasnprintf.c (VASNPRINTF): + * m4/printf.m4 (gl_SWPRINTF_WORKS): + (gl_SWPRINTF_DIRECTIVE_LA): + * m4/vasnprintf.m4 (gl_PREREQ_PRINTF_PARSE): + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-09 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsWindow.java (eventModifiers) + (motionEventModifiers): New functions. + (onKeyDown, onKeyUp, onFocusChanged, onSomeKindOfMotionEvent): + Don't record the previous modifier mask; instead, always use the + modifier state specified in the event. + * src/androidterm.c (handle_one_android_event): Don't dispatch + button release events when a popup is active. + + Update Android port + * java/org/gnu/emacs/EmacsService.java (onStartCommand): Fix + typo in notification message. + * java/org/gnu/emacs/EmacsWindow.java (onFocusChanged): Reset + the recorded modifier state upon a change to the window focus. + + * java/org/gnu/emacs/EmacsService.java (onCreate): Fix typo. + + Update Android port + * java/org/gnu/emacs/EmacsDrawPoint.java (perform): Don't fill + an extra pixel. + * java/org/gnu/emacs/EmacsService.java (onCreate): Make sure + scaledDensity is always at least 160 dpi. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-08 Po Lu + + Fix EmacsDrawLine again + * java/org/gnu/emacs/EmacsDrawLine.java (perform): Symmetrically + adjust coordinates to cover the last pixel. Then, fill the left + most pixel of the line. + + Update Android port + * java/org/gnu/emacs/EmacsService.java (DEBUG_IC) + (DEBUG_THREADS): Improve commentary. + * src/androidterm.c (handle_one_android_event): Signal + completion of IME events that have lost their frames. + (requestCursorUpdates): Don't set an edit counter as this event + won't be passed to the text conversion machinery. + + ; Fix whitespace + * src/android.c (android_blit_xor): + (android_check_query_urgent): + (android_run_in_emacs_thread): + (android_update_extracted_text): Fix whitespace. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-07 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsSurfaceView.java (copyToFrontBuffer): + Use fallback bit blit function on Android 7.0 as well, as + crashes have been observed in drawBitmap. + +2023-07-07 Po Lu + + Update Android port + * lisp/tool-bar.el (modifier-bar-modifier-list): New variable. + (modifier-bar-button): New function. + (tool-bar-event-apply-alt-modifier) + (tool-bar-event-apply-super-modifier) + (tool-bar-event-apply-hyper-modifier) + (tool-bar-event-apply-shift-modifier) + (tool-bar-event-apply-control-modifier) + (tool-bar-event-apply-meta-modifier): Factor out modifier bar + logic to that function, and redisplay the tool bar where + necessary. + (modifier-bar-available-p): New function. + (modifier-bar-mode): Disable tool bar items corresponding to + modifier keys that've already been pressed. + + * etc/images/shift.pbm: Regenerate from correct font. + +2023-07-07 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-06 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsNative.java (scaledDensity): Announce + new argument `scaledDensity'. + * java/org/gnu/emacs/EmacsNoninteractive.java (main): Specify + new argument. + * java/org/gnu/emacs/EmacsService.java (onCreate): Compute an + adjusted DPI for the font size based on the ratio between + density and scaledDensity. + (run): Specify that adjusted density. + * src/android.c (setEmacsParams): Set + `android_scaled_pixel_density'. + * src/android.h: (android_scaled_pixel_density: New variable. + * src/androidterm.c (android_term_init): Set `font_resolution'. + * src/androidterm.h (struct android_display_info): New field. + * src/font.c (font_pixel_size, font_find_for_lface) + (font_open_for_lface, Ffont_face_attributes, Fopen_font): Use + FRAME_RES instead of FRAME_RES_Y. + * src/frame.h (FRAME_RES): New macro. Use this to convert + between font point and pixel sizes as opposed to FRAME_RES_Y. + * src/w32font.c (fill_in_logfont): + * src/xfaces.c (Fx_family_fonts, set_lface_from_font): Use + FRAME_RES instead of FRAME_RES_Y. (bug#64444) + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-05 Po Lu + + Fix bug#64445 + * doc/emacs/android.texi (Android Environment): Document that + Emacs also receives READ_EXTERNAL_STORAGE by default on old + versions of Android. + * java/AndroidManifest.xml.in: Request READ_EXTERNAL_STORAGE. + (bug#64445) + + * doc/emacs/emacs.texi (Emacs and Android): Fix menu. + + ; Update Android port + * doc/emacs/android.texi (Android): Fix discrepancies between + menu and sectioning. + + Fix crash between Android 4.0 and Android 5.1 + * java/org/gnu/emacs/EmacsService.java (detectMouse): Don't use + function that is not present on Android 4.0. + + Update Android port + * doc/lispref/commands.texi (Misc Events): Correctly index + `set-text-conversion-style'. + * lisp/tool-bar.el (tool-bar-event-apply-alt-modifier) + (tool-bar-event-apply-super-modifier) + (tool-bar-event-apply-hyper-modifier) + (tool-bar-event-apply-shift-modifier) + (tool-bar-event-apply-control-modifier) + (tool-bar-event-apply-meta-modifier): Pass t when restoring text + conversion style. + * src/keyboard.c (restore_reading_key_sequence): New function. + (read_key_sequence): Set `reading_key_sequence' where necessary. + * src/keyboard.h: Declare variable. + * src/textconv.c (check_postponed_buffers): New function. + (Fset_text_conversion_style): New argument. If set, and a key + sequence is being read, postpone resetting the IME until the key + sequence is read. + (syms_of_textconv): Clear new variable and add staticpro. + * src/textconv.h: Update prototypes. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-04 Po Lu + + Implement a tool bar containing modifier keys + * doc/emacs/frames.texi (Tool Bars): Describe modifier bars. + * doc/lispref/keymaps.texi (Extended Menu Items, Tool Bar): + Document changes to tool bar menu item handling and secondary + tool bars. + * etc/NEWS: Announce changes. + * lisp/simple.el (event-apply-modifier): Correctly apply Ctrl + and Shift modifiers to lower case ASCII key events that already + have other modifiers applied. + * lisp/tool-bar.el (tool-bar--cache-key) + (tool-bar--secondary-cache-key): New defsubsts. + (tool-bar--flush-cache): Flush secondary tool bar cache. + (tool-bar-make-keymap): Include secondary tool bar if necessary. + (tool-bar-make-keymap-1): New arg MAP. Generate a keymap for + that map if specified, else default to tool-bar-map. + (set-text-conversion-style, tool-bar-apply-modifiers) + (overriding-text-conversion-style) + (tool-bar-event-apply-alt-modifier) + (tool-bar-event-apply-super-modifier) + (tool-bar-event-apply-hyper-modifier) + (tool-bar-event-apply-shift-modifier) + (tool-bar-event-apply-control-modifier) + (tool-bar-event-apply-meta-modifier, modifier-bar-mode): New + functions. + * src/dispextern.h (enum tool_bar_item_idx): Add + TOOL_BAR_ITEM_WRAP. + * src/frame.c (make_frame): Clear new field `tool_bar_wraps_p'. + * src/frame.h (struct frame): New field `tool_bar_wraps_p'. + * src/keyboard.c (parse_tool_bar_item): Handle QCwrap properties + in tool bar menu items. + (syms_of_keyboard): New defsym QCwrap. + * src/xdisp.c (build_desired_tool_bar_string): Clear + f->tool_bar_wraps_p and set it appropriately. Insert new line + characters in the tool bar string upon encountering a wrap + character. + (display_tool_bar_line): Stop at EOB, not line end. Reseat on + the next line upon encountering EOL characters. + (redisplay_tool_bar): Allow rows to be different heights if + explicit new lines are present upon the tool bar string. + + * src/sfnt.c (sfnt_decompose_compound_glyph): Pacify warning. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-03 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-02 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-07-01 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-30 Po Lu + + * src/android.c (android_query_tree): Correctly return children. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-29 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-28 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-27 Po Lu + + * doc/emacs/android.texi (Android Environment): Improve wording. + + * doc/emacs/android.texi (Android Environment): Fix typos. + + Update Android port + * src/android.c (android_exception_check) + (android_exception_check_1) + (android_exception_check_2) + (android_exception_check_nonnull) + (android_exception_check_nonnull_1): Tell the compiler to expect + that the object is non-NULL, or that no exception has been + thrown. + + * exec/loader-mips64el.s (rest_of_exec): Fix typo in comment. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-26 Po Lu + + * doc/lispref/commands.texi (Touchscreen Events): Fix typo. + +2023-06-26 Po Lu + + Update Android port + * lisp/calc/calc.el (calc-mode, calc): Make sure the on-screen + keyboard is not hidden when a Calc buffer is created or a Calc + Trail window is being created for the first time. + * lisp/touch-screen.el (touch-screen-window-selection-changed): + Take touch-screen-display-keyboard in to account. + + * src/sfnt.c (sfnt_decompose_compound_glyph) + (sfnt_interpret_compound_glyph_1): Reset `defer_offsets' before + processing each component. + (sfnt_lerp_half): Avoid undefined shift of negative value. + (sfnt_compute_tuple_scale): Pacify compiler warning. + +2023-06-26 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-25 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-24 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-23 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): + * java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7FontEntity): + (hasChar): Clean up dead stores. + + * src/android.c (android_wc_lookup_string): Fix typo. + + Correctly check result of string lookup + * src/android.c (android_wc_lookup_string): Check that + GetStringChars returns non-NULL, not if it throws an exception. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-22 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-21 Po Lu + + Update Android port + * src/androidfns.c (android_set_tool_bar_position): + (frame_geometry): + * src/androidterm.c (android_flash): + (android_clear_under_internal_border): Synchronize with X. + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-20 Po Lu + + * src/androidfns.c (android_frame_parm_handlers): Fix typo. + + Synchronize tool bar position code with X + * src/androidfns.c (android_set_tool_bar_position): New + function. + (android_frame_parm_handlers): Add new frame param handler. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-19 Po Lu + + * lib-src/Makefile.in (seccomp-filter$(EXEEXT)): Link with Gnulib. + + Update Android port + * java/org/gnu/emacs/EmacsView.java (EmacsView, dimensionsLock): + New field. + (): Create new lock object. + (handleDirtyBitmap, onLayout, onAttachedToWindow): Make sure + measuredWidth and measuredHeight are extracted and set + atomically. Send Expose upon layout changes if the view has + grown. + + * exec/Makefile.in (clean): Add `exec1'. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-18 Po Lu + + Update Android port + * src/window.h (GCALIGNED_STRUCT): Improve documentation of + `last_mark'. + * src/xdisp.c (mark_window_display_accurate_1): Don't set + `last_mark' to -1 if the mark is inactive. + + Enable text conversion in conf-modes + * lisp/textmodes/conf-mode.el (conf-mode-initialize): Set + text-conversion-style. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-17 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Initialize signal mask earlier + * java/org/gnu/emacs/EmacsService.java (onCreate, run): Don't + initialize signal mask here. + * java/org/gnu/emacs/EmacsApplication.java (onCreate): Do it + here instead. + * src/android.c (JNICALL): Restore previous signal masks. + + * java/README: More documentation. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-16 Po Lu + + Fix quitting after changes to signal delivery + * src/android.c (android_write_event, JNICALL) + (android_run_in_emacs_thread): Don't rely on raise to call + deliver_process_signal. + + Update Android port + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): + * java/org/gnu/emacs/EmacsApplication.java (findDumpFile): + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) + (addSubmenu, display): + * java/org/gnu/emacs/EmacsDocumentsProvider.java + (getNotificationUri, queryChildDocuments, deleteDocument): + * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): + * java/org/gnu/emacs/EmacsFillRectangle.java (perform): + * java/org/gnu/emacs/EmacsOpenActivity.java (readEmacsClientLog) + (checkReadableOrCopy): + * java/org/gnu/emacs/EmacsSdk7FontDriver.java + (EmacsSdk7FontDriver): + * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): + * java/org/gnu/emacs/EmacsView.java (EmacsView): + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, onKeyUp): + * java/org/gnu/emacs/EmacsWindowAttachmentManager.java + (EmacsWindowAttachmentManager): Remove various unused arguments + and variables, dead stores, and make minor cleanups and + performance improvements. + * src/androidmenu.c (FIND_METHOD_STATIC, android_menu_show): + Adjust accordingly. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-15 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsInputConnection.java + (EmacsInputConnection, beginBatchEdit, reset, endBatchEdit): + Keep track of the number of batch edits and return an + appropriate value. + (takeSnapshot): Implement function. + * java/org/gnu/emacs/EmacsNative.java (takeSnapshot): New + function. + * java/org/gnu/emacs/EmacsService.java (resetIC): Improve + debugging output. + * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): + Call `reset' to clear the UI side batch edit count. + * src/androidterm.c (struct + android_get_surrounding_text_context): New fields + `conversion_start' and `conversion_end'. + (android_get_surrounding_text): Return the conversion region. + (android_get_surrounding_text_internal, NATIVE_NAME): Factor out + `getSurroundingText'. + (takeSnapshot): New function. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-14 Po Lu + + Improve IM synchronization on Android + * java/org/gnu/emacs/EmacsInputConnection.java + (EmacsInputConnection): Reimplement as an InputConnection, not + BaseInputConnection. + * src/androidterm.c (performEditorAction): Sync prior to sending + keyboard events. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-13 Po Lu + + Improve behavior of Gnus on Android + * etc/NEWS: Fix typo. + * lisp/gnus/gnus-score.el (gnus-read-char): New function. + (gnus-summary-increase-score): Use it to display a dialog box on + Android, where input methods have trouble with plain old + read-char. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-12 Po Lu + + Improve appearance of custom dialog buttons on Android + * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Resolve + dialog button style and use it instead. + + Fix deadlocks + * java/org/gnu/emacs/EmacsView.java (EmacsView) + (showOnScreenKeyboard, hideOnScreenKeyboard): Don't synchronize. + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow) + (toggleOnScreenKeyboard): Revert to calling IMM functions from + the main thread. + * src/android.c (struct android_event_container) + (android_pselect_nfds, android_pselect_readfds) + (android_pselect_writefds, android_pselect_exceptfds) + (android_pselect_timeout): Don't make volatile. + (android_wait_event): Run queries if necessary. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-11 Po Lu + + Update Android port + * lisp/net/tramp.el (tramp-encoding-shell): + * lisp/obsolete/terminal.el (terminal-emulator): + * lisp/term.el (term-exec-1): + * lisp/textmodes/artist.el (artist-figlet-get-font-list): + * src/android.c (JNICALL): Where /bin/sh was previously used, + use /system/bin/sh on Android. + + Update Android port + * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): + Document member variables. + (onDraw): Use separate Paint object on the UI thread. + * src/textconv.c (really_commit_text, really_set_composing_text) + (really_delete_surrounding_text): Run modification hooks when + deleting text. + + Avoid extraneous calls to the UI thread + * java/org/gnu/emacs/EmacsView.java (EmacsView) + (showOnScreenKeyboard, hideOnScreenKeyboard) + (onCheckIsTextEditor): Make synchronized. + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow) + (toggleOnScreenKeyboard): Don't post to the main thread. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-10 Po Lu + + ; Update Android port + * src/keyboard.c (handle_input_available_signal): Don't generate + instructions not available in arm mode. + + Update Android port + * src/android.c (android_select, android_check_query) + (android_check_query_urgent, android_answer_query) + (android_answer_query_spin, android_begin_query, android_end_query) + (android_run_in_emacs_thread): Use finer grained memory + synchronization semantics. + * src/androidterm.c (android_get_selection): Use the current + selection, not its value at the time of the last redisplay. + * src/keyboard.c (handle_input_available_signal): Place memory + barrier. + + Inherit surrounding text properties when inserting conversion text + * src/textconv.c (really_commit_text) + (really_set_composing_text): Improve behavior of certain + fontification mechanisms by inheriting text properties from + surrounding text. + + Merge remote-tracking branch 'origin/master' into feature/android + + Prevent hangs from IM requests with the main thread busy + * src/android.c (android_select): Clear `android_urgent_query'. + (android_check_query): Make static. Clear + `android_urgent_query'. + (android_check_query_urgent): New function; work like + `android_check_query', but only answer urgent queries. + (android_answer_query, android_end_query): Clear urgent query + flag. + (android_run_in_emacs_thread): Initially wait two seconds for + the query to run from the keyboard loop; upon a timeout, set + `android_urgent_query' to true and wait for it to run while + reading async input. + * src/android.h: Update prototypes. + * src/keyboard.c (handle_async_input): Call + `android_check_query_urgent'. + +2023-06-09 Po Lu + + ; Fix typos + * src/textconv.c (really_commit_text): + (handle_pending_conversion_events): Fix minor typos. + + Avoid responding to input method queries asynchronously + * src/androidterm.c (handle_one_android_event): Don't answer + queries here; just rely on the event interrupting + android_select. This avoids exposing buffer contents to input + methods while a command is being executed. + * src/textconv.c (TEXTCONV_DEBUG, really_commit_text) + (really_finish_composing_text, really_set_composing_text) + (really_set_composing_region, really_delete_surrounding_text) + (really_set_point_and_mark, get_extracted_text): Add debugging + printouts. + + Initialize text conversion hooks for each C Mode buffer + * lisp/progmodes/cc-mode.el (c-initialize-cc-mode): Always add + text conversion hooks. + + * src/android.c (android_get_gc_values): Remove redundancy. + + Block profiling signals in the Android UI thread + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + function `setupSystemThread'. + * java/org/gnu/emacs/EmacsService.java (onCreate): Block all + signals except for SIGBUS and SIGSEGV in the UI thread. + * src/android.c (setupSystemThread): New function. + + Fix crash starting Emacs to open file + * java/org/gnu/emacs/EmacsThread.java (run): Correct check + against extraStartupArgument when an initial file is specified. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-08 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Correctly display popup dialogs from Emacsclient + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): + Make subclasses final. + * java/org/gnu/emacs/EmacsDialog.java (display1): Check if an + instance of EmacsOpenActivity is open; if it is, try using it to + display the pop up dialog. + * java/org/gnu/emacs/EmacsDialogButtonLayout.java + (EmacsDialogButtonLayout): Make final. + * java/org/gnu/emacs/EmacsHolder.java (EmacsHolder): + Likewise. + * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity): + New field `currentActivity'. + (onCreate, onDestroy, onWindowFocusChanged, onPause): Set that + field as appropriate. + + Update Android port + * src/android.c (android_is_special_directory): New function. + (android_get_asset_name, android_content_name_p) + (android_get_content_name): + * src/android.h (android_is_special_directory) + (JNI_STACK_ALIGNMENT_PROLOGUE): + * src/fileio.c (check_mutable_filename): + * src/filelock.c (WTMP_FILE, make_lock_file_name): + * src/inotify.c (IN_ONLYDIR, Finotify_add_watch): Factor out + checks against asset and content directories to that function. + + ; Update from Gnulib + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-07 Po Lu + + ; Update Android port + * doc/emacs/android.texi (Android Startup): Fix reference to + non existent node. + + Update Android port + * java/org/gnu/emacs/EmacsInputConnection.java (beginBatchEdit) + (endBatchEdit, commitCompletion, commitText, deleteSurroundingText) + (finishComposingText, getSelectedText, getTextAfterCursor) + (getTextBeforeCursor, setComposingText, setComposingRegion) + (performEditorAction, performContextMenuAction, getExtractedText) + (setSelection, sendKeyEvent, deleteSurroundingTextInCodePoints) + (requestCursorUpdates): Ensure that the input connection is up + to date. + (getSurroundingText): New function. + * java/org/gnu/emacs/EmacsNative.java (getSurroundingText): + Export new C function. + * java/org/gnu/emacs/EmacsService.java (resetIC): Invalidate + previously created input connections. + * java/org/gnu/emacs/EmacsView.java (EmacsView) + (onCreateInputConnection): Signify that input connections are + now up to date. + * src/androidterm.c (struct + android_get_surrounding_text_context): New structure. + (android_get_surrounding_text, NATIVE_NAME): + * src/textconv.c (get_surrounding_text): + * src/textconv.h: New functions. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-06 Po Lu + + * lisp/simple.el (analyze-text-conversion): Remove old workaround. + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-06 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsContextMenu.java (display): Use + `EmacsHolder' instead of `Holder'. + * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Use + `EmacsDialogButtonLayout' to ensure that buttons are wrapped + properly. + (display): Adjust for new holder class. + * java/org/gnu/emacs/EmacsDialogButtonLayout.java + (EmacsDialogButtonLayout, onMeasure, onLayout): New functions. + + * java/org/gnu/emacs/EmacsDrawLine.java: + * java/org/gnu/emacs/EmacsFillPolygon.java: Remove redundant + imports. + * java/org/gnu/emacs/EmacsHolder.java (EmacsHolder): + * java/org/gnu/emacs/EmacsService.java (class Holder) + (getEmacsView, EmacsService): Rename `Holder' to `EmacsHolder' + and make it public. + +2023-06-06 Po Lu + + Improve undo behavior on Android + * lisp/simple.el (undo-auto-amalgamate): Update doc string to + describe new amalgamating commands. + (analyze-text-conversion): Make this an amalgamating command by + default, unless a new line has been inserted. Also, shorten the + undo boundary timer. + * src/textconv.c (really_commit_text) + (really_set_composing_text): Correctly report ephemeral + deletions. + (syms_of_textconv): Fix doc strings. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-05 Po Lu + + Clear batch edit state once a new input connection is established + * src/androidterm.c (android_handle_ime_event): Clear batch edit + state, in case the previous input method forgot to do so. + + Update Android port + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + function clearInputFlags. + * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): + Stop reporting changes after a new input method connection is + established. + * src/androidterm.c (android_handle_ime_event): Implement that + change. + (JNICALL): New function. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-04 Po Lu + + * src/keyboard.c: Fix build without window system + + * configure.ac: Tune pty detection for Android. + + Fix input method synchronization problems + * java/debug.sh (gdbserver_cmd, is_root): Prefer TCP again. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + function `queryAndSpin'. + * java/org/gnu/emacs/EmacsService.java (EmacsService) + (icBeginSynchronous, icEndSynchronous, viewGetSelection): New + synchronization functions. + (resetIC, updateCursorAnchorInfo): Call those instead. + * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): + Call viewGetSelection. + * src/android.c (JNICALL, android_answer_query_spin): New + functions. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-03 Po Lu + + Fix typos in Android port + * lisp/bindings.el (global-map): Bind cut, copy and paste. + * src/androidterm.c (JNICALL): Use key. + + Behave correctly when IMEs commit or compose text with active mark + * src/textconv.c (really_commit_text) + (really_set_composing_text): Delete text between mark and point + if the mark is active. Don't record changes if the text is + empty. + + Update Android port + * src/androidterm.c (struct android_get_extracted_text_context): + New field `mark_active'. + (android_get_extracted_text): Set that field. + (struct android_extracted_text_class): New field `flags'. + (android_build_extracted_text): New argument `mark_active'. Set + flags appropriately. + (NATIVE_NAME, android_update_selection): Likewise. + * src/textconv.c (get_extracted_text): New argument + `mark_active'. Set it if the mark is active. + * src/textconv.h: Update prototypes. + + Merge remote-tracking branch 'origin/master' into feature/android + + * etc/MACHINES: Fix reference to obsolete file. + +2023-06-02 Po Lu + + Improve Eldoc text conversion support + * lisp/emacs-lisp/eldoc.el: ("back-to-indentation"): Register + touch screen and text conversion commands. + + Improve CC Mode support for text conversion + * lisp/progmodes/cc-cmds.el (c-post-text-conversion): New + function. + * lisp/progmodes/cc-mode.el (c-initialize-cc-mode): Add it as + the `post-texxt-conversion-hook'. + * lisp/simple.el (post-text-conversion-hook): New hook. + (analyze-text-conversion): Run it until success before trying + post insert functions. + + Update Android port + * java/org/gnu/emacs/EmacsInputConnection.java + (EmacsInputConnection): Apply workarounds on Vivo devices as + well. + * src/android.c (sendKeyPress, sendKeyRelease): Clear counter. + * src/androidgui.h (struct android_key_event): New field + `counter'. + * src/androidterm.c (handle_one_android_event): Generate + barriers as appropriate. + (JNICALL): Set `counter'. + * src/frame.h (enum text_conversion_operation): + * src/textconv.c (detect_conversion_events) + (really_set_composing_text, handle_pending_conversion_events_1) + (handle_pending_conversion_events, textconv_barrier): + * src/textconv.h: Implement text conversion barriers and fix + various typos. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-06-01 Po Lu + + Correctly export file:// URIs on Android + * java/org/gnu/emacs/EmacsService.java (browseUrl): If uri's + scheme is `file', rewrite it into a content URI. + + Update Android port + * java/org/gnu/emacs/EmacsInputConnection.java + (EmacsInputConnection, performContextMenuAction): New function. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative) + (performContextMenuAction): New function. + * src/android.c (android_get_gc_values): Implement more + efficiently. + * src/androidterm.c (android_handle_ime_event): Pass through + `update' argument to `finish_composing_text'. Fix thinko. + * src/textconv.c (really_finish_composing_text) + (really_set_composing_text, really_set_composing_region) + (handle_pending_conversion_events_1, finish_composing_text): New + argument `update'. Notify IME of conversion region changes if + set. + * src/textconv.h: Update structs and prototypes. + + Update Android port + * java/org/gnu/emacs/EmacsInputConnection.java + (EmacsInputConnection): Add compatibility adjustments for + Samsung devices. + + Correctly report start and end in extracted text + * src/androidterm.c (struct android_get_extracted_text_context): + New field `start_offset' and `end_offset'. Delete `offset'. + (android_get_extracted_text, android_build_extracted_text): + Replace `offset' with new args `start_offset' and `end_offset'. + (NATIVE_NAME): Set `start_offset' and `end_offset'. + (android_update_selection): Likewise. + * src/textconv.c (get_extracted_text): Likewise. + * src/textconv.h: Update prototypes. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-31 Po Lu + + Fix build with Lisp_Object type checking + * configure.ac: Pass through `--enable-check-lisp-object-type' + on Android. + * src/alloc.c (android_make_lisp_symbol): + * src/android.c: + * src/androidfns.c (android_set_no_focus_on_map) + (android_set_no_accept_focus): + * src/androidfont.c (androidfont_match, androidfont_open_font): + * src/androidselect.c (Fandroid_get_clipboard) + (Fandroid_get_clipboard_targets): + * src/keyboard.c (make_lispy_event, syms_of_keyboard): + * src/sfntfont.c (sfnt_enum_font_1, sfntfont_list_1): + * src/textconv.c (really_set_point_and_mark): Fix Lisp_Object + and integer screw-ups. + + Update Android port + * doc/emacs/input.texi (Other Input Devices, Touchscreens) + (On-Screen Keyboards): + * doc/lispref/commands.texi (Misc Events): + * src/android.c (android_faccessat): Improve word choices and + commentary. + * lisp/touch-screen.el (touch-screen-handle-scroll): Make + precision scrolling work better with horizontal movement. + + * src/android.c (android_copy_area): Pacify compiler warning. + + Update Android port + * exec/exec.c (insert_args): New argument `arg3'. Replace + argv[1] with that argument. + (exec_0): Pass file name of script to `insert_args'. + + Update android.texi + * doc/emacs/android.texi (What is Android?): + (Android Startup): + (Android File System): + (Android Environment): + (Android Windowing): + (Android Troubleshooting): Improve wording and various other + issues. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * java/debug.sh (is_root): Go back to using unix sockets; allow + adb to forward them correctly. + * java/org/gnu/emacs/EmacsInputConnection.java + (getExtractedText): Don't print text if NULL. + * java/org/gnu/emacs/EmacsService.java (EmacsService): New field + `imSyncInProgress'. + (updateIC): If an IM sync might be in progress, avoid deadlocks. + * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): + Set `imSyncInProgress' across synchronization point. + * src/android.c (android_check_query): Use __atomic_store_n. + (android_answer_query): New function. + (android_begin_query): Set `android_servicing_query' to 2. + Check once, and don't spin waiting for query to complete. + (android_end_query): Use __atomic_store_n. + (android_run_in_emacs_thread): Compare-and-exchange flag. If + originally 1, fail. + * src/textconv.c (really_set_composing_text): Clear conversion + region if text is empty. + +2023-05-30 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-29 Po Lu + + * src/android.c: Fix typos. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/android.c (android_blit_copy): + (android_blit_xor): Fix typos. + + * src/android.c (android_blit_copy): Fix typos. + + Work around more problems with Bitmaps + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + function `blitRect'. + * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): + Use it on Android 8.x. + * src/android.c (blitRect): Implement new function. + + Update Android port + * src/android.c (android_neon_mask_line): Fix iteration + over remainder. + (android_blit_copy): Be more paranoid. + + Implement android_copy_area in C + * java/org/gnu/emacs/EmacsCopyArea.java: Remove file. + * java/org/gnu/emacs/EmacsService.java (EmacsService, copyArea): + Delete function. + * src/android.c (struct android_emacs_service) + (android_init_emacs_service): Remove `copy_area'. + (android_create_gc, android_change_gc, android_get_gc_values): + Record new GC values. + (android_neon_mask_line): New function. + (android_blit_copy, android_blit_xor): New functions. + (android_copy_area): Implement in C. + (android_lock_bitmap): Accept drawables instead of windows. + * src/android.h: Adjust prototype for `android_lock_bitmap'. + * src/androidgui.h (struct android_gc): Record last known GC + values. + +2023-05-27 Po Lu + + Add extra thread-related checking + * java/org/gnu/emacs/EmacsService.java (EmacsService) + (checkEmacsThread): New function. + (fillPolygon, drawRectangle, drawLine, drawPoint, copyArea) + (clearArea): + * java/org/gnu/emacs/EmacsThread.java (EmacsThread): + * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers): + Call where appropriate. + + Remove synchronization around `damageRegion' + * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers): + Remove unnecessary documentation. `damageRegion' is only + changed from the Emacs thread. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-26 Po Lu + + Allow starting Emacs --debug-init on Android + * doc/emacs/android.texi (Android Troubleshooting): Document + `debug-init' option. + * java/AndroidManifest.xml.in + (EmacsLauncherPreferencesActivity): New activity. Export on + systems older than Android 7.0. + * java/org/gnu/emacs/EmacsActivity.java (onCreate): Adjust for + string startup argument. + * java/org/gnu/emacs/EmacsLauncherPreferencesActivity.java: New + file. + * java/org/gnu/emacs/EmacsPreferencesActivity.java + (EmacsPreferencesActivity): Don't make final. + (startEmacsQ): Give start-up argument as an argument, not as a + boolean. + (startEmacsDebugInit): New function. + (onCreate): Register new listener; make final. + * java/org/gnu/emacs/EmacsService.java (onCreate): Pass + extraStartupArgument. + * java/org/gnu/emacs/EmacsThread.java (EmacsThread): Rename + startDashQ to extraStartupArgument. + (run): Adjust accordingly. + * java/res/values-v24/bool.xml: + * java/res/values/bool.xml: + * java/res/values/strings.xml: New files. + * java/res/xml/preferences.xml: Add new option. Move string + resources around. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-25 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-24 Po Lu + + Update Android port + * src/sfnt.c (sfnt_decompose_compound_glyph): Allow decomposing + up to 16 nested components. + (CALL, LOOPCALL): Correctly error if no fdef storage exists. + (sfnt_interpret_run): New label `next_instruction', for CALL. + (sfnt_interpret_compound_glyph_1): Allow decomposing up to 16 + nested components. Prevent crash if there are no end points or + points. + (sfnt_read_cvar_table): Prevent assigning uninitialized values. + (sfnt_vary_simple_glyph): Update commentary. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-23 Po Lu + + * exec/exec.c (exec_0): Use strcpy. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-22 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-21 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-20 Po Lu + + Remove arbitrary process count limit + * exec/trace.c (handle_clone_prepare): + (handle_clone): When !REENTRANT, use malloc to allocate + tracees after running out of static ones. + + Update Android port + * java/org/gnu/emacs/EmacsView.java (swapBuffers): Restore + missing damage rect code. + (onDetachedFromWindow): Remove redundant synchronization. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-19 Po Lu + + Make tapping on header lines behave reasonably + * lisp/touch-screen.el (touch-screen-tap-header-line): New + function. + ([header-line touchscreen-begin]): Define to + `touch-screen-tap-header-line'. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-18 Po Lu + + * make-dist (possibly_non_vc_files): Add Android-specific files. + + Allow interacting with the tab line from a touch screen + * doc/emacs/frames.texi (Tab Bars): Explain how to interact with + the tab bar from a touch screen. + * doc/emacs/input.texi (Touchscreens): Document exactly what a + ``long press'' is. + * doc/emacs/windows.texi (Tab Line): Likewise. + * lisp/tab-line.el (tab-line-tab-map, tab-line-add-map) + (tab-line-tab-close-map, tab-line-left-map, tab-line-right-map): + Bind `touchscreen-begin' to each command. + (tab-line-track-tap, tab-line-event-start): New functions. + (tab-line-hscroll-right, tab-line-hscroll-left, tab-line-new-tab) + (tab-line-select-tab, tab-line-close-tab): Use them. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-17 Po Lu + + ; Update from Gnulib + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-16 Po Lu + + Add touchscreen support to the tab bar + * lisp/menu-bar.el (popup-menu-normalize-position): Normalize + `touchscreen-begin' events correctly. + * lisp/tab-bar.el (tab-bar-mouse-context-menu): New argument + POSN. Use it if specified. + (touch-screen-track-tap, tab-bar-handle-timeout) + (tab-bar-touchscreen-begin): New functions. + (tab-bar-map): Bind [tab-bar touchscreen-begin]. + * lisp/touch-screen.el (touch-screen-track-drag): Fix doc + string. + * src/dispextern.h: Export `get_tab_bar_item_kbd'. + * src/keyboard.c (coords_in_tab_bar_window): New function. + (make_lispy_event): Adjust touchscreen begin event mouse + position list for tab bar. + * src/xdisp.c (tab_bar_item_info): Allow CLOSE_P to be NULL. + (get_tab_bar_item): Adjust doc string. + (get_tab_bar_item_kbd): New function. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-15 Po Lu + + Fix year 2038 code for Android 4.4 and earlier + * configure.ac: Also disable enable_year2038. + + Fix the MS-DOS build + * msdos/sed1v2.inp: Fix removal of ANDROID_BUILD_CFLAGS. + * msdos/sedlibmk.inp: Clear DIR_HAS_FD_MEMBER and + LOCALE_FR_UTF8. + + ; Update from Gnulib + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-14 Po Lu + + Implement document moving on Android + * java/org/gnu/emacs/EmacsDocumentsProvider.java + (notifyChangeByName): New function. + (queryDocument1): Set FLAG_SUPPORTS_MOVE where necessary. + (moveDocument): Implement new function. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-13 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-12 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-11 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-10 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-09 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-08 Po Lu + + Update Android port + * java/Makefile.in (install_temp/assets/version): Fix generation + in out of tree builds. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-07 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsInputConnection.java + (requestCursorUpdates): + * java/org/gnu/emacs/EmacsNative.java (requestCursorUpdates): + * java/org/gnu/emacs/EmacsService.java (updateCursorAnchorInfo): + New functions. + * src/android.c (struct android_emacs_service) + (android_init_emacs_service): Add new method. + (android_update_cursor_anchor_info): New function. + * src/androidfns.c (android_set_preeditarea): New function. + * src/androidgui.h (enum android_ime_operation): New operation + `REQUEST_CURSOR_UPDATES'. + (struct android_ime_event): Document new meaning of `length'. + * src/androidterm.c (android_request_cursor_updates): New + function. + (android_handle_ime_event): Handle new operations. + (handle_one_android_event, android_draw_window_cursor): Update + the preedit area if needed, like on X. + (requestCursorUpdates): New function. + * src/androidterm.h (struct android_output): New field + `need_cursor_updates'. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-06 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * configure.ac (LIBGMP_CFLAGS): Avoid non portable test + expression. + + Update Android port + * cross/verbose.mk.android: Get rid of badly aligned ANDROID_CC + messages. + * java/org/gnu/emacs/EmacsInputConnection.java (syncAfterCommit) + (extractAbsoluteOffsets): Add workarounds for several kinds of + machines. + (commitText, getExtractedText): Likewise. + * src/textconv.c (really_commit_text): Improve definition of + POSITION. + (get_extracted_text): Default to providing at least 4 + characters. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-05 Po Lu + + Fix execution of /proc/self/exe within child processes + * exec/exec.h (struct exec_tracee): New field `new_child'. + Also, make `waiting_for_syscall' a bitfield. + * exec/trace.c (PTRACE_GETEVENTMSG): New declaration. + (MAX_TRACEES): Bump to 4096. + (handle_clone_prepare): New function. + (handle_clone): If required, set `new_child' and wait for a + ptrace event describing the parent to arrive. + (after_fork): Clear new field. + (exec_waitpid): Upon a ptrace event describing a clone, create + the child's tracee if it doesn't already exist. Otherwise, copy + over the parent's cmdline and start running it. + + Update Android port + * doc/emacs/android.texi (Android Environment): Document lossage + with SIGSTOP. + * exec/exec.c (exec_0): Check X_OK on file being opened. Also + handle /proc/self/exe. + + Update Android port + * exec/trace.c (SYS_SECCOMP): Define when not present. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-04 Po Lu + + Document another misfeature of Android + * doc/emacs/android.texi (Android Environment): Describe how to + turn off process killing. + + Update Android port + * exec/trace.c (check_signal): New function. + (handle_exec, process_system_call): Handle signal-delivery-stop + while waiting synchronously for syscall completion. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-03 Po Lu + + Update Android port + * exec/config.h.in: Autoheader. + * exec/configure.ac: Check for siginfo_t.si_syscall. + * exec/trace.c (exec_waitpid): If SIGSYS is received, and caused by + seccomp, drop it should the call number be the invalid system call + used by Emacs. + + Update Android port + * exec/config.h.in: Autoheader. + * exec/configure.ac: Use system extensions. + (HAVE_PROCESS_VM): Define if process_vm_readv and + process_vm_writev are available. + * exec/trace.c (read_memory, user_copy): Implement in terms of + process_vm if possible. + + Remove extra debugging code + * exec/loader-mipsel.s (__start): Remove extraneous debugging + code. + + Update Android port + * exec/Makefile.in: (.PHONY): Add `bootstrap-clean' and + `extraclean'. + (bootstrap-clean): New rule. + + Update Android port + * java/Makefile.in (FIND_DELETE): New substitution. + (clean): Use FIND_DELETE. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-02 Po Lu + + * doc/emacs/android.texi (Android Environment): Improve doc. + + Update Android port + * exec/config.h.in (__bool_true_false_are_defined): + * exec/configure.ac (REENTRANT): New definition. + (READLINKAT_SYSCALL, READLINK_SYSCALL): New defines. Set on all + hosts. + * exec/exec.c (MIN, MAX): Remove redundant declarations. Move + to config.h. + (exec_0): Copy name of executable into NAME when !REENTRANT. + * exec/exec.h (struct exec_tracee): New struct `exec_file'. + * exec/trace.c (remove_tracee, handle_exec, handle_readlinkat) + (process_system_call, after_fork): Handle readlinkat system + calls. + + Fix ps name in Android subprocesses + * exec/Makefile.in (.SUFFIXES): Include ., then `srcdir'. + * exec/loader-aarch64.s (_start): + * exec/loader-armeabi.s (_start): + * exec/loader-mips64el.s (__start): + * exec/loader-mipsel.s (__start): + * exec/loader-x86.s (_start): + * exec/loader-x86_64.s (_start): Get basename of opened exec + file and make it the command name. Fix envp skipping on x86 + and various leaks. + + Port Android port to older Android systems + * exec/config.h.in: Autoheader. + * exec/configure.ac: Check for declarations of stpcpy and + stpncpy. + * exec/exec.c (stpcpy, stpncpy): Use replacements if + declarations are not present; this happens when a new Android + NDK is building for an old version of Android. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-05-01 Po Lu + + Update Android port + * exec/config.h.in: Update config.h.in. + * exec/configure.ac: Check for stpcpy and stpncpy. + * exec/exec.c (rpl_stpcpy, rpl_stpncpy): Define replacements + when they are not present on the system. + (process_program_header): Fill comment. + + * src/term.c (syms_of_term): Pretend Android uses TERMINFO. + + Fix cwd relative process execution on Android + * exec/exec.c (format_pid): New function. + (exec_0): Make cwd relative file names relative to + /proc/pid/cwd. + * exec/trace.c (handle_exec): Handle EINTR. + (process_system_call): Report failure without clobbering x0. + + * README: Describe `exec' directory. + + Fix use dialog box regression on Android + * lisp/subr.el (use-dialog-box-p): Always prefer dialog boxes. + + Make it easier to quit on Android + * src/android.c (android_write_event): + (JNICALL): Raise SIGIO on key press and window action events. + + Fix syscall error reporting on aarch64 + * exec/trace.c (process_system_call): Save and restore x0, x1 + and x2 regs after replacing them with an invalid file + descriptor. + + Update Android port + * Makefile.in (extraclean): Clean in exec as well. + * configure.ac: Fix detection of absolute srcdir. Also, pass + CFLAGS. + * exec/Makefile.in: (.c.o): Add -I. so config.h can be + found.:(.s.o): Don't create m4 temporary in srcdir. + * exec/config-mips.m4.in (DADDI2, DADDI3): New macros. Define + to substitute if as cannot assemble daddi. + * exec/configure.ac (user_h): Look for user.h in asm/ as well. + Use new user.h. Also look in ptrace.h on arm systems. Check if + as supports daddi on mips64. + * exec/exec.c (check_interpreter): Fix char signedness bug. + * exec/loader-mips64el.s (__start): Use DADDI2 and DADDI3 for + two- and 3-operand daddi. + * exec/mipsel-user.h: Don't include sgidefs.h. + * java/INSTALL: Document that m4 is now required. + * src/android.c (android_rewrite_spawn_argv): Add missing NULL. + + Merge remote-tracking branch 'origin/master' into feature/android + + Work around system restrictions regarding exec + * doc/emacs/android.texi (Android Environment): Document + `android-use-exec-loader'. + * exec/exec1.c (main): Set program group of child process. + * src/android.c (android_rewrite_spawn_argv): New function. + * src/android.h: Update prototypes. + * src/androidfns.c (syms_of_androidfns): New variable + `android_use_exec_loader'. + * src/callproc.c (emacs_spawn): Rewrite the argument vector to + use exec1 if necessary. + + Remove exec/configure + * exec/configure: Remove file. + * .gitignore: Add exec/configure. + +2023-04-30 Po Lu + + Add helper binary `exec1' + * .gitignore: New files. + * Makefile.in (mostlyclean_dirs): Add libexec, if its Makefile + exists. + * autogen.sh (do_git): Autoreconf in exec as well. + * configure.ac: Configure libexec on Android. + * exec/Makefile.in: + * exec/README: + * exec/config-mips.m4.in: + * exec/config.guess: + * exec/config.h.in: + * exec/config.sub: + * exec/configure: + * exec/configure.ac: + * exec/deps.mk: + * exec/exec.c (MIN, struct exec_open_command) + (struct exec_map_command, struct exec_jump_command) + (write_open_command, write_load_command, process_interpreter_1) + (process_interpreter, process_program_header, insert_args) + (exec_0): + * exec/exec.h (_EXEC_H_, struct elf_header_32) + (struct program_header_32, struct dt_entry_32) + (struct elf_header_64, struct program_header_64) + (struct dt_entry_64, struct exec_tracee): + * exec/exec1.c (main): + * exec/install-sh (scriptversion): + * exec/loader-aarch64.s (_start): + * exec/loader-armeabi.s (_start): + * exec/loader-mips64el.s (__start): + * exec/loader-mipsel.s (__start): + * exec/loader-x86.s (_start): + * exec/loader-x86_64.s (_start): + * exec/mipsel-user.h (_MIPSEL_USER_H_): + * exec/mipsfpu.c (MIPS_ABI_FP_ANY, fpu_reqs, valid_abi_p) + (fp_mode_for_abi, cpu_supports_fr0_p, determine_fpu_mode): + * exec/mipsfpu.h (_MIPSFPU_H_, FP_FR0): + * exec/test.c (print_usage, main): + * exec/trace.c (MAX_TRACEES, aarch64_set_regs, read_memory) + (user_alloca, user_copy, remove_tracee, handle_clone) + (syscall_trap_p, handle_exec, process_system_call, tracing_execve) + (after_fork, find_tracee, exec_waitpid, exec_init): New files. + * java/Makefile.in (CROSS_EXEC_BINS): Add exec1 and + loader. + ($(CROSS_EXEC_BINS) &): New target. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-29 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * build-aux/ndk-build-helper.mk (TARGET_ARCH): Define variable. + * configure.ac (ENABLE_CHECKING, CHECK_STRUCTS) + (GC_CHECK_STRING_OVERRUN, GC_CHECK_STRING_FREE_LIST, GLYPH_DEBUG) + (GC_CHECK_STRING_BYTES): Enable checking correctly on Android. + * java/README: Fix typos. + * m4/ndk-build.m4 (ndk_run_test): Pass target arch. + * src/android.c (android_get_content_name, android_close) + (android_fclose, android_check_string): Fix various typos caught + by checking. + * src/charset.c (load_charset_map_from_file): Call emacs_fclose, + not fclose. + * src/image.c (image_set_transform): Fix thinko. + (png_load_body, jpeg_load_body, gif_load): Call emacs_fclose, + not fclose. Use open instead of fdopen. + * src/xfaces.c (Fx_load_color_file): Likewise. + +2023-04-28 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-27 Po Lu + + Update Android port + * src/image.c (image_create_bitmap_from_data): Fix typo in + preprocessor conditionals. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * doc/emacs/android.texi (Android File System): + (Android Windowing): Make Emacs manual more portable. + + Update Android port + * doc/lispref/commands.texi (Misc Events): + * doc/lispref/frames.texi (Accessing Selections): + (X Selections): Fix pieces of the Info manual. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-26 Po Lu + + Make two well known amusements work on Android + * lisp/play/doctor.el (text-conversion-style): + (doctor-mode): + * lisp/play/dunnet.el (text-conversion-style): + (dun-mode): Set `text-conversion-style' to `action'. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-25 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-24 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-23 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-22 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-21 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-20 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-19 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-18 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-17 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-16 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-15 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-14 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-13 Po Lu + + Update Android port + * doc/emacs/android.texi (Android Fonts): Update documentation. + * doc/lispref/frames.texi (Accessing Selections, X Selections): + Fix typos. + * src/sfntfont-android.c (system_font_directories) + (init_sfntfont_android): Add `/product/fonts'. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-12 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-11 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-10 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-09 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-08 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Document selections on Android more thoroughly + * doc/lispref/elisp.texi (Top): + * doc/lispref/frames.texi (Frames): Add ``Accessing Selections'' + to menu. + (Accessing Selections, X Selections, Other Selections): New + nodes. + +2023-04-07 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-06 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-06 Po Lu + + Implement `yank-media' on Android + * doc/emacs/android.texi (Android Windowing): Update selection + restrictions. + * java/org/gnu/emacs/EmacsClipboard.java (EmacsClipboard): New + functions `getClipboardTargets' and `getClipboardData'. + * java/org/gnu/emacs/EmacsSdk11Clipboard.java + (EmacsSdk11Clipboard, getClipboardTargets, getClipboardData): + Implement. + * java/org/gnu/emacs/EmacsSdk8Clipboard.java: Stub out new + functions. + + * lisp/term/android-win.el (android-get-clipboard-1): Implement + MIME type targets. + * src/android.c (android_exception_check) + (android_exception_check_1, android_exception_check_2): Fix + punctuation in warning message. + (android_exception_check_nonnull_1): New function. + * src/android.h: Update prototypes. + * src/androidselect.c (struct android_emacs_clipboard): New + methods. + (android_init_emacs_clipboard): Initialize new methods. + (Fandroid_get_clipboard_targets, android_xfree_inside) + (Fandroid_get_clipboard_data): New functions. + (syms_of_androidselect): Define new subrs. + +2023-04-05 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-04 Po Lu + + * lisp/subr.el (read-char-from-minibuffer): Fix typo. + + Update Android port + * lisp/subr.el (read-char-from-minibuffer): Don't use undefined + variable. Reported by Robert Pluim. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-03 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + ; * src/androidselect.c (Fandroid_clipboard_exists_p): Add check. + + Update Android port + * src/sfnt.c (sfnt_normalize_vector): Don't rely on undefined + sign extension semantics. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-02 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-04-01 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-31 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-30 Po Lu + + Tweak outline cache stuff + * src/sfntfont.c: Adjust font cache size constants. + + * src/sfnt.c (GETINFO): Fix typo. + + * src/sfnt.h: Fix typo. + + Update Android port + * src/sfnt.c (sfnt_make_interpreter): New argument `fvar'. Set + axis count. + (SCANCTRL): Implement selector bit 8. + (GXAXIS): New instruction. + (SFVTPV): Validate graphics state after changing freedom vector. + (sfnt_line_to_vector): Implement `original'. + (sfnt_move): Remove redundant division. + (sfnt_interpret_run): Implement distortable font related GXAXIS + instruction (0x91). + (sfnt_vary_interpreter): Set naxis and norm_coords. + (sfnt_make_test_interpreter, pushb_test_args, pushw_test_args) + (sfnt_name_instruction, main): Adjust accordingly. + * src/sfnt.h (struct sfnt_interpreter, PROTOTYPE): + * src/sfntfont.c (sfntfont_setup_interpreter, sfntfont_open): + Set up distortion information. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-29 Po Lu + + Improve rules for enumerating user fonts + * doc/emacs/android.texi (Android Fonts): Document distortable + font replacement rules. + * src/sfntfont.c (sfnt_replace_fonts_p): New function. + (sfnt_enum_font_1): Call it. + + Fix optimized move functions + * src/sfnt.c (sfnt_move_x): + (sfnt_move_y): + (sfnt_move): Set N flags and don't forget to + set N points too. + + * src/sfnt.c (sfnt_read_avar_table): Fix sequencing problem. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/sfntfont.c (sfntfont_setup_interpreter): Don't create + interpreter for blatently broken fonts. + + Update Android port + * src/sfntfont.c (sfntfont_open): Avoid specifying redundant + blends. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/sfnt.c (sfnt_validate_gs): Fix validation of projection + vector. + +2023-03-28 Po Lu + + Update Android port + * src/sfnt.c (sfnt_vary_compound_glyph): + * src/sfntfont.c (sfntfont_get_glyph) + (sfntfont_get_glyph_outline): Avoid clobbering offset size flag + when varying compound glyph. + + Update Android port + * src/sfnt.c (sfnt_vary_simple_glyph, sfnt_vary_compound_glyph): + Fix application of intermediate tuples. + * src/sfntfont.c (sfntfont_open): Set xlfd name after applying + distortion. + + Correctly round lbearing values + * src/sfnt.h (SFNT_ROUND_FIXED): + * src/sfntfont.c (sfntfont_probe_widths): + (sfntfont_measure_pcm): Round lbearing properly. + + Update Android port + * src/sfntfont.c (sfnt_open_tables): Fix typos in non-HarfBuzz + code. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/androidterm.c (android_draw_image_glyph_string): Restore + potentially clobbered GC clipping. + * src/sfnt.c (sfnt_large_integer_add, sfnt_multiply_divide_round) + (sfnt_mul_fixed_round): New functions. + (sfnt_build_glyph_outline): Take unscaled glyph metrics. + (sfnt_prepare_raster, sfnt_vary_simple_glyph) + (sfnt_vary_compound_glyph, sfnt_vary_interpreter): Use rounding + multiplication to scale deltas. + (main): Adjust tests. + * src/sfntfont.c (sfntfont_get_glyph_outline) + (sfntfont_probe_widths, sfntfont_open, sfntfont_measure_pcm) + (sfntfont_draw): More minor fixes to variable fonts. + +2023-03-27 Po Lu + + Update Android port + * src/sfnt.c (sfnt_normalize_blend): Don't crash when axis + variations are not present. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * configure.ac (HAVE_OTF_GET_VARIATION_GLYPHS): Check for + `hb_font_set_var_named_instance'. + * src/sfnt.c (main): Update tests. + * src/sfntfont-android.c (Fandroid_enumerate_fonts): Blacklist + bad font. + * src/sfntfont.c (struct sfnt_font_tables, struct sfnt_font_desc) + (sfnt_decode_instance_name, sfnt_weight_descriptions) + (sfnt_enum_font_1, sfntfont_list_1, sfntfont_desc_to_entity) + (sfntfont_list, struct sfntfont_get_glyph_outline_dcontext) + (sfntfont_get_glyph, sfntfont_get_glyph_outline) + (struct sfnt_font_info, sfnt_close_tables, sfnt_open_tables) + (sfntfont_open, sfntfont_measure_pcm, sfntfont_close) + (sfntfont_draw, sfntfont_begin_hb_font, syms_of_sfntfont) + (mark_sfntfont): Handle variable fonts correctly. + + Refactor sfntfont.c + * src/sfnt.c (sfnt_build_glyph_outline): Take scale, not head + and pixel size. + (sfnt_scale_metrics_to_pixel_size): Delete function. + (sfnt_get_scale): New function. + (main): Update tests. + * src/sfnt.h (PROTOTYPE): Update prototypes. + * src/sfntfont.c (struct sfnt_outline_cache) + (sfntfont_get_glyph_outline, struct sfnt_font_info) + (sfntfont_open): Save scale in font information and use it. + (sfntfont_measure_instructed_pcm): Delete function. + (sfntfont_measure_pcm): Make this the only ``measure pcm'' + function. + (sfntfont_draw): Rely on sfntfont_get_glyph_outline for the + scale. + + Refactor sfntfont.c + * src/sfntfont.c (struct sfnt_font_tables): New structure. + (struct sfnt_font_desc): New field `tables'. + (struct sfnt_font_info): New field `desc'. + (sfntfont_setup_interpreter): Drop fd arguments and don't try to + load interpreter tables. + (sfnt_open_tables, sfnt_close_tables): New functions. + (sfnt_reference_font_tables, sfnt_dereference_font_tables): New + functions. + (sfntfont_open, sfntfont_close): Implement in terms of those + functions in order to share tables. + +2023-03-26 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-26 Po Lu + + Update Android port + * src/sfnt.c (sfnt_table_names): Add avar. + (sfnt_read_glyph): Clear distortion fields. + (sfnt_build_glyph_outline): Calculate the outline origin point. + (sfnt_prepare_raster): Apply the origin point to the X axis + offset. + (sfnt_scale_metrics_to_pixel_size): New function. + (sfnt_build_instructed_outline): Use instructed origin phantom + point to determine the outline origin. + (sfnt_compute_phantom_points): Apply origin and advance + distortion. + (struct sfnt_variation_axis, struct sfnt_instance) + (struct sfnt_fvar_table, sfnt_read_fvar_table) + (struct sfnt_gvar_table, sfnt_read_gvar_table) + (sfnt_read_avar_table, struct sfnt_blend, sfnt_init_blend) + (sfnt_free_blend, sfnt_normalize_blend, struct sfnt_tuple_header) + (struct sfnt_gvar_glyph_header, sfnt_read_packed_deltas) + (sfnt_compute_tuple_scale, sfnt_read_cvar_table) + (sfnt_infer_deltas_1, sfnt_vary_simple_glyph, sfnt_infer_deltas) + (sfnt_vary_glyph, sfnt_vary_compound_glyph) + (sfnt_vary_interpreter): New functions. Add structs to + sfntfont.h. + (struct sfnt_test_dcontext, sfnt_test_get_glyph, main): Test + distortable font handling. + + * src/sfnt.h (SFNT_ENABLE_HINTING): + (enum sfnt_table): + (struct sfnt_glyph): + (struct sfnt_glyph_outline): + (struct sfnt_raster): + (struct sfnt_default_uvs_table): + (struct sfnt_unicode_value_range): + (struct sfnt_nondefault_uvs_table): + (struct sfnt_uvs_mapping): + (struct sfnt_mapped_variation_selector_record): + (struct sfnt_table_offset_rec): + (struct sfnt_uvs_context): + (struct sfnt_mapped_table): + (struct sfnt_variation_axis): + (struct sfnt_instance): + (struct sfnt_fvar_table): + (struct sfnt_short_frac_correspondence): + (struct sfnt_short_frac_segment): + (struct sfnt_avar_table): + (struct sfnt_tuple_variation): + (struct sfnt_cvar_table): + (struct sfnt_gvar_table): + (struct sfnt_blend): + (struct sfnt_metrics_distortion): + (PROTOTYPE): Update prototypes. + + * src/sfntfont.c (sfntfont_get_glyph_outline): + (sfntfont_measure_pcm): Adjust calls. + +2023-03-24 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/sfnt.c (sfnt_table_names): Add fvar, gvar, cvar. + (sfnt_read_maxp_table): Call xmalloc, not malloc. + (sfnt_read_simple_glyph): Avoid use-after-free if simple is + invalid. + (sfnt_fill_span): Fix max coverage. + (sfnt_normalize_vector): Fail if magnitude is zero. + (sfnt_measure_distance): Fix opcode order. + (sfnt_dot_fix_14): Fix implementation. + (struct sfnt_variation_axis, struct sfnt_instance) + (struct sfnt_fvar_table, struct sfnt_gvar_table) + (sfnt_read_fvar_table, sfnt_read_gvar_table, struct sfnt_blend) + (sfnt_init_blend, sfnt_free_blend, sfnt_normalize_blend) + (struct sfnt_tuple_header, struct sfnt_gvar_glyph_header) + (sfnt_read_packed_points, sfnt_read_packed_deltas) + (sfnt_compute_tuple_scale, sfnt_infer_deltas_1, sfnt_infer_deltas) + (sfnt_vary_glyph): Add WIP variation glyph implementation. + * src/sfnt.h (enum sfnt_table, struct sfnt_simple_glyph): + Likewise. + +2023-03-20 Po Lu + + * java/INSTALL: Fix typo. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * configure.ac: Add support for HarfBuzz on Android. + * java/INSTALL: Document where to get Emacs with HarfBuzz. + * lisp/subr.el (overriding-text-conversion-style, y-or-n-p): + Correctly set text conversion style if y-or-n-p is called inside + the minibuffer. + * src/sfnt.c (sfnt_read_cmap_format_8) + (sfnt_read_cmap_format_12): Fix typos. + (sfnt_read_24, sfnt_read_cmap_format_14): New function. + (sfnt_read_cmap_table_1, sfnt_read_cmap_table): Handle format 14 + cmap tables. + (sfnt_read_default_uvs_table, sfnt_read_nondefault_uvs_table) + (sfnt_compare_table_offsets, sfnt_create_uvs_context) + (sfnt_free_uvs_context, sfnt_compare_uvs_mapping) + (sfnt_variation_glyph_for_char, sfnt_map_table, sfnt_unmap_table) + (sfnt_read_table, sfnt_test_uvs): New functions. + (main): Add UVS tests. + * src/sfnt.h (struct sfnt_cmap_format_14) + (struct sfnt_variation_selector_record) + (struct sfnt_default_uvs_table, struct sfnt_unicode_value_range) + (struct sfnt_nondefault_uvs_table, struct sfnt_uvs_mapping) + (struct sfnt_mapped_variation_selector_record) + (struct sfnt_table_offset_rec, struct sfnt_uvs_context) + (struct sfnt_mapped_table): New structures. Update prototypes. + * src/sfntfont-android.c (android_sfntfont_driver): Register + HarfBuzz callbacks where required. + * src/sfntfont.c (sfntfont_select_cmap): Look for a format 14 + table. Save it in new arg FORMAT14. + (sfntfont_read_cmap): Adjust accordingly. + (struct sfnt_font_info): New field `uvs'. New fields `hb_font', + `fd' and `directory'. + (sfntfont_open): Open uvs context. Under HarfBuzz, don't close + the fd or subtable, but save them in the font info instead. + (sfntfont_close): Free UVS context. Close font fd and table + directory and HarfBuzz font. + (sfntfont_draw): Handle case where s->padding_p. + (sfntfont_get_variation_glyphs): New function. + (sfntfont_unmap_blob, sfntfont_get_font_table) + (sfntfont_begin_hb_font): New functions. + * src/sfntfont.h: Update prototypes. + * src/textconv.c (Fset_text_conversion_style): Fix doc string. + +2023-03-19 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-18 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsView.java (onAttachedToWindow): Send + measured width and height in exposures again. + + Merge remote-tracking branch 'origin/master' into feature/android + + Remove extraneous debugging code + * src/androidterm.c (handle_one_android_event): Don't log expose + events. + + Work around pselect lossage on Android + * src/android.c (android_run_select_thread): New flag. Use it + rather than the rc of pselect and errno to determine whether or + not it has been interrupted. + (android_handle_sigusr1): Set said flag. + + Update Android port + * java/org/gnu/emacs/EmacsView.java (EmacsView) + (prepareForLayout): New function. Call this prior to mapping + the view. + (onGlobalLayout): New function. Register as global layout + listener. + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow) + (notifyContentRectPosition): New function. Use specified + xPosition and yPosition when reporting the offsets of children + of the root window. + * java/org/gnu/emacs/EmacsWindowAttachmentManager.java + (registerWindow): Specify activity launch bounds if necessary. + * src/androidterm.c (handle_one_android_event): Send + MOVE_FRAME_EVENT where necessary. + +2023-03-17 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Include more information in Android bug reports + * src/androidfns.c (Fx_server_vendor, Fx_server_version): New + functions. + (syms_of_androidfns): Define new functions. + * src/androidterm.c (android_set_build_fingerprint) + (syms_of_androidterm): Set new variable + Vandroid_build_manufacturer. + * src/xfns.c (Fx_server_vendor, Fx_server_version): Update doc + strings. + + Fix WINDOWSNT build of fileio.c and image.c + * src/fileio.c (emacs_fd_to_int): Don't define on WINDOWSNT. + * src/image.c (image_create_bitmap_from_data): Don't abort if + !defined HAVE_ANDROID. + + Update Android port + * configure.ac: + * m4/ndk-build.m4 (ndk_INIT): + (ndk_LATE): Avoid AC_REQUIRE magic. + + Merge remote-tracking branch 'origin/master' into feature/android + + Improve radio button appearance in Android menus + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): + New field `lastGroupId'. + (Item): New field `isRadio'. + (addItem): New arg `isRadio'. + (inflateMenuItems): Apply an empty radio button group if + required. + * src/androidmenu.c (android_init_emacs_context_menu): Adjust + accordingly. + (android_menu_show): Likewise. + + Update Android port + * java/org/gnu/emacs/EmacsView.java (cancelPopupMenu): Dismiss + context menu correctly. + (isOpaque): New function. + * java/org/gnu/emacs/EmacsWindowAttachmentManager.java: Make + consumer list public. + + * configure.ac: Add missing precious variable. + +2023-03-16 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * lisp/frame.el (android-detect-mouse): + * lisp/term/android-win.el (android-get-connection): Add + function declarations. + + * configure.ac: Remove unnecessary escape. + + Make ANDROID_CC and SDK_BUILD_TOOLS precious variables + * configure.ac (AUTO_DEPEND, ANDROID_STUBIFY, ANDROID_LDFLAGS): + * lib/Makefile.in (ANDROID_CFLAGS, ANDROID_BUILD_CFLAGS) + (ALL_CFLAGS): + * lib/gnulib.mk.in (AM_DEFAULT_VERBOSITY): + * msdos/sed1v2.inp: + * msdos/sedlibmk.inp: + * src/Makefile.in (ANDROID_OBJ, EMACS_CFLAGS): Make those + variables precious. Rename ANDROID_CFLAGS substitution to + ANDROID_BUILD_CFLAGS. + + Update Android port + * nt/mingw-cfg.site: Suppress build of gnulib printf. + + Update Android port + * java/org/gnu/emacs/EmacsDocumentsProvider.java (queryRoots): Add + icon to document root. + + Update Android port + * lisp/loadup.el (current-load-list): Set to empty load list + after startup. + * src/lread.c (build_load_history): Revert earlier changes. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-15 Po Lu + + Update Android port + * configure.ac: Improve portability. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-15 Robert Pluim + + Fix typos in Android port + * src/fileio.c (Finsert_file_contents): + * src/window.c (replace_buffer_in_windows): Call Fboundp, not + boundp. + +2023-03-15 Po Lu + + Update Android port + * cross/Makefile.in (lib/gnulib.mk): Edit out + build-aux stuff. + * m4/ndk-build.m4: Also look for cross ranlib. + + * src/sfntfont.c (sfntfont_close): Fix warning w/o mmap. + + Port to systems without endian.h + * lib-src/asset-directory-tool.c (main_2): Port to systems + without htole32. + +2023-03-15 Po Lu + + Update Android port + * configure.ac (XCONFIGURE): Disable NS. + * cross/Makefile.in (lib-src/config.h): + + (lib/libgnu.a): + (src/android-emacs): Port sed invocation to Mac OS without GNU + sed. + +2023-03-15 Po Lu + + Update Android port + * doc/lispref/commands.texi (Misc Events): Document variable + `disable-inhibit-text-conversion'. + * java/org/gnu/emacs/EmacsDialog.java (display1): Try an + activity that is certain to be focused first. + * lisp/touch-screen.el (touch-screen-track-tap) + (touch-screen-track-drag): Bind + `disable-inhibit-text-conversion'. + * src/keyboard.c (read_key_sequence): Only disable text + conversion if an actual function or numeric key is found in the + key sequence. + (syms_of_keyboard): New variable + `disable-inhibit-text-conversion'. + * src/lread.c (read_filtered_event): Check new variable. + * src/textconv.c (textconv_query): Remove unused label. + + Omit gnulib modules added by Android port on MinGW + * nt/gnulib-cfg.mk: Omit new gnulib modules. + +2023-03-14 Po Lu + + Update Android port + * lisp/minibuffer.el (minibuffer-setup-on-screen-keyboard): + Handle cases where last-event-frame is a kbd macro. + * src/keyboard.c (lispy_function_keys): Remove duplicates. + + Fix the MS-DOS build + * msdos/sed1v2.inp: + * msdos/sed3v2.inp: + * msdos/sedlibcf.inp: + * msdos/sedlibmk.inp: Update for Android port and new Gnulib + modules. + + Update Android port + * java/org/gnu/emacs/EmacsWindow.java (figureChange): Detect + mice on up events as well. + (onSomeKindOfMotionEvent): Work past framework bug. + * src/androidterm.c (android_perform_conversion_query): + * src/textconv.c (textconv_query): + * src/textconv.h (TEXTCONV_SKIP_ACTIVE_REGION): Remove unused + code. + + Update Android port + * doc/emacs/android.texi (Android Windowing): Document how to + display dialogs when Emacs is in the background. + * java/org/gnu/emacs/EmacsDialog.java (display1): Use system + dialogs if possible. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-13 Po Lu + + Update Android port + * etc/NEWS: Announce new option. + * lisp/menu-bar.el (menu-bar-close-window): New option. + (kill-this-buffer): + (kill-this-buffer-enabled-p): Adjust accordingly. + * src/keyboard.c (lispy_function_keys): Add more silly + keys. + + Update Android port + * src/android.c (android_check_string, android_build_string): + Reduce consing when building menu bar strings. + + * etc/MACHINES (Android): Update with more recent information. + + Update Android port + * doc/emacs/android.texi (Android Startup): Document changes to + emacsclient wrapper. + * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity) + (startEmacsClient): Open EmacsActivity if the service is not + running. + * java/org/gnu/emacs/EmacsService.java (onCreate): + * java/org/gnu/emacs/EmacsThread.java (EmacsThread, run): Pass + any file to open to Emacs. + * lisp/term/android-win.el (handle-args-function): Implement. + + Update Android port + * src/image.c (image_create_bitmap_from_file, image_find_image_fd) + (close_android_fd, slurp_file): Return and use `struct + android_fd_or_asset' on Android. + (xbm_load, xpm_load, pbm_load, png_load_body, jpeg_load_body) + (webp_load, svg_load): Adjust accordingly. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-12 Po Lu + + Update Android port + * src/android.c (android_get_screen_width): + (android_get_screen_height): + (android_get_mm_width): + (android_get_mm_height): + (android_detect_mouse): Correctly handle Java exceptions. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/android.c (android_check_if_event): + * src/androidgui.h: New function. + * src/androidterm.c (android_event_is_for_frame): New function. + (android_reset_conversion): Free and unqueue all text conversion + events for the given frame. + + Update Android port + * src/androidterm.c (NATIVE_NAME, JNICALL) + (android_build_extracted_text, android_update_selection): Use + 0-based indices for Android buffer positions. Also, report + surrounding text relative to the region, not to the cursor. + * src/textconv.c (textconv_query): Accept new values of + position. + (really_set_composing_text): Use ephemeral last point. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity) + (onCancel): New function. + (displayFailureDialog): Handle dialog cancellation. + * src/sfntfont.c (sfnt_parse_languages): Look for SLNG tag if + DLNG is not present. + + Add Super modifier support to Android port + * src/androidgui.h (enum android_modifier_mask): New modifier + ANDROID_SUPER_MASK. + * src/androidterm.c (android_android_to_emacs_modifiers) + (android_emacs_to_android_modifiers): Add new modifier. + + Fix crash during androidterm init + * src/androidterm.c (syms_of_androidterm): Initialize + Vandroid_build_fingerprint in case GC happens. + + * src/emacs-module.c (module_reset_handlerlist): Fix macro conflict. + + Clean up emacs-module.c + * src/emacs-module.c (MODULE_HANDLE_NONLOCAL_EXIT) + (module_make_global_ref, module_free_global_ref) + (module_make_function, module_get_function_finalizer) + (module_set_function_finalizer, module_make_interactive) + (module_funcall, module_intern, module_type_of) + (module_extract_integer, module_make_integer, module_extract_float) + (module_make_float, module_copy_string_contents) + (module_make_string, module_make_unibyte_string) + (module_make_user_ptr, module_get_user_ptr, module_set_user_ptr) + (module_get_user_finalizer, module_set_user_finalizer) + (module_vec_set, module_vec_get, module_vec_size) + (module_process_input, module_extract_time, module_make_time) + (module_extract_big_integer, module_make_big_integer) + (module_open_channel, module_reset_handlerlist): Adjust as + recommended by Paul Eggert . + + Update Android port + * configure.ac: Take option `--with-shared-user-id' and give it + to AndroidManifest.xml.in. + * java/AndroidManifest.xml.in: Substitute that into the + application info. + * java/INSTALL (BUILDING WITH A SHARED USER ID): New section. + +2023-03-11 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Improve default value of `with_mailutils' on Android + * configure.ac: Default to off on Android. + + * configure.ac: Fix typo. + + Update Android port + * configure.ac (HAVE_MAILUTILS, with_mailutils) + (ANDROID_SDK_8_OR_EARLIER, XCONFIGURE): Fix POP and mailutils + configuration on Android. + * java/Makefile.in: + * src/callproc.c (syms_of_callproc): Avoid using built-in + movemail when --with-mailutils. + + Update Android port + * src/android.c (android_resolve_handle) + (android_resolve_handle2): Don't perform checking done by + CheckJNI by default. + (android_copy_area): Check for errors here because CopyArea can + perform a lot of consing. + (android_define_cursor): Remove redundant code. + + Fix problems with the menu bar on large screen Android devices + * java/org/gnu/emacs/EmacsActivity.java (onContextMenuClosed): + Process submenu closing normally if it happens more than 300 ms + after a submenu item was selected. + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) + (onMenuItemClick, display1): Give `wasSubmenuSelected' different + values depending on how the submenu was selected. + + * lib/gnulib.mk.in: Update from gnulib. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-11 Po Lu + + Don't use GCC extensions in src/emacs-module.c + * configure.ac: Default modules to on. Remove check for + __attribute__((cleanup)). However, keep the new `ifavailable' + value for systems without dlopen. + + * src/emacs-module.c (MODULE_HANDLE_NONLOCAL_EXIT): Don't rely + on cleanup attribute and correctly reset handlerlist upon + longjmp. + (MODULE_INTERNAL_CLEANUP): New macro. + (module_make_global_ref, module_free_global_ref) + (module_make_function, module_get_function_finalizer) + (module_set_function_finalizer, module_make_interactive) + (module_funcall, module_intern, module_type_of) + (module_extract_integer, module_make_integer, module_extract_float) + (module_make_float, module_copy_string_contents) + (module_make_string, module_make_unibyte_string) + (module_make_user_ptr, module_get_user_ptr, module_set_user_ptr) + (module_get_user_finalizer, module_set_user_finalizer) + (module_vec_set, module_vec_get, module_vec_size) + (module_process_input, module_extract_time, module_make_time) + (module_extract_big_integer, module_make_big_integer) + (module_open_channel): Call MODULE_INTERNAL_CLEANUP prior to + returning. + +2023-03-11 Po Lu + + Implement hourglass cursor on Android + * lisp/term/android-win.el (x-pointer-arrow, x-pointer-left-ptr) + (x-pointer-left-side, x-pointer-sb-h-double-arrow) + (x-pointer-sb-v-double-arrow, x-pointer-watch, x-pointer-xterm) + (x-pointer-invisible): New constants. + * src/androidterm.c (android_show_hourglass) + (android_hide_hourglass): New functions. + (android_toggle_visible_pointer, android_define_frame_cursor): + Define or don't define hourglass cursor if x->hourglass. + (android_redisplay_interface): Add new functions. + * src/androidterm.h (struct android_output): New field + `hourglass'. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-10 Po Lu + + Update Android port + * doc/emacs/android.texi (Android Windowing): Document how to + pass multimedia keys to the system. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + function. + * java/org/gnu/emacs/EmacsView.java (onKeyDown, onKeyMultiple) + (onKeyUp): Check that function. + * java/org/gnu/emacs/EmacsWindow.java (defineCursor): Handle + cases where cursor is NULL. + * src/android.c (NATIVE_NAME): New function. + * src/androidfns.c (syms_of_androidfns): New variable. + * src/keyboard.c (lispy_function_keys): Add volume keys. + + * java/org/gnu/emacs/EmacsCursor.java: New file. + + Implement mouse cursors on Android 7.0 and later + * java/org/gnu/emacs/EmacsWindow.java (defineCursor): New + function. + * src/android.c (struct android_emacs_cursor): New struct. + (android_init_emacs_cursor): New function. + (JNICALL): Call it. + (android_create_font_cursor, android_define_cursor) + (android_free_cursor): New functions. + * src/android.h (enum android_handle_type): Add cursor handle + type. + * src/androidfns.c (Fx_create_frame, android_create_tip_frame) + (enum mouse_cursor, struct mouse_cursor_types, mouse_cursor_types) + (struct mouse_cursor_data, android_set_mouse_color) + (syms_of_androidfns): + * src/androidgui.h (enum android_cursor_shape): + * src/androidterm.c (make_invisible_cursor) + (android_toggle_invisible_pointer, android_free_frame_resources) + (android_define_frame_cursor): + * src/androidterm.h (struct android_display_info) + (struct android_output): Port mouse cursor code over from X. + + * java/org/gnu/emacs/EmacsNative.java: Add missing dependency. + + Avoid using Linux sysfs APIs to access battery state on Android + * lisp/battery.el (battery-status-function): Don't look for /sys, + /proc* on Android. Explain why. + + Port Android battery status to Android 4.4 and earlier + * java/org/gnu/emacs/EmacsService.java (EmacsService) + (queryBattery19): New function. + (queryBattery): Call it on old systems. Also, return AC line + status and temperature. + * lisp/battery.el (battery-android): Implement more format + directives. + * src/android.c (android_query_battery): Handle new status + fields. + * src/android.h (struct android_battery_state): Add `plugged' + and `temperature'. + * src/androidfns.c (Fandroid_query_battery): Return new fields. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-09 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/android.c (android_destroy_handle): Handle OOM errors in + android_destroy_handle. + + * textconv.c: Remove out-of-date comment. + +2023-03-09 Po Lu + + Fix menu and popup race conditions on Android + * java/org/gnu/emacs/EmacsActivity.java (onContextMenuClosed): + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) + (onMenuItemClick, run): + * java/org/gnu/emacs/EmacsDialog.java (EmacsDialog, onClick) + (createDialog, onDismiss): Take menu event serial, and pass it + along in context menu events. + * java/org/gnu/emacs/EmacsNative.java (sendContextMenu): New + argument. + * src/android.c (sendContextMenu): Pass serial number in event. + + * src/androidgui.h (struct android_menu_event): New field + `menu_event_serial'. + * src/androidmenu.c (FIND_METHOD_STATIC) + (android_init_emacs_context_menu): Adjust method declarations. + (android_menu_show, android_dialog_show): + * src/androidterm.c (handle_one_android_event): Expect serial in + context menu events. + * src/androidterm.h: Update prototypes. + +2023-03-09 Po Lu + + Fix webp test for Android + * configure.ac (HAVE_WEBP): Disable WebPGetInfo check when + REALLY_ANDROID. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * java/debug.sh (is_root): Port to android versions which don't + support `chmod +x'. + * src/android.c (android_content_name_p): Disable before API + level 19. + + Update Android port + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) + (addItem): New argument `tooltip'. + + Update Android port + * src/android.c (android_build_string): Convert the text to + UTF-16, and create the Java string using that. + (android_build_jstring): Update comment. + * src/androidmenu.c (android_init_emacs_context_menu): Add + String argument to `addItem'. + (android_menu_show): Correctly pass help strings in regular menu + items. + * src/sfnt.c (_sfnt_swap16, _sfnt_swap32): Avoid reserved names. + + Fix crash upon restoring desktop + * src/android.c (android_set_input_focus): Don't call method on + window using service class. + + * src/sfnt.c (ODD): Use PUSH_UNCHECKED. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-08 Po Lu + + Update Android port + * src/fileio.c (Fcopy_file): On Android, ignore ENOSYS and + ENOTSUP when restoring file times, as the system call used is + supported by many kernels. + + Merge remote-tracking branch 'origin/master' into feature/android + + Fix occasional crash + * src/androidterm.c (android_build_extracted_text): Return NULL + if text class not initialized. + (android_update_selection): Check that EXTRACTED is not NULL. + + Update Android port + * doc/emacs/android.texi (Android File System): Document what + `temp~unlinked' means in the temporary files directory. + * java/org/gnu/emacs/EmacsService.java (updateExtractedText): + New function. + * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): + Ask the input method nicely to not display the extracted text + UI. + * src/android.c (struct android_emacs_service): New method + `updateExtractedText'. + (android_hack_asset_fd_fallback): Improve naming convention. + Fix typo. + (android_init_emacs_service): Add new method. + (android_update_extracted_text): New function. + (android_open_asset): Fix typo. + * src/androidgui.h: Update prototypes. + * src/androidterm.c (struct android_get_extracted_text_context): + New field `flags'. + (android_get_extracted_text): Set flags on the frame's output + data. + (android_build_extracted_text): New function. + (getExtractedText): Move out class structures. + (android_update_selection): Send updates to extracted text if + the input method asked for them. + (android_reset_conversion): Clear extracted text flags. + * src/androidterm.h (struct android_output): New fields for + storing extracted text data. + + Fix double free upon encountering invalid font + * src/sfnt.c (sfnt_read_cmap_table): Don't allocate too big + data. Also, free elements of (*data), not offsets into data + itself. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-07 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Save build timestamps in Android builds + * java/Makefile.in (install_temp/assets/build_info): New + rule.:(emacs.apk-in): Depend on that file. + * lisp/version.el (android-read-build-system) + (android-read-build-time): New functions. + (emacs-build-system, emacs-build-time): Use those functions on + Android, as dumping is done after installation on Android. + * src/fileio.c (Finsert_file_contents): + * src/window.c (replace_buffer_in_windows): Don't call functions + if they are not defined, which can happen during loadup. + + Update Android port + * java/org/gnu/emacs/EmacsWindow.java (onSomeKindOfMotionEvent): + Dismiss splurious LeaveNotify events from button presses. + * src/android.c (android_change_window_attributes) + (android_change_gc, android_set_clip_rectangles) + (android_reparent_window, android_clear_window, android_map_window) + (android_unmap_window, android_resize_window, android_move_window) + (android_swap_buffers, android_fill_rectangle, android_copy_area) + (android_fill_polygon, android_draw_rectangle, android_draw_point) + (android_draw_line, android_clear_area, android_bell) + (android_set_input_focus, android_raise_window) + (android_lower_window, android_set_dont_focus_on_map) + (android_set_dont_accept_focus, android_get_keysym_name) + (android_toggle_on_screen_keyboard, android_restart_emacs) + (android_display_toast, android_update_ic, android_reset_ic) + (android_set_fullscreen): Optimize by specifying the class + explicitly when calling a method. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/lread.c (lread_fd, file_tell, infile, skip_dyn_bytes) + (skip_dyn_eof, readbyte_from_stdio, safe_to_load_version) + (close_infile_unwind, close_file_unwind_android_fd): New + function. + (Fload, Flocate_file_internal, openp): New argument PLATFORM. + All callers changed. + (skip_lazy_string): Add optimized versions of various functions + for accessing Android assets. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-06 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + function requestSelectionUpdate. + * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): + Call it instead of losing if getting the current selection + fails. + * src/android-asset.h (AAsset_seek): Define stub. + * src/android.c (android_open): Take mode_t. + (android_open_asset, android_close_asset, android_asset_read_quit) + (android_asset_read, android_asset_lseek, android_asset_fstat): + New functions. + * src/android.h (struct android_fd_or_asset): Update prototypes. + * src/androidgui.h (enum android_ime_operation): Add new + operation to update the selection position. + * src/androidterm.c (android_handle_ime_event): Handle new + operation. + (requestSelectionUpdate): New function. + * src/fileio.c (close_file_unwind_emacs_fd): New function. + (Fcopy_file, union read_non_regular, read_non_regular) + (Finsert_file_contents): Use optimized codepath to insert + Android asset files. + * src/frame.h (enum text_conversion_operation): New operation. + * src/textconv.c (really_request_point_update) + (handle_pending_conversion_events_1, request_point_update): New + functions. + * src/textconv.h: Update prototypes. + + * src/conf_post.h: Avoid macro redeclaration. + + Update Android port + * java/org/gnu/emacs/EmacsService.java (sync): Delete function. + * java/org/gnu/emacs/EmacsView.java (handleDirtyBitmap): Erase + with window background. + (onDetachedFromWindow): Only recycle bitmap if non-NULL. + * java/org/gnu/emacs/EmacsWindow.java (background): New field. + (changeWindowBackground): Set it. + * src/android.c (struct android_emacs_service): Remove `sync'. + (android_init_emacs_service): Likewise. + (android_sync): Delete function. + * src/androidfns.c (android_create_tip_frame): Set frame + background color correctly. + (Fx_show_tip): Make the tip frame visible. + * src/androidgui.h: Update prototypes. + * src/androidterm.c (handle_one_android_event): Handle tooltip + movement correctly. + +2023-03-05 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * java/org/gnu/emacs/EmacsActivity.java (onCreate): + * java/org/gnu/emacs/EmacsContextMenu.java: + * java/org/gnu/emacs/EmacsDocumentsProvider.java (getMimeType): + * java/org/gnu/emacs/EmacsDrawLine.java (perform): + * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): + * java/org/gnu/emacs/EmacsFillPolygon.java: + * java/org/gnu/emacs/EmacsFontDriver.java: + * java/org/gnu/emacs/EmacsHandleObject.java: + * java/org/gnu/emacs/EmacsInputConnection.java: + * java/org/gnu/emacs/EmacsMultitaskActivity.java + (EmacsMultitaskActivity): + * java/org/gnu/emacs/EmacsNative.java: + * java/org/gnu/emacs/EmacsNoninteractive.java + (EmacsNoninteractive, main): + * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity) + (startEmacsClient): + * java/org/gnu/emacs/EmacsSdk7FontDriver.java: + * java/org/gnu/emacs/EmacsSdk8Clipboard.java: + * java/org/gnu/emacs/EmacsService.java (EmacsService, onCreate): + * java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout): + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): + * java/org/gnu/emacs/EmacsWindowAttachmentManager.java + (EmacsWindowAttachmentManager): Remove redundant includes. + Reorganize some functions around, remove duplicate `getLibDir' + functions, and remove unused local variables. + + Update Android port + * java/org/gnu/emacs/EmacsOpenActivity.java (onCreate): Don't + set the style here. + * java/res/values-v11/style.xml: + * java/res/values-v14/style.xml: + * java/res/values-v29/style.xml: + * java/res/values/style.xml: Define styles for the emacsclient + wrapper. + * src/keyboard.c (read_key_sequence): Don't disable text + conversion if use_mouse_menu or if a menu bar prefix key is + being displayed. + + Update Android port + * etc/PROBLEMS: Document problem with default monospace font. + * src/fileio.c (check_mutable_filename): Check /content as well. + (Fcopy_file, Fdelete_directory_internal, Fdelete_file) + (Frename_file, Fadd_name_to_file, Fmake_symbolic_link) + (Fset_file_modes, Fset_file_times, Ffile_newer_than_file_p) + (write_region): Adjust accordingly. + (Fset_visited_file_modtime): Remove unnecessary restriction. + * src/filelock.c (make_lock_file_name): Don't interlock files + under /assets and /content. + * src/inotify.c (Finotify_add_watch): Fix typo. + + Fix cross compilation of cross/lib in some cases + * cross/Makefile.in: (config.status): Depend on + top_builddir/config.status instead. + + * configure.ac: Fix another typo. + + * cross/Makefile.in (builddir): Define. + + * cross/README: Update. + + Remove redundant gnulib files + * cross/lib: Delete. Make configure generate it instead. + + Remove redundant second copy of gnulib + * .gitignore: Simplify cross/lib rule. + * admin/merge-gnulib (avoided_flags): Stop copying to cross/lib. + * configure.ac: Link gnulib source and header files to + cross/lib. + * cross/Makefile.in (LIB_SRCDIR): Make relative to builddir. + (maintainer-clean): Merge with distclean. Remove links created + by configure. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-04 Po Lu + + Fix x86_64 builds of libjpeg on Android + * cross/ndk-build/ndk-build-shared-library.mk: + * cross/ndk-build/ndk-build-static-library.mk: Specify right ELF + format for 64 bit executables. + + Fix calls to nasm in cross/ndk-build + * cross/ndk-build/ndk-build-shared-library.mk: + * cross/ndk-build/ndk-build-static-library.mk: Ensure nasm + generates ELF objects. + + Merge remote-tracking branch 'origin/master' into feature/android + + Fix out of bound write after poly of single pixel span + * src/sfnt.c (sfnt_fill_span): Specifically handle spans that + span a single pixel by computing the coverage in the center. + + Improve context menus on old versions of Android + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New + field `lastClosedMenu'. + (onContextMenuClosed): Don't send event if a menu is closed + twice in a row. Also, clear wasSubmenuSelected immediately. + * java/org/gnu/emacs/EmacsContextMenu.java: Display submenus + manually in Android 6.0 and earlier. + * java/org/gnu/emacs/EmacsView.java (onCreateContextMenu) + (popupMenu): Adjust accordingly. + + Port to broken Android NDK version + * configure.ac: Check for __ctype_get_mb_cur_max. + Then see if MB_CUR_MAX is defined to it, and define + REPLACEMENT_MB_CUR_MAX if so and it does not link. + * java/INSTALL: Update documentation. + * src/conf_post.h (MB_CUR_MAX): Define replacement if + necessary. + + * m4/ndk-build.m4 (ndk_INIT): Fix typo. + + Merge remote-tracking branch 'origin/master' into feature/android + + Improve support for building Android C++ dependencies + * configure.ac: Call ndk_LATE after gl_EARLY. + * cross/ndk-build/Makefile.in (NDK_BUILD_CXX): New variable. + * cross/ndk-build/ndk-build-shared-library.mk: + * cross/ndk-build/ndk-build-static-library.mk: Use it. + * java/INSTALL: Describe how to build C++ dependencies. + * m4/ndk-build.m4 (ndk_LATE): New macro. + (ndk_INIT): Try to find a suitable C++ compiler. + (ndk_CHECK_MODULES): Make sure the C++ compiler works before + allowing C++ dependencies. + + Fix cross-compilation of C++ code with old NDK versions + * cross/ndk-build/Makefile.in (NDK_BUILD_CFLAGS_CXX): New variable. + * cross/ndk-build/ndk-build-shared-library.mk + ($(call objname,$(LOCAL_MODULE),$(basename $(1)))): + * cross/ndk-build/ndk-build-static-library.mk + ($(call objname,$(LOCAL_MODULE),$(basename $(1)))): Use it to build + C++ code. + +2023-03-03 Po Lu + + Minor fixes to configury + * configure.ac (ANDROID_SDK_8_OR_EARLIER): Pass through + `--with-ndk-cxx-shared'. + * m4/ndk-build.m4: Fix quoting of $CC. + + Fix out-of-tree builds with native dependencies + * cross/ndk-build/ndk-build-shared-library.mk: + * cross/ndk-build/ndk-build-static-library.mk: Include + ndk-resolve.mk in srcdir. + + * cross/ndk-build/README: Update accordingly. + + Improve ndk-build implementation + * build-aux/ndk-build-helper.mk: Define in terms of BUILD_AUXDIR. + * m4/ndk-build.m4 (ndk_INIT): Find right build-aux directory. + Remove uses of unportable shell constructs. + + Fix visiting and saving writable content provider files + * java/org/gnu/emacs/EmacsService.java (checkContentUri): + Improve debug output. + * lisp/files.el (basic-save-buffer): Check whether or not file + itself exists before checking for the existence of the directory + containing it. + * src/android.c (android_open): Don't forget to set errno after + open_content_uri fails. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-03 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity) + (onCreate): Add view tree observer. + (onGlobalLayout): Sync fullscreen state. + (syncFullscreenWith): Improve visibility flag setting. + + * src/textconv.c (select_window): New function. + (textconv_query): + (restore_selected_window): + (really_commit_text): + (really_set_composing_text): + (really_set_composing_region): + (really_delete_surrounding_text): + (really_set_point_and_mark): + (get_extracted_text): Call it instead of Fselect_window + to avoid selecting the mini window if it is no longer active. + +2023-03-03 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-02 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + + * doc/emacs/input.texi (On-Screen Keyboards): Fix indexing. + + Summary: Update Android port + * INSTALL: Document where to find Android installation + instructions. + * configure.ac (CHECK_LISP_OBJECT_TYPE): Pacify + -Wsuggest-attribute=noreturn only on Android. + * cross/ndk-build/README: New file. + * doc/emacs/android.texi (Android): + * doc/emacs/emacs.texi (Top): + * doc/emacs/input.texi (Other Input Devices): Untabify menus. + * etc/NEWS: Move INSTALL.android to java/INSTALL. + * java/INSTALL: New file. + * java/README: + * src/coding.c (from_unicode_buffer): Make Android specific code + only build on Android. + + * INSTALL.android: Remove file. + + Fix Makefile race conditions + * configure.ac: Make cross/* and related directories. + * cross/Makefile.in (src/verbose.mk, lib/libgnu.a) + (src/config.h): Stop making directories here. + (lib-src/config.h): New config.h rule. + ($(LIBSRC_BINARIES)): Add it. + (clean): Don't remove CLEAN_SUBDIRS, but clean inside. + + Fix Android handle wraparound + * src/android.c (android_alloc_id): Return correct values upon + wraparound. + +2023-03-02 Po Lu + + Improve criteria for restoring fullscreen state on Android + * java/Makefile.in ($(CLASS_FILES) &): Touch all class files, + even those javac chose not to rebuild. + + * java/org/gnu/emacs/EmacsActivity.java (onWindowFocusChanged): + Restore fullscreen state here. + (onResume): And not here. + +2023-03-02 Po Lu + + Fix sectioning of android texi files + * doc/emacs/android.texi (Android): + * doc/emacs/emacs.texi (Top, GNU Free Documentation License): + Rearrange menu and sectioning. + + Update Android port + * doc/emacs/android.texi (Android Windowing): Reword + documentation. + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): + * java/org/gnu/emacs/EmacsFontDriver.java (EmacsFontDriver): + * java/org/gnu/emacs/EmacsSdk7FontDriver.java + (EmacsSdk7FontDriver): + * java/org/gnu/emacs/EmacsService.java (queryBattery): + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): Make + functions final and classes static where necessary. + * src/android.c (struct android_emacs_service): New method + `display_toast'. + (android_init_emacs_service): Load new method. + (android_display_toast): New function. + * src/android.h: Export. + * src/androidfns.c (Fandroid_detect_mouse): + * src/androidselect.c (Fandroid_clipboard_owner_p) + (Fandroid_set_clipboard, Fandroid_get_clipboard) + (Fandroid_browse_url): Prevent crashes when called from + libandroid-emacs.so. + * src/androidterm.c (handle_one_android_event): Fix out of date + commentary. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-03-01 Po Lu + + Fix out-of-tree Android builds + * configure.ac (JAVA_PUSH_LINT): Push to WARN_JAVAFLAGS instead + of JAVAFLAGS. + (cross/lib): Always AS_MKDIR_P. + * cross/Makefile.in (srcdir): New variable. + (LIB_SRCDIR): Take realpath relative to srcdir, not + .:(src/verbose.mk): Depend on verbose.mk.android in srcdir. + (lib/Makefile): Edit srcdir and VPATH to LIB_SRCDIR. + (src/Makefile): Edit -I$$(top_srcdir) to -I../$(srcdir)/lib, + instead of ommitting it. + (clean): Allow ndk-build clean to fail. + + * java/Makefile.in (builddir): New variable. + (WARN_JAVAFLAGS): Likewise. + (JAVAFLAGS): Define in terms of WARN_JAVAFLAGS. + (SIGN_EMACS, SIGN_EMACS_V2): Use emacs.keystore relative to + srcdir. Allow inclusion of ndk-build.mk to fail. + (install_temp, emacs.apk-in) + (../config.status): Depend relative to top_srcdir. + (AndroidManifest.xml, $(APK_NAME)): Likewise. + (RESOURCE_FILE, CLASS_FILES, classes.dex): Output class files + to $(srcdir); these are arch independents, so this is okay. + +2023-03-01 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Fix mostlyclean rules + * cross/Makefile.in: Remove outdated comment. + * src/Makefile.in: (.PHONY): Clean android-emacs and + libemacs.so, not emacs.so and aemacs. + + Update Android port + * doc/emacs/android.texi (Android File System): Document new + behavior of starting a subprocess from /assets. + * java/org/gnu/emacs/EmacsWindow.java (onSomeKindOfMotionEvent): + Don't use isFromSource where not present. + * src/androidterm.c (android_scroll_run): Avoid undefined + behavior writing to bitfields. + * src/callproc.c (get_current_directory): When trying to run a + subprocess inside /assets, run it from the home directory + instead. + + Update Android port + * java/AndroidManifest.xml.in: Specify @style/EmacsStyle. + * java/org/gnu/emacs/EmacsActivity.java (onCreate): Stop setting + the theme here. + * java/res/values-v11/style.xml: + * java/res/values-v14/style.xml: + * java/res/values-v29/style.xml: + * java/res/values/style.xml: Extract style resources into + res/values. + + Update Android port + * java/Makefile.in (ETAGS, clean): New rules to generate tags. + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): + * java/org/gnu/emacs/EmacsApplication.java (EmacsApplication): + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): + * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea): + * java/org/gnu/emacs/EmacsDialog.java (EmacsDialog)::(dialog. + Then): + * java/org/gnu/emacs/EmacsDocumentsProvider.java + (EmacsDocumentsProvider): + * java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine): + * java/org/gnu/emacs/EmacsDrawPoint.java (EmacsDrawPoint): + * java/org/gnu/emacs/EmacsDrawRectangle.java + (EmacsDrawRectangle): + * java/org/gnu/emacs/EmacsFillPolygon.java (EmacsFillPolygon): + * java/org/gnu/emacs/EmacsFillRectangle.java + (EmacsFillRectangle): + * java/org/gnu/emacs/EmacsGC.java (EmacsGC): + * java/org/gnu/emacs/EmacsInputConnection.java + (EmacsInputConnection): + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): + * java/org/gnu/emacs/EmacsNoninteractive.java + (EmacsNoninteractive): + * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity): + * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): + * java/org/gnu/emacs/EmacsPreferencesActivity.java + (EmacsPreferencesActivity): + * java/org/gnu/emacs/EmacsSdk11Clipboard.java + (EmacsSdk11Clipboard): + * java/org/gnu/emacs/EmacsSdk23FontDriver.java + (EmacsSdk23FontDriver): + * java/org/gnu/emacs/EmacsSdk8Clipboard.java + (EmacsSdk8Clipboard): + * java/org/gnu/emacs/EmacsService.java (EmacsService): + * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView) + (buffers): + * java/org/gnu/emacs/EmacsView.java (EmacsView, ViewGroup): + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, drawables): + * java/org/gnu/emacs/EmacsWindowAttachmentManager.java + (EmacsWindowAttachmentManager): Make classes final where + appropriate. + + More fixes to JNI error checking + * src/android.c (android_query_tree, android_get_geometry) + (android_translate_coordinates, android_query_battery): + Correctly handle result of GetTArrayElements. + (android_exception_check_nonnull): New function. + * src/android.h: + * src/androidselect.c (Fandroid_get_clipboard): Likewise. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-28 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/sfnt.c (main): + * src/sfntfont.c (sfntfont_get_glyph_outline): Remove outdated + comment. + +2023-02-26 Po Lu + + Get rid of android_lookup_method + * src/android.c (struct android_emacs_window): New methods. + (android_init_emacs_window): Add new methods. + (android_lookup_method): Delete now-unused function. + (android_change_window_attributes, android_reparent_window) + (android_map_window, android_unmap_window, android_resize_window) + (android_move_window, android_set_input_focus) + (android_raise_window, android_lower_window, android_get_geometry) + (android_translate_coordinates, android_set_dont_focus_on_map) + (android_set_dont_accept_focus): Don't look up the class and + method each time when calling a function; that's just waste. + + Update from gnulib + * cross/lib/unistd.in.h: + * lib/gnulib.mk.in (INT64_MAX_EQ_LONG_MAX): + * m4/gnulib-comp.m4 (gl_EARLY): Update from gnulib. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * doc/lispref/commands.texi (Misc Events): Update documentation. + * java/org/gnu/emacs/EmacsService.java (EmacsService) + (onStartCommand): Improve notification message. + * src/android.c (android_hack_asset_fd): Detect if ashmem is + available dynamically. + (android_detect_ashmem): New function. + * src/textconv.c (record_buffer_change): Use markers to + represent BEG and END instead. + (syms_of_textconv): Update doc string. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-25 Po Lu + + Update Android port + * java/debug.sh (is_root): Fix tee detection again for old + systems which don't return exit codes from adb shell. + * src/android.c (android_run_select_thread, NATIVE_NAME, + JNICALL): + * src/android.h (NATIVE_NAME): + * src/androidterm.c (JNICALL, NATIVE_NAME): Apply stack + alignment to all JNICALL functions. + + * src/android.c (android_open): Clean up unused variables. + + Update Android port + * java/org/gnu/emacs/EmacsNoninteractive.java (main): Port to + Android 2.2. + * src/android-asset.h (AAsset_openFileDescriptor): Delete stub + function. + * src/android.c (android_check_compressed_file): Delete + function. + (android_open): Stop trying to find compressed files or to use + the system provided file descriptor. Explain why. + + Update Android port + * doc/emacs/android.texi (Android Startup, Android File System) + (Android Environment, Android Windowing, Android + Troubleshooting): Improve documentation; fix typos. + * doc/lispref/commands.texi (Misc Events): Likewise. + * java/org/gnu/emacs/EmacsService.java (queryBattery): New + function. + * lisp/battery.el (battery-status-function): Set appropriately + for Android. + (battery-android): New function. + * src/android.c (struct android_emacs_service): New method + `query_battery'. + (android_check_content_access): Improve exception checking. + (android_init_emacs_service): Look up new method. + (android_destroy_handle, android_create_window) + (android_init_android_rect_class, android_init_emacs_gc_class) + (android_set_clip_rectangles) + (android_create_pixmap_from_bitmap_data, android_fill_polygon) + (android_get_image, android_put_image, android_bell) + (android_set_input_focus, android_raise_window) + (android_lower_window, android_query_tree, android_get_geometry) + (android_translate_coordinates, android_wc_lookup_string) + (android_damage_window, android_build_string) + (android_build_jstring, android_exception_check_1) + (android_exception_check_2): New functions. + (android_browse_url): Improve exception handling. Always use + android_exception_check and don't leak local refs. + (android_query_battery): New function. + * src/android.h (struct android_battery_state): New struct. + * src/androidfns.c (Fandroid_query_battery, syms_of_androidfns): + New function. + * src/androidfont.c (androidfont_from_lisp, DO_SYMBOL_FIELD) + (DO_CARDINAL_FIELD, androidfont_list, androidfont_match) + (androidfont_draw, androidfont_open_font) + (androidfont_close_font): + * src/androidselect.c (Fandroid_set_clipboard) + (Fandroid_get_clipboard): + * src/sfnt.c (sfnt_map_glyf_table): + * src/sfntfont.c (sfntfont_free_outline_cache) + (sfntfont_free_raster_cache, sfntfont_close): Allow font close + functions to be called twice. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-24 Po Lu + + Improve Android configury + * configure.ac (JAVA_PUSH_LINT): New macro. + (JAVAFLAGS): New variable. Check for various lint flags and + macros and enable them. + * java/Makefile.in (ANDROID_ABI): + * java/org/gnu/emacs/EmacsSdk7FontDriver.java: Remove compiler + warning. + + Enable normal-erase-is-backspace on Android + * lisp/frame.el (display-symbol-keys-p): + * lisp/simple.el (normal-erase-is-backspace-setup-frame): Return + appropriate values on Android. + + Merge remote-tracking branch 'origin/master' into feature/android + + Fix auto-revert-mode on Android + * src/inotify.c (Finotify_add_watch): Handle asset files. + + * src/keyboard.c (lispy_function_keys): Add missing delete key. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-23 Po Lu + + Make sure scroll-bar.el is loaded on Android + * lisp/loadup.el: Update commentary. + * src/androidterm.c (syms_of_androidterm): Define + Vx_toolkit_scroll_bars. + * src/xterm.c (syms_of_xterm): Update doc string.xf64 + + Merge remote-tracking branch 'origin/master' into feature/android + + Fix ImageMagick build on Android + * INSTALL.android (-linux_arm_sources): + * build-aux/ndk-build-helper-1.mk: + (NDK_$(LOCAL_MODULE)_STATIC_LIBRARIES)::(NDK_CXX_FLAG_$(LOCAL_MODULE)): + * build-aux/ndk-build-helper-2.mk: + (NDK_$(LOCAL_MODULE)_STATIC_LIBRARIES)::(NDK_CXX_FLAG_$(LOCAL_MODULE)): + * cross/ndk-build/ndk-build-shared-library.mk (objname)::($(call + objname,$(LOCAL_MODULE),$(basename + $(1))))::(ALL_OBJECT_FILES$(LOCAL_MODULE)): + * cross/ndk-build/ndk-build-static-library.mk (objname)::($(call + objname,$(LOCAL_MODULE),$(basename + $(1))))::(ALL_OBJECT_FILES$(LOCAL_MODULE)): + (ALL_SOURCE_FILES): Update ImageMagick build instructions and + C++ module detection. + + * src/android.c (android_run_select_thread): Fix typos. + + Make android_select more robust + * src/android.c (android_run_select_thread): Lock select_mutex + before signalling condition variable. + (android_select): Unlock event queue mutex prior to waiting for + it. + +2023-02-22 Po Lu + + ; Fix typo + * cross/ndk-build/ndk-build-shared-library.mk: Fix typo. + + * src/image.c (imagemagick_load_image): Check HAVE_DECL_xxx. + +2023-02-22 Po Lu + + Update Android port + ImageMagick now builds but does not link yet some of the time. + + * INSTALL.android: Document ImageMagick and caveats. + * build-aux/ndk-build-helper-1.mk (NDK_SO_NAMES): + * build-aux/ndk-build-helper-2.mk (NDK_A_NAMES): + * build-aux/ndk-build-helper.mk (TARGET_ARCH_ABI): Define architecture + and don't respect explicitly specified library names. + * configure.ac: Enable ImageMagick and lcms2 on Android. + * cross/ndk-build/ndk-build-shared-library.mk (objname)::($(call + objname,$(LOCAL_MODULE),$(basename + $(1))))::(ALL_OBJECT_FILES$(LOCAL_MODULE)): + * cross/ndk-build/ndk-build-static-library.mk (objname)::($(call + objname,$(LOCAL_MODULE),$(basename $(1)))): + (NDK_CFLAGS, ALL_SOURCE_FILES): Handle sources files which start with + $(LOCAL_PATH). + * cross/ndk-build/ndk-clear-vars.mk: Don't undefine; clear variables + instead. + * m4/ndk-build.m4 (ndk_SEARCH_MODULE): Redirect make stderr to + config.log.xf64 + +2023-02-22 Po Lu + + * src/androidmenu.c (android_menu_show): Fix typo. + + Update Android port + * doc/emacs/input.texi (On-Screen Keyboards): Document changes + to text conversion. + * java/org/gnu/emacs/EmacsInputConnection.java (getExtractedText) + (EmacsInputConnection): + * src/keyboard.c (read_key_sequence): Disable text conversion + after reading prefix key. + * src/textconv.c (get_extracted_text): Fix returned value when + request length is zero. + + Merge remote-tracking branch 'origin/master' into feature/android + + Add cross-compilation test for cleanup attribute + * configure.ac: Per title. + + Update Android port + * INSTALL.android: Port to MIPS. + * configure.ac (modules): Default to ifavailable. + Write actual test for __attribute__((cleanup)). + * m4/ndk-build.m4: Recognize mips and mips64. + * src/emacs-module.c: Remove broken HAS_ATTRIBUTE test. + +2023-02-21 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) + (addSubmenu, inflateMenuItems): Handle tooltips correctly. + * src/android.c (android_scan_directory_tree): Fix limit + generation for root directory. + * src/androidmenu.c (android_init_emacs_context_menu) + (android_menu_show): Implement menu item help text on Android + 8.0 and later. + + Update from gnulib + * admin/merge-gnulib (GNULIB_MODULES): + * cross/lib/getopt-pfx-core.h (optind): + * cross/lib/limits.in.h (BOOL_WIDTH): + * cross/lib/math.in.h: + * cross/lib/stpncpy.c (__stpncpy): + * cross/lib/string.in.h: + * lib/getopt-pfx-core.h (optind): + * lib/gnulib.mk.in (ANDROID_MIN_SDK): + (GL_COND_OBJ_STDIO_READ_CONDITION): + (LIBS): + (NDK_BUILD_AR): + (REPLACE__EXIT): + (libgnu_a_SOURCES): + * lib/limits.in.h (BOOL_WIDTH): + * lib/math.in.h: + * lib/stpncpy.c (__stpncpy): + * lib/string.in.h: + * m4/assert_h.m4 (gl_ASSERT_H): + * m4/fdopendir.m4 (gl_FUNC_FDOPENDIR): + * m4/getdelim.m4 (gl_FUNC_GETDELIM): + * m4/getline.m4 (gl_FUNC_GETLINE): + * m4/gnulib-common.m4 (gl_COMMON_BODY): + (gl_CONDITIONAL_HEADER): + (gl_CHECK_FUNCS_ANDROID): + * m4/gnulib-comp.m4 (gl_EARLY): + (gl_INIT): + (gl_FILE_LIST): + * m4/limits-h.m4: + * m4/lstat.m4 (gl_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK): + * m4/malloc.m4 (_AC_FUNC_MALLOC_IF): + * m4/printf.m4 (gl_PRINTF_SIZES_C99): + (gl_PRINTF_INFINITE): + (gl_PRINTF_INFINITE_LONG_DOUBLE): + (gl_PRINTF_DIRECTIVE_A): + (gl_PRINTF_DIRECTIVE_F): + (gl_PRINTF_FLAG_ZERO): + (gl_SNPRINTF_PRESENCE): + (gl_SNPRINTF_DIRECTIVE_N): + (gl_VSNPRINTF_ZEROSIZE_C99): + * m4/pselect.m4 (gl_FUNC_PSELECT): + * m4/readlink.m4 (gl_FUNC_READLINK): + * m4/realloc.m4 (_AC_FUNC_REALLOC_IF): + * m4/signbit.m4 (gl_SIGNBIT): + * m4/stpncpy.m4 (gl_FUNC_STPNCPY): + * m4/symlink.m4 (gl_FUNC_SYMLINK): Add gnulib module stpncpy. + * src/android.c: Include string.h. + + Update Android port + * doc/emacs/android.texi (Android Startup): Document `content' + special directory. + * java/debug.sh (is_root): Improve /bin/tee detection. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + function `dup'. + * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity) + (checkReadableOrCopy, onCreate): Create content directory names + when the file is not readable. + * java/org/gnu/emacs/EmacsService.java (EmacsService) + (openContentUri, checkContentUri): New functions. + * src/android.c (struct android_emacs_service): New methods. + (android_content_name_p, android_get_content_name) + (android_check_content_access): New function. + (android_fstatat, android_open): Implement opening content URIs. + (dup): Export to Java. + (android_init_emacs_service): Initialize new methods. + (android_faccessat): Implement content file names. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-20 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-20 Po Lu + + Update Android port + * INSTALL.android: Explain where to get tree-sitter. + + * configure.ac: Add support for dynamic modules and tree-sitter. + + * doc/emacs/android.texi (Android Windowing): + * java/org/gnu/emacs/EmacsSdk11Clipboard.java + (EmacsSdk11Clipboard, ownsClipboard): Improve clipboard handling + and documentation. + +2023-02-20 Po Lu + + Fix crash inside font-list-family + * src/androidfont.c (androidfont_list_family): Don't + unconditionally initialize the Android font driver. + + Merge remote-tracking branch 'origin/master' into feature/android + + Improve SFNT driver lookup efficiency + * src/fontset.c (fontset_find_font): Add compatibility test to + registry strangeness case. + * src/sfnt.c (sfnt_read_cmap_table): Don't read subtable data if + DATA is NULL. + * src/sfntfont.c (struct sfnt_font_desc): New field `registry'. + (sfnt_registry_for_subtable): New function. + (sfntfont_identify_cmap): Move above sfnt_grok_registry. + (sfnt_grok_registry): New function. + (sfnt_enum_font_1): Call it. + (sfntfont_registries_compatible_p): New function. + (sfntfont_list_1): Check registry compatibility. + (sfntfont_registry_for_desc): New function. + (mark_sfntfont): Mark desc->registry. + + Improve reliability of Java code rebuilds + * java/Makefile.in ($(CLASS_FILES)): Depend on the Java + compiler's internal dependency tracking. + +2023-02-19 Po Lu + + Match font registry after font is opened + * src/fontset.c (fontset_find_font): Work around TrueType + performance problem. + + Merge remote-tracking branch 'origin/master' into feature/android + + * cross/Makefile.in (src/libemacs.so): Depend on libgnu.a. + + More fixes to parallel Make + * cross/ndk-build/ndk-build.mk.in (NDK_BUILD_MODULES) + (NDK_BUILD_SHARED, NDK_BUILD_STATIC): Define group rule to build + all files so that they are built within one make process. + * java/Makefile.in: Reorganize cross compilation and make sure + there is only one make subprocess for each subdirectory of + cross. + + More parallel build fixes + * cross/Makefile.in: (.PHONY): + * java/Makefile.in: (.PHONY): + * src/Makefile.in: (libemacs.so): Avoid calling ndk-build from + two places at once. Build android-emacs separately from + libemacs.so. + + Fix parallel compilation of Android port + * cross/Makefile.in ($(top_builddir)/lib/libgnu.a): + * java/Makefile.in (CROSS_LIBS): Explicitly depend on gnulib + to prevent it from being built at the same time from different + jobs. + + Fix sfntfont.c build without mmap + * src/sfntfont.c (sfntfont_close): Don't unlink font if mmap is + not available. + + Improve Android documentation + * INSTALL.android: Say where building Emacs is supported. + * doc/emacs/android.texi (Android Startup): Describe how to + connect via ADB. + + Report both sides of the region to the input method upon setup + * java/org/gnu/emacs/EmacsNative.java (getSelection): Return + array of ints. + * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): + Adjust accordingly. + * src/androidterm.c (struct android_get_selection_context): New + field `mark'. + (android_get_selection): Set the mark field as appropriate. + (getSelection): Adjust accordingly. + + Fix gamegrid.el with high resolution displays + * lisp/play/gamegrid.el (gamegrid-setup-default-font): Clamp + font size at eight. + + Allow opening more files in emacsclient on Android + * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity) + (checkReadableOrCopy): New function. + (onCreate): If the file specified is not readable from C, read + it into a temporary file and ask Emacs to open that. + + Implement `fullscreen' on Android 4.0 and later + * doc/emacs/android.texi (Android Windowing): Document what new + frame parameters are now supported. + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New + field `isFullscreen'. + (detachWindow, attachWindow): Sync fullscreen state. + (onWindowFocusChanged): Add more logging. + (onResume): Restore previous fullscreen state. + (syncFullscreen): New function. + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow) + (setFullscreen): New function. + * src/android.c (struct android_emacs_window): Add new method. + (android_init_emacs_window): Look up new method. + (android_set_fullscreen): New function. + * src/androidgui.h: + * src/androidterm.c (android_fullscreen_hook): Implement + accordingly. + + Fix crashes in desktop-save-mode + * lisp/subr.el (overriding-text-conversion-style, y-or-n-p): + Disable text conversion when reading from minibuffer. + * src/androidfns.c (android_make_monitor_attribute_list): New + function. + (Fandroid_display_monitor_attributes_list): Call it to set + monitor_frames, which avoids a NULL pointer dereference. + Reported by Angelo Graziosi . + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-18 Po Lu + + * lisp/loadup.el: Fix merge typos. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * doc/emacs/input.texi (On-Screen Keyboards): Document + `touch-screen-always-display'. + * doc/lispref/commands.texi (Misc Events): Improve documentation + of text conversion events. + * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog, display1): + Reorder buttons to make more sense. + * lisp/elec-pair.el (electric-pair-analyze-conversion): New + function. + * lisp/simple.el (analyze-text-conversion): Improve integration + with electric pair modes. + * lisp/term.el (term-mode): Always display the onscreen + keyboard. + * lisp/touch-screen.el (touch-screen-display-keyboard) + (touch-screen-handle-point-up): Respect new options. + * src/textconv.c (really_set_composing_text): Stop widenining + unnecessarily. + (really_delete_surrounding_text): Really delete surrounding + text. Give text conversion analyzers the buffer text. + (syms_of_textconv): Update doc string. + + Notify input methods when editing fails + * INSTALL.android: Clarify build instructions. + * src/textconv.c (struct complete_edit_check_context): New + structure. + (complete_edit_check): New function. + (handle_pending_conversion_events_1): If the window is known, + then ensure that any editing failures are reported to the input + method. + + * configure.ac: Fix typo. + + Update Android port + * configure.ac: Check for madvise. + * lisp/international/fontset.el (script-representative-chars): + Improve detection of CJK fonts. + * src/pdumper.c (dump_discard_mem): Use madvise if possible. + * src/sfnt.c (sfnt_map_glyf_table, sfnt_unmap_glyf_table): New + functions. + * src/sfnt.h (struct sfnt_glyf_table): New field. + * src/sfntfont.c (struct sfnt_font_info, sfntfont_open) + (sfntfont_close, sfntfont_detect_sigbus): Allow mmapping fonts + if possible. + * src/sfntfont.h: Update prototypes. + * src/sysdep.c (handle_sigbus, init_sigbus, init_signals): + Initialize SIGBUS correctly. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-17 Po Lu + + Work around race condition bug in Android 13's input manager + * src/androidterm.c (android_get_selection): Use ephemeral last + point. + * src/textconv.c (report_selected_window_change): Set + w->ephemeral_last_point to the window's point now. + + Update emacsbug and version.el for the Android port + * java/Makefile.in (install_temp/assets/version): New generated + file. + * lisp/loadup.el: Set emacs versions appropriately prior to + dumping on Android. + * lisp/mail/emacsbug.el (emacs-build-description): Insert + Android build fingerprint. + * lisp/version.el (emacs-repository-version-android) + (emacs-repository-get-version, emacs-repository-get-branch): + Implement for Android. + * src/androidterm.c (android_set_build_fingerprint): New + function. + (syms_of_androidterm): New variable `android-build-fingerprint'. + + * src/android.c (android_exception_check): Fix typo. + + Merge remote-tracking branch 'origin/master' into feature/android + + Improve logging of Java exceptions + * src/android.c (android_exception_check): Print more detailed + information. + + Fix crash on old versions of Android + * java/org/gnu/emacs/EmacsService.java (nameKeysym): Implement + stub on Android 3.0 and earlier. + + Fix build and running on Android 2.2 + * INSTALL.android: Document that Android 2.2 is now supported, + with caveats. + * configure.ac (ANDROID_MIN_SDK, ANDROID_SDK_18_OR_EARLIER) + (SYSTEM_TYPE, ANDROID_STUBIFY, SIZEOF_LONG): Correctly detect + things missing on Android 2.2. + * java/Makefile.in (ANDROID_JAR, JARSIGNER_FLAGS): + * java/debug.sh (jdb, gdbserver, line): + * java/org/gnu/emacs/EmacsApplication.java (findDumpFile): + * java/org/gnu/emacs/EmacsService.java (onCreate): + * java/org/gnu/emacs/EmacsThread.java (EmacsThread, run): Run + parameter initialization on main thread. + * src/android-asset.h (struct android_asset_manager) + (struct android_asset, AAssetManager_fromJava, AAssetManager_open) + (AAsset_close, android_asset_create_stream) + (android_asset_read_internal, AAsset_openFileDescriptor) + (AAsset_getLength, AAsset_getBuffer, AAsset_read): New file. + * src/android.c (android_user_full_name, android_hack_asset_fd) + (android_check_compressed_file): Implement for Android 2.2. + * src/process.c (Fprocess_send_eof): Don't call tcdrain if + unavailable. + * src/sfntfont-android.c (system_font_directories): Fix compiler + warning. + * src/sfntfont.c (sfntfont_read_cmap): Correctly test rc of + emacs_open. + * src/textconv.c (handle_pending_conversion_events_1): Mark + buffer UNINIT. + +2023-02-16 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-16 Po Lu + + Update Android port + * doc/emacs/android.texi (Android Fonts): + * doc/emacs/input.texi (On-Screen Keyboards): + * doc/lispref/commands.texi (Misc Events): Update documentation. + * java/org/gnu/emacs/EmacsInputConnection.java (setSelection): + New function. + * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView) + (reconfigureFrontBuffer): Make bitmap references weak + references. + * java/org/gnu/emacs/EmacsView.java (handleDirtyBitmap): Don't + clear surfaceView bitmap. + * lisp/comint.el (comint-mode): + * lisp/international/fontset.el (script-representative-chars) + (setup-default-fontset): Improve detection of CJK fonts. + * lisp/isearch.el (set-text-conversion-style): New variable. + (isearch-mode, isearch-done): Save and restore the text + conversion style. + * lisp/minibuffer.el (minibuffer-mode): Set an appropriate text + conversion style. + * lisp/simple.el (analyze-text-conversion): Run + post-self-insert-hook properly. + * lisp/subr.el (read-char-from-minibuffer): Disable text + conversion when reading character. + * src/androidterm.c (show_back_buffer): Don't check that F is + not garbaged. + (android_update_selection, android_reset_conversion): Use the + ephemeral last point and handle text conversion being disabled. + * src/buffer.c (syms_of_buffer): Convert old style DEFVAR. + * src/keyboard.c (kbd_buffer_get_event): Handle text conversion + first. + * src/lisp.h: Update prototypes. + * src/lread.c (read_filtered_event): Temporarily disable text + conversion. + * src/sfnt.c (sfnt_decompose_glyph_1, sfnt_decompose_glyph_2): + New functions. + (sfnt_decompose_glyph, sfnt_decompose_instructed_outline): + Refactor contour decomposition to those two functions. + (main): Update tests. + * src/sfntfont-android.c (system_font_directories): Add empty + field. + (Fandroid_enumerate_fonts, init_sfntfont_android): Enumerate + fonts in a user fonts directory. + * src/sfntfont.c (struct sfnt_font_desc): New field + `num_glyphs'. + (sfnt_enum_font_1): Set num_glyphs and avoid duplicate fonts. + (sfntfont_glyph_valid): New function. + (sfntfont_lookup_char, sfntfont_list_1): Make sure glyphs found + are valid. + + * src/textconv.c (sync_overlay, really_commit_text) + (really_set_composing_text, really_set_composing_region) + (really_delete_surrounding_text, really_set_point_and_mark) + (handle_pending_conversion_events_1) + (handle_pending_conversion_events, conversion_disabled_p) + (disable_text_conversion, resume_text_conversion) + (Fset_text_conversion_style, syms_of_textconv): Update to + respect new options. + * src/textconv.h: + * src/window.h (GCALIGNED_STRUCT): New field + `ephemeral_last_point'. + * src/xdisp.c (mark_window_display_accurate_1): Set it. + +2023-02-15 Po Lu + + Update Android port + * doc/emacs/input.texi (On-Screen Keyboards): + * doc/lispref/commands.texi (Misc Events): Improve documentation + of text conversion stuff. + * java/org/gnu/emacs/EmacsInputConnection.java (beginBatchEdit) + (endBatchEdit, commitCompletion, commitText, deleteSurroundingText) + (finishComposingText, getSelectedText, getTextAfterCursor) + (EmacsInputConnection, setComposingRegion, performEditorAction) + (getExtractedText): Condition debug code on DEBUG_IC. + * java/org/gnu/emacs/EmacsService.java (EmacsService, updateIC): + Likewise. + * lisp/bindings.el (global-map): + * lisp/electric.el (global-map): Make `text-conversion' + `analyze-text-conversion'. + * lisp/progmodes/prog-mode.el (prog-mode): Enable text + conversion in input methods. + * lisp/simple.el (analyze-text-conversion): New function. + * lisp/textmodes/text-mode.el (text-conversion-style) + (text-mode): Likewise. + * src/androidterm.c (android_handle_ime_event): Handle + set_point_and_mark. + (android_sync_edit): Give Emacs 100 ms instead. + (android_perform_conversion_query): Skip the active region, not + the conversion region. + (getSelectedText): Implement properly. + (android_update_selection): Expose mark to input methods. + (android_reset_conversion): Handle `text-conversion-style'. + * src/buffer.c (init_buffer_once, syms_of_buffer): Add buffer + local variable `text-conversion-style'. + * src/buffer.h (struct buffer, bset_text_conversion_style): New + fields. + * src/emacs.c (android_emacs_init): Call syms_of_textconv. + * src/frame.h (enum text_conversion_operation): Rename + TEXTCONV_SET_POINT. + * src/lisp.h: Export syms_of_textconv. + + * src/marker.c (set_marker_internal): Force redisplay when the + mark is set and the buffer is visible on builds that use text + conversion. Explain why. + + * src/textconv.c (copy_buffer): Fix copying past gap. + (get_mark): New function. + (textconv_query): Implement new flag. + (sync_overlay): New function. Display conversion text in an + overlay. + (record_buffer_change, really_commit_text) + (really_set_composing_text, really_set_composing_region) + (really_delete_surrounding_text, really_set_point) + (handle_pending_conversion_events_1, decrement_inside) + (handle_pending_conversion_events, textconv_set_point) + (get_extracted_text, register_textconv_interface): Various fixes + and improvements. + + * src/textconv.h (struct textconv_interface): Update + documentation. + * src/window.h (GCALIGNED_STRUCT): New field `prev_mark'. + * src/xdisp.c (mark_window_display_accurate_1): Handle + prev_mark. + +2023-02-15 Po Lu + + Make debug.sh detect adb running as root + * java/debug.sh: Run gdbserver directly if possible. + + Fix small bugs + * src/androidterm.c (android_handle_ime_event): Pacify compiler + warnings. + * src/textconv.c (really_set_composing_text) + (handle_pending_conversion_events, get_extracted_text): Fix + reentrancy problems and uses of uninitialized values. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-15 Po Lu + + Update Android port + * configure.ac (HAVE_TEXT_CONVERSION): Define on Android. + * doc/emacs/input.texi (On-Screen Keyboards): Document ``text + conversion'' slightly. + * doc/lispref/commands.texi (Misc Events): Document new + `text-conversion' event. + * java/org/gnu/emacs/EmacsContextMenu.java (display): Use + `syncRunnable'. + * java/org/gnu/emacs/EmacsDialog.java (display): Likewise. + * java/org/gnu/emacs/EmacsEditable.java: Delete file. + * java/org/gnu/emacs/EmacsInputConnection.java + (EmacsInputConnection): Reimplement from scratch. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Add new + functions. + * java/org/gnu/emacs/EmacsService.java (EmacsService, getEmacsView) + (getLocationOnScreen, sync, getClipboardManager, restartEmacs): + Use syncRunnable. + (syncRunnable): New function. + (updateIC, resetIC): New functions. + + * java/org/gnu/emacs/EmacsView.java (EmacsView): New field + `inputConnection' and `icMode'. + (onCreateInputConnection): Update accordingly. + (setICMode, getICMode): New functions. + + * lisp/bindings.el (global-map): Ignore text conversion events. + * src/alloc.c (mark_frame): Mark text conversion data. + * src/android.c (struct android_emacs_service): New fields + `update_ic' and `reset_ic'. + (event_serial): Export. + (android_query_sem): New function. + (android_init_events): Initialize new semaphore. + (android_write_event): Export. + (android_select): Check for UI thread code. + (setEmacsParams, android_init_emacs_service): Initialize new + methods. + (android_check_query, android_begin_query, android_end_query) + (android_run_in_emacs_thread): + (android_update_ic, android_reset_ic): New functions for + managing synchronous queries from one thread to another. + + * src/android.h: Export new functions. + * src/androidgui.h (enum android_event_type): Add input method + events. + (enum android_ime_operation, struct android_ime_event) + (union android_event, enum android_ic_mode): New structs and + enums. + + * src/androidterm.c (android_window_to_frame): Allow DPYINFO to + be NULL. + (android_decode_utf16, android_handle_ime_event) + (handle_one_android_event, android_sync_edit) + (android_copy_java_string, beginBatchEdit, endBatchEdit) + (commitCompletion, deleteSurroundingText, finishComposingText) + (getSelectedtext, getTextAfterCursor, getTextBeforeCursor) + (setComposingText, setComposingRegion, setSelection, getSelection) + (performEditorAction, getExtractedText): New functions. + (struct android_conversion_query_context): + (android_perform_conversion_query): + (android_text_to_string): + (struct android_get_selection_context): + (android_get_selection): + (struct android_get_extracted_text_context): + (android_get_extracted_text): + (struct android_extracted_text_request_class): + (struct android_extracted_text_class): + (android_update_selection): + (android_reset_conversion): + (android_set_point): + (android_compose_region_changed): + (android_notify_conversion): + (text_conversion_interface): New functions and structures. + (android_term_init): Initialize text conversion. + + * src/coding.c (syms_of_coding): Define Qutf_16le on Android. + * src/frame.c (make_frame): Clear conversion data. + (delete_frame): Reset conversion state. + + * src/frame.h (enum text_conversion_operation) + (struct text_conversion_action, struct text_conversion_state) + (GCALIGNED_STRUCT): Update structures. + * src/keyboard.c (read_char, readable_events, kbd_buffer_get_event) + (syms_of_keyboard): Handle text conversion events. + * src/lisp.h: + * src/process.c: Fix includes. + + * src/textconv.c (enum textconv_batch_edit_flags, textconv_query) + (reset_frame_state, detect_conversion_events) + (restore_selected_window, really_commit_text) + (really_finish_composing_text, really_set_composing_text) + (really_set_composing_region, really_delete_surrounding_text) + (really_set_point, complete_edit) + (handle_pending_conversion_events_1) + (handle_pending_conversion_events, start_batch_edit) + (end_batch_edit, commit_text, finish_composing_text) + (set_composing_text, set_composing_region, textconv_set_point) + (delete_surrounding_text, get_extracted_text) + (report_selected_window_change, report_point_change) + (register_texconv_interface): New functions. + + * src/textconv.h (struct textconv_interface) + (TEXTCONV_SKIP_CONVERSION_REGION): Update prototype. + * src/xdisp.c (mark_window_display_accurate_1): + * src/xfns.c (xic_string_conversion_callback): + * src/xterm.c (init_xterm): Adjust accordingly. + +2023-02-12 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * doc/emacs/android.texi (Android Environment): Document + notifications permission. + * java/org/gnu/emacs/EmacsEditable.java (EmacsEditable): + * java/org/gnu/emacs/EmacsInputConnection.java + (EmacsInputConnection): New files. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Load + library dependencies in a less verbose fashion. + * java/org/gnu/emacs/EmacsView.java (EmacsView): Make imManager + public. + (onCreateInputConnection): Set InputType to TYPE_NULL for now. + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, onKeyDown) + (onKeyUp, getEventUnicodeChar): Correctly handle key events with + strings. + * lisp/term/android-win.el (android-clear-preedit-text) + (android-preedit-text): New special event handlers. + * src/android.c (struct android_emacs_window): Add function + lookup_string. + (android_init_emacs_window): Adjust accordingly. + (android_wc_lookup_string): New function. + * src/androidgui.h (struct android_key_event): Improve + commentary. + (enum android_lookup_status): New enum. + * src/androidterm.c (handle_one_android_event): Synchronize IM + lookup code with X. + * src/coding.c (from_unicode_buffer): Implement on Android. + * src/coding.h: + * src/sfnt.c: Fix commentary. + +2023-02-11 Po Lu + + Fix displaying popup menus from a menu entry on Android + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity, onDestroy) + (onWindowFocusChanged): Keep track of the last focused activity. + * java/org/gnu/emacs/EmacsDialog.java (display1): Use it if + there is no current focus. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-10 Po Lu + + Improve appearance of the Android preferences screen + * .gitignore: Add org/gnu/emacs/R.java. + * cross/Makefile.in (top_builddir): Include verbose.mk. Rewrite + rules to print nice looking statements. + * doc/emacs/android.texi (Android, Android Startup) + (Android Environment, Android Windowing, Android Fonts): + * doc/emacs/emacs.texi (Top): Add an extra ``Android + Troubleshooting'' node and move troubleshooting details there. + * java/Makefile.in: Generate R.java; improve appearance by using + verbose.mk. + + * java/org/gnu/emacs/EmacsPreferencesActivity.java: Reimplement + in terms of PreferencesActivity. + * java/org/gnu/emacs/EmacsView.java (handleDirtyBitmap): Avoid + flicker. + * java/res/xml/preferences.xml: New file. + * src/verbose.mk.in (AM_V_AAPT, AM_V_SILENT): New variables. + +2023-02-10 Po Lu + + Implement more features for the Emacs ``documents provider'' + * java/org/gnu/emacs/EmacsDocumentsProvider.java (queryRoots): + Implement isChild. + (getNotificationUri, notifyChange): New functions. + (queryDocument1): Set rename and remove flags. + (queryDocument, queryChildDocuments): Allow the requester to + detect changes in the directory hierarchy. + (createDocument, deleteDocument, removeDocument): Signal changes + to the directory hierarchy. + + * java/org/gnu/emacs/EmacsCopyArea.java (perform): Fix typo. + + Merge remote-tracking branch 'origin/master' into feature/android + + Fix buffer swapping on Android 7.1 and earlier + * java/org/gnu/emacs/EmacsSurfaceView.java + (reconfigureFrontBuffer): Don't use function only present on + Android 8.0 and later. + + Update Android port + * doc/emacs/android.texi (Android Windowing): Remove yet another + limitation. + * java/debug.sh: Make this work on systems which prohibit + attaching to app processes from adbd. + * java/org/gnu/emacs/EmacsCopyArea.java (perform): Avoid + creating copies whenever possible. + * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): + Remove SurfaceView based implementation and use manual double + buffering with invalidate instead. + * java/org/gnu/emacs/EmacsView.java (EmacsView, handleDirtyBitmap) + (raise, lower, onDetachedFromWindow): Adjust accordingly. + * java/org/gnu/emacs/EmacsWindow.java (windowUpdated): Remove + function. + * src/sfntfont.c (sfntfont_open): Set font->max_width correctly. + +2023-02-10 Po Lu + + Fix IUP for contours which start past end + Found with Droid Sans Mono hinted with ttfautohint 1.8.4. + + * src/sfnt.c (IUP_SINGLE_PAIR): If i is initially more than end, + make it start. + (sfnt_verbose): Handle cases where interpreter->glyph_zone is + NULL. + (main): Update tests. + +2023-02-10 Po Lu + + Fix typo + * src/sfnt.c (sfnt_read_cmap_table): Fix typo. + (main): Update tests. + +2023-02-09 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Allow other text editors to edit files in Emacs' home directory + * java/AndroidManifest.xml.in: Declare the new documents + provider. + * java/README: Describe the meaning of files in res/values. + * java/org/gnu/emacs/EmacsDocumentsProvider.java + (EmacsDocumentsProvider): New file. + * java/res/values-v19/bool.xml: + * java/res/values/bool.xml: New files. + + * src/sfnt.c (main): Update tests. + +2023-02-09 Po Lu + + Implement instructing compound glyphs + * src/sfnt.c (sfnt_read_simple_glyph, sfnt_read_compound_glyph) + (sfnt_read_glyph): Take size_t offsets. + (struct sfnt_compound_glyph_context) + (sfnt_expand_compound_glyph_context) + (sfnt_decompose_compound_glyph): Take size_t contour offsets. + (sfnt_decompose_glyph): Always close contour even if the first + point isn't on-curve. + (sfnt_build_outline_edges): Fix coding style. + (sfnt_interpret_iup): Skip phantom points during IUP. + (sfnt_decompose_instructed_outline): Clarify documentation. + Always close contour even if the first point isn't on-curve. + (struct sfnt_test_dcontext, sfnt_test_move_to, sfnt_test_line_to) + (sfnt_test_curve_to, sfnt_transform_f26dot6, sfnt_test_get_glyph) + (sfnt_test_free_glyph, sfnt_test_span, sfnt_test_edge_ignore) + (sfnt_interpret_compound_glyph_2, sfnt_test_edges, main): Update + tests. + + * src/sfnt.h: Export new function. + + * src/sfntfont.c (sfntfont_get_glyph_outline): Handle compound + glyphs. + (sfntfont_measure_instructed_pcm, sfntfont_measure_pcm) + (sfntfont_draw): Update accordingly. + +2023-02-08 Po Lu + + Update Android port + * src/sfnt.c (SCFS): Fix order of arguments. + (sfnt_normalize_vector): Make sure vx and vy are within a + reasonable range. + (sfnt_move): Don't move when vectors are orthogonal. + (main): Update. + + Update Android port + * doc/emacs/android.texi (Android Startup): Fix typos. + * src/sfnt.c (sfnt_interpret_msirp): Fix order in which operands + to MSIRP are popped. + (main): Reduce ppem values. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-08 Po Lu + + Update Android port + * doc/lispref/frames.texi (On-Screen Keyboards): Describe return + value of `frame-toggle-on-screen-keyboard'. + * java/org/gnu/emacs/EmacsSurfaceView.java (surfaceChanged) + (surfaceCreated, EmacsSurfaceView): Remove unuseful + synchronization code. The framework doesn't seem to look at + this at all. + + * java/org/gnu/emacs/EmacsView.java (EmacsView): + (onLayout): Lay out the window after children. + (swapBuffers): Properly implement `force'. + (windowUpdated): Delete function. + + * lisp/frame.el (frame-toggle-on-screen-keyboard): Return + whether or not the on screen keyboard might've been displayed. + + * lisp/minibuffer.el (minibuffer-on-screen-keyboard-timer): + (minibuffer-on-screen-keyboard-displayed): + (minibuffer-setup-on-screen-keyboard): + (minibuffer-exit-on-screen-keyboard): Improve OSK dismissal when + there are consecutive minibuffers. + + * lisp/touch-screen.el (touch-screen-window-selection-changed): + New function. + (touch-screen-handle-point-up): Register it as a window + selection changed function. + + * src/android.c (struct android_emacs_window) + (android_init_emacs_window): Remove references to + `windowUpdated'. + (android_window_updated): Delete function. + * src/android.h (struct android_output): Remove + `last_configure_serial'. + * src/androidterm.c (handle_one_android_event) + (android_frame_up_to_date): + * src/androidterm.h (struct android_output): Remove frame + synchronization, as that does not work on Android. + +2023-02-08 Po Lu + + Fix graphics state when instructing glyphs + * src/sfntfont.c (sfntfont_get_glyph_outline): Take new argument + STATE and restore it prior to instructing the glyph. + (sfntfont_measure_instructed_pcm, sfntfont_measure_pcm) + (sfntfont_draw): Adjust accordingly. + + Correctly round bearing values while computing pcm + * src/sfntfont.c (sfntfont_measure_instructed_pcm) + (sfntfont_measure_pcm): Ceil rbearing value. + +2023-02-08 Po Lu + + Improve text display on Android port + * src/sfnt.c (sfnt_build_glyph_outline): Clear + build_outline_context. + (sfnt_poly_coverage): Extend coverage map. + (sfnt_prepare_raster): Always floor coordinates, since the + increase in coverage makes this hack unnecessary. + (sfnt_build_outline_edges): Likewise. + (sfnt_compare_edges): Remove function. + (sfnt_edge_sort): New function. Since edges are already + partially sorted, and there are not many, insertion sort + suffices. + (sfnt_poly_edges): Use sfnt_edge_sort. + (sfnt_fill_span): Stop rounding x0 and x1 to the grid, and make + coverage computation static. + (sfnt_lookup_glyph_metrics): Fix return code for unscaled + metrics. + (sfnt_scale_metrics): New function. + (SFNT_ENABLE_HINTING): Remove define. + (struct sfnt_cvt_table, struct sfnt_fpgm_table) + (struct sfnt_prep_table): Move to sfnt.h. + (sfnt_read_cvt_table): + (sfnt_read_fpgm_table, sfnt_read_prep_table): Make TEST_STATIC. + (struct sfnt_unit_vector, struct sfnt_interpreter_definition) + (struct sfnt_interpreter_zone, struct sfnt_graphics_state): + (struct sfnt_interpreter): Move to sfnt.h. + (sfnt_make_interpreter): Make TEST_STATIC. + (POP, PUSH, DELTAP1, DELTAP2, DELTAP3): When TEST, define to + regular push and pop. + (sfnt_deltac): + (sfnt_deltap): Fix order of arguments. + (IUP_SINGLE_PAIR): Fix interpolation loop wraparound. + (sfnt_interpret_font_program): + (sfnt_interpret_control_value_program): Make TEST_STATIC. + (struct sfnt_instructed_outline): Move to sfnt.h. + (sfnt_build_instructed_outline): Make TEST_STATIC. + (sfnt_interpret_simple_glyph): + (sfnt_x_raster): + (sfnt_test_raster): + (all_tests): + (sfnt_verbose): + (main): Improve test code. + + * src/sfnt.h (SFNT_ENABLE_HINTING, struct sfnt_cvt_table) + (struct sfnt_fpgm_table, struct sfnt_prep_table) + (struct sfnt_unit_vector, struct sfnt_interpreter_definition) + (struct sfnt_interpreter_zone, struct sfnt_graphics_state) + (struct sfnt_interpreter, struct sfnt_instructed_outline) + (PROTOTYPE): New definitions. + * src/sfntfont-android.c (sfntfont_android_put_glyphs): Make + coordinate generation more straightforward. + * src/sfntfont.c (sfntfont_get_glyph_outline): New arguments + INTERPRETER and METRICS. + (struct sfnt_font_info): New tables. + (sfntfont_setup_interpreter): New function. + (sfntfont_open): Avoid memory leak. Set up interpreter. + (sfntfont_measure_instructed_pcm): New function. + (sfntfont_measure_pcm): Delegate to measure_instructed_pcm where + appropriate. + (sfntfont_close): Free new tables. + (sfntfont_draw): Scale metrics properly. + +2023-02-07 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Remove junk from instruction table + * src/sfnt.c (sfnt_name_instruction): Do so. + + Remove bresenham stuff + * src/sfnt.c (sfnt_step_edge, sfnt_step_edge_n) + (sfnt_build_outline_edges, sfnt_test_edge, main): + * src/sfnt.h (struct sfnt_edge): Stop using error corrected line + drawing, as it's actually slower. + +2023-02-07 Po Lu + + Update Android port + * INSTALL.android: Describe patches for BoringSSL on ARM. + + * src/sfnt.c (sfnt_build_glyph_outline): Remove redundant + multiplication. + (sfnt_prepare_raster): Update offset calculation for changes. + (sfnt_step_edge, sfnt_step_edge_n): Handle bresenham terms. + (sfnt_build_outline_edges): Don't subtract floored xmin, just + xmin. + (sfnt_saturate_short): Make clang generate better code. + (sfnt_fill_span): Stop rounding coordinates. + (sfnt_poly_span): Poly consecutive on transitions all in one go. + (sfnt_lookup_glyph_metrics): Remove redundant multiplication. + (struct sfnt_interpreter): New hooks for debugging. + (sfnt_large_integer_add): New function. + (sfnt_mul_f26dot6_fixed): Round product. + (sfnt_make_interpreter): Remove redundant multiplication. + + (CHECK_STACK_ELEMENTS, POP_UNCHECKED, PUSH_UNCHECKED): New + macros. + (MOVE, POP, SWAP, CINDEX, RS, RCVT, LT, LTEQ, GT, GTEQ, EQ, NEQ) + (EVEN, AND, OR, NOT, ADD, SUB, DIV, MUL, ABS, NEG, FLOOR, CEILING) + (GETINFO, ROLL, _MAX, _MIN, ROUND, NROUND, GC, MD): Don't check + SP redundantly, especially when pushing an element right after + popping one. + (sfnt_move_glyph_zone): Don't touch points by passing NULL as + flags. + (sfnt_direct_move_zp2): Touch P in the directions of the + movement. + (sfnt_interpret_scfs): Fix coding style. + (sfnt_interpret_simple_glyph): Don't round Y coordinates. + (sfnt_test_span, sfnt_test_edges, sfnt_debug_edges, sfnt_test_edge) + (sfnt_x_raster, sfnt_test_raster, rcvt_test_args) + (deltac1_test_args, deltac2_test_args, deltac3_test_args) + (roll_1_test_args, sfnt_run_hook, sfnt_identify_instruction) + (sfnt_verbose, main): Improve debug code and tests. + + * src/sfnt.h (struct sfnt_edge): Add bresenham terms. + +2023-02-06 Po Lu + + Port emacsclient wrapper to Android 7.1 and earlier + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Load every + native library on which Emacs depends prior to loading libemacs + itself. + + * java/org/gnu/emacs/EmacsOpenActivity.java (readEmacsClientLog) + (EmacsOpenActivity, startEmacsClient): Don't use redirectError + on Android 7.1 and earlier. + +2023-02-06 Po Lu + + Adjust ndk-build implementation for old NDK versions + * configure.ac: Pass ANDROID_CFLAGS to ndk_INIT. + * cross/ndk-build/Makefile.in (NDK_BUILD_CFLAGS): + * cross/ndk-build/ndk-build-shared-library.mk + ($(call objname,$(LOCAL_MODULE),$(basename $(1)))): + ($$(error Unsupported suffix): + * cross/ndk-build/ndk-build-static-library.mk + ($(call objname,$(LOCAL_MODULE),$(basename $(1)))): + ($$(error Unsupported suffix): Use NDK_BUILD_CFLAGS. + * m4/ndk-build.m4 (ndk_INIT): Accept cflags. + (ndk_CONFIG_FILES): Export NDK_BUILD_CFLAGS. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-06 Po Lu + + Update Android port + * java/AndroidManifest.xml.in: Prevent the Emacs activity from + being overlayed by the emacsclient wrapper. + * java/org/gnu/emacs/EmacsOpenActivity.java (run): Likewise. + (onCreate): Set an appropriate theme on ICS and up. + + * java/org/gnu/emacs/EmacsWindow.java (onTouchEvent): Handle + ACTION_CANCEL correctly. + +2023-02-06 Po Lu + + Update Android port + * src/sfnt.c (struct sfnt_build_glyph_outline_context) + (sfnt_build_glyph_outline, sfnt_fill_span): Improve glyph + appearance by rounding coordinate values. + + (struct sfnt_interpreter): New fields `twilight_original_x', + `twilight_original_y'. + (sfnt_make_interpreter): Set new fields. + (DELTAP1, DELTAP2, DELTAP3, SVTCAy, SPVTL, SFVTL, MD): Implement + instructions. + (sfnt_save_projection_vector): New argument `dual_only'. All + callers changed. + (sfnt_address_zp2, sfnt_address_zp1, sfnt_address_zp0): Obtain + original positions in the twilight zone as well. + (sfnt_check_zp1, sfnt_interpret_fliprgoff, sfnt_interpret_fliprgon) + (sfnt_interpret_flippt, sfnt_interpret_scfs, sfnt_interpret_miap) + (sfnt_interpret_alignrp, sfnt_line_to_vector, P) + (sfnt_interpret_msirp, sfnt_interpret_ip, sfnt_interpret_call) + (load_point, sfnt_interpret_iup_1, sfnt_interpret_iup) + (sfnt_interpret_run, struct sfnt_scaled_outline) + (struct sfnt_instructed_outline, sfnt_decompose_instructed_outline) + (sfnt_build_instructed_outline, sfnt_compute_phantom_points) + (sfnt_interpret_simple_glyph, all_tests, sfnt_setup_debugger) + (sfnt_name_instruction, sfnt_draw_debugger, sfnt_run_hook) + (sfnt_verbose, main): Make glyph instructing work. + + * src/sfnt.h (SFNT_POLY_ROUND): New enumerator. + +2023-02-05 Po Lu + + Update Android port + * INSTALL.android: Explain how to build selinux. + * configure.ac: Enable selinux on Android. + * cross/ndk-build/ndk-build-shared-library.mk: ($(call + objname,$(LOCAL_MODULE),$(basename $(1))))::($$(error + Unsupported suffix)::(NDK_CFLAGS_$(LOCAL_MODULE)): + * cross/ndk-build/ndk-build-static-library.mk: ($(call + objname,$(LOCAL_MODULE),$(basename $(1))))::($$(error + Unsupported suffix)::(NDK_CFLAGS_$(LOCAL_MODULE)): Correctly + handle files with a .cc suffix, and clang-specific asflags. + * cross/ndk-build/ndk-clear-vars.mk: Handle AOSP extensions + LOCAL_ADDITIONAL_DEPENDENCIES, + LOCAL_CLANG_ASFLAGS_$(NDK_BUILD_ARCH) and LOCAL_IS_HOST_MODULE. + + * doc/emacs/android.texi (Android Startup): Explain emacsclient + wrapper. + + * java/org/gnu/emacs/EmacsView.java (EmacsView): New flag + `isCurrentlyTextEditor'. + (showOnScreenKeyboard, hideOnScreenKeyboard): Set as + appropriate. + (onCheckIsTextEditor): Return its value. + + * lisp/touch-screen.el (touch-screen-handle-scroll): Don't ding + at buffer limits. + * m4/ndk-build.m4: Improve doc. + + * src/Makefile.in (LIBSELINUX_CFLAGS): New variable. + (EMACS_CFLAGS): Add it. + +2023-02-05 Po Lu + + Update from gnulib + * admin/merge-gnulib (avoided_flags): + * cross/lib/cdefs.h (__bos): + (__glibc_unsigned_or_positive): + (__glibc_unsafe_len): + (__glibc_fortify): + (__glibc_fortify_n): + * cross/lib/isnan.c: + * cross/lib/libc-config.h: + * cross/lib/openat-proc.c (openat_proc_name): + * cross/lib/vasnprintf.c (VASNPRINTF): + * cross/lib/verify.h (_Static_assert): + (_GL_SA3): + * lib/gnulib.mk.in (HAVE_GRANTPT): + (HAVE_SPAWN_H): + (NEXT_AS_FIRST_DIRECTIVE_LIMITS_H): + (NEXT_LIMITS_H): + (REPLACE_GETSUBOPT): + (REPLACE_ILOGB): + (SYSTEM_TYPE): + (BUILT_SOURCES): + * lib/isnan.c: + * lib/vasnprintf.c (VASNPRINTF): + * lib/verify.h (_GL_SA3): + * m4/gnulib-common.m4 (gl_COMMON_BODY): + * m4/gnulib-comp.m4 (gl_INIT): Update from gnulib. + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/sfnt.c (struct sfnt_graphics_state): + (LOOPCALL): + (DELTAC3): + (PROJECT): + (SHPIX): + (sfnt_save_projection_vector): + (sfnt_check_zp0): + (sfnt_dual_project_vector): + (sfnt_interpret_scfs): + (sfnt_round_symmetric): + (sfnt_interpret_miap): + (sfnt_interpret_alignrp_1): + (sfnt_interpret_alignrp): + (sfnt_measure_distance): + (sfnt_interpret_msirp): + (sfnt_interpret_ip): + (sfnt_interpret_mdap): + (sfnt_deltap): + (sfnt_dual_project_onto_any_vector): + (sfnt_validate_gs): + (sfnt_set_projection_vector): + (sfnt_interpret_shp): + (sfnt_interpret_run): + (sfnt_check_sloop): + (main): Check in more WIP font code. + +2023-02-04 Po Lu + + Add emacsclient desktop file equivalent on Android + * doc/emacs/android.texi (Android File System): + * java/AndroidManifest.xml.in: Update with new activity. Remove + Android 10 restrictions through a special flag. + + * java/org/gnu/emacs/EmacsNative.java (getProcName): New + function. + * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity): + New file. + * java/org/gnu/emacs/EmacsService.java (getLibraryDirection): + Remove unused annotation. + * lib-src/emacsclient.c (decode_options): Set alt_display on + Android. + * src/android.c (android_proc_name): New function. + (NATIVE_NAME): Export via JNI. + +2023-02-04 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Add additional permissions to Android port + * doc/emacs/android.texi (Android Environment): + * java/AndroidManifest.xml.in: Add network state permissions. + + Update Android port + * src/sfnt.c (sfnt_multiply_divide_signed): + (struct sfnt_interpreter_zone): + (struct sfnt_graphics_state): + (struct sfnt_interpreter): + (sfnt_mul_f2dot14): + (sfnt_interpret_trap): + (WCVTF): + (ALIGNPTS): + (sfnt_scale_by_freedom_vector): + (sfnt_interpret_utp): + (sfnt_address_zp2): + (sfnt_address_zp1): + (sfnt_address_zp0): + (sfnt_check_zp2): + (sfnt_move_zp0): + (sfnt_move_zp1): + (sfnt_move_glyph_zone): + (sfnt_move_twilight_zone): + (sfnt_direct_move_zp2): + (sfnt_interpret_alignpts): + (sfnt_interpret_isect): + (sfnt_line_to_vector): + (sfnt_deltac): + (sfnt_interpret_mdap): + (sfnt_interpret_call): + (sfnt_dot_fix_14): + (sfnt_move_x): + (sfnt_move_y): + (sfnt_move): + (sfnt_validate_gs): + (sfnt_interpret_shz): + (sfnt_interpret_shc): + (sfnt_interpret_shp): + (sfnt_interpret_iup_1): + (sfnt_interpret_iup): + (sfnt_interpret_run): + (sfnt_interpret_font_program): + (sfnt_interpret_control_value_program): + (sfnt_interpret_simple_glyph): + (jrot_test_args): + (jrof_test_args): + (all_tests): + (main): Check in more WIP code. + +2023-02-02 Po Lu + + Add Emacs icon for Android package + * java/AndroidManifest.xml.in: Add new icon. + * java/Makefile.in (srcdir): New variable. + (JAVA_FILES, RESOURCE_FILES): Update variables. + (emacs.apk-in): Apply resources. + * java/README: Describe directory tree. + + Add Emacs icon for Android + * java/res/drawable/emacs.png: New file. + + Update Android port + * src/android.c (android_get_current_api_level): New function. + * src/android.h: Export it. + * src/sfntfont-android.c (init_sfntfont_android): Make device + API level detection always work. + + Clean up compiler warnings + * src/sfnt.c (sfnt_multiply_divide_signed): Add MAYBE_UNUSED. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-02-02 Po Lu + + Update Android port + * src/sfnt.c (xmalloc, xrealloc): Improve behavior upon + allocation failures during test. + (sfnt_table_names): Add prep. + (sfnt_transform_coordinates): Allow applying offsets during + coordinate transform. + (sfnt_decompose_compound_glyph): Defer offset computation until + any component compound glyph is loaded, then apply it during the + transform process. + + (sfnt_multiply_divide): Make available everywhere. Implement on + 64 bit systems. + (sfnt_multiply_divide_signed): New function. + (sfnt_mul_fixed): Fix division overflow. + + (sfnt_curve_to_and_build_1, sfnt_build_glyph_outline): Remove + outdated comment. + (sfnt_build_outline_edges): Fix coding style. + (sfnt_lookup_glyph_metrics): Allow looking up metrics without + scaling. + (struct sfnt_cvt_table): Fix type of cvt values. + (struct sfnt_prep_table): New structure. + (sfnt_read_cvt_table): Read cvt values in terms of fwords, not + longs (as Apple's doc seems to say). + (sfnt_read_fpgm_table): Fix memory allocation for font program + table. + (sfnt_read_prep_table): New function. + (struct sfnt_interpreter_zone): New structure. + (struct sfnt_interpreter_graphics_state): New fields `project', + `move', `vector_dot_product'. Rename to `sfnt_graphics_state'. + (struct sfnt_interpreter, sfnt_mul_f26dot6): Stop doing rounding + division. + (sfnt_init_graphics_state): + (sfnt_make_interpreter): + (MOVE): + (SSW): + (RAW): + (SDS): + (ADD): + (SUB): + (ABS): + (NEG): + (WCVTF): + (_MIN): + (S45ROUND): + (SVTCAx): + (sfnt_set_srounding_state): + (sfnt_skip_code): + (sfnt_interpret_unimplemented): + (sfnt_interpret_fdef): + (sfnt_interpret_idef): + (sfnt_interpret_if): + (sfnt_interpret_else): + (sfnt_round_none): + (sfnt_round_to_grid): + (sfnt_round_to_double_grid): + (sfnt_round_down_to_grid): + (sfnt_round_up_to_grid): + (sfnt_round_to_half_grid): + (sfnt_round_super): + (sfnt_validate_gs): + (sfnt_interpret_run): + (sfnt_interpret_font_program): + (struct sfnt_test_dcontext): + (sfnt_test_move_to): + (sfnt_test_line_to): + (sfnt_test_curve_to): + (sfnt_test_get_glyph): + (sfnt_test_free_glyph): + (sfnt_test_span): + (sfnt_test_edge_ignore): + (sfnt_test_edge): + (sfnt_test_raster): + (test_interpreter_profile): + (test_cvt_values): + (test_interpreter_cvt): + (test_interpreter_head): + (sfnt_make_test_interpreter): + (struct sfnt_interpreter_test): + (sfnt_run_interpreter_test): + (struct sfnt_generic_test_args): + (sfnt_generic_check): + (sfnt_check_srp0): + (sfnt_check_szp0): + (sfnt_check_sloop): + (struct sfnt_rounding_test_args): + (sfnt_check_rounding): + (sfnt_check_smd): + (sfnt_check_scvtci): + (sfnt_check_sswci): + (sfnt_check_ssw): + (sfnt_check_flipon): + (sfnt_check_flipoff): + (npushb_test_args): + (npushw_test_args): + (pushb_test_args): + (pushw_test_args): + (stack_overflow_test_args): + (stack_underflow_test_args): + (rtg_test_args): + (rtg_symmetric_test_args): + (rtg_1_test_args): + (rtg_1_symmetric_test_args): + (rthg_test_args): + (rthg_1_test_args): + (rtdg_test_args): + (rtdg_1_test_args): + (rtdg_2_test_args): + (rtdg_3_test_args): + (else_test_args): + (jmpr_test_args): + (dup_test_args): + (pop_test_args): + (clear_test_args): + (swap_test_args): + (depth_test_args): + (cindex_test_args): + (mindex_test_args): + (raw_test_args): + (loopcall_test_args): + (call_test_args): + (fdef_test_args): + (fdef_1_test_args): + (endf_test_args): + (ws_test_args): + (rs_test_args): + (wcvtp_test_args): + (rcvt_test_args): + (mppem_test_args): + (mps_test_args): + (debug_test_args): + (lt_test_args): + (all_tests): + (main): Implement more instructions. + + * src/sfnt.h (enum sfnt_table, struct sfnt_glyph_metrics): Add + new tables. Add comment. + +2023-01-30 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-01-30 Po Lu + + Update Android port + * cross/ndk-build/ndk-build-shared-library.mk: ($(call + objname,$(LOCAL_MODULE),$(basename $(1)))): + * cross/ndk-build/ndk-build-static-library.mk: ($(call + objname,$(LOCAL_MODULE),$(basename $(1)))): Revert broken typo + fixes. + * src/sfnt.c (TEST_STATIC): Define ARRAYELTS. + (sfnt_table_names): New CVT and FPGM tables. + (sfnt_decompose_compound_glyph, sfnt_decompose_glyph) + (struct sfnt_large_integer, sfnt_multiply_divide_1) + (sfnt_count_leading_zero_bits, sfnt_multiply_divide_2) + (sfnt_multiply_divide, sfnt_mul_fixed, sfnt_div_fixed) + (sfnt_ceil_fixed, sfnt_build_glyph_outline): Fix fixed point + multiplication routines on systems without 64 bit long long + type. + (SFNT_ENABLE_HINTING, struct sfnt_test_dcontext, sfnt_test_move_to) + (sfnt_test_line_to, sfnt_test_curve_to, sfnt_test_get_glyph) + (sfnt_test_free_glyph, sfnt_test_span, sfnt_test_edge_ignore) + (sfnt_read_cvt_table, sfnt_test_edge, sfnt_test_raster) + (sfnt_read_fpgm_table, struct sfnt_unit_vector) + (struct sfnt_interpreter_definition) + (struct sfnt_interpreter_graphics_state, struct sfnt_interpreter) + (sfnt_div_f26dot6, sfnt_mul_f26dot6, sfnt_floor_f26dot6) + (sfnt_ceil_f26dot6, sfnt_round_f26dot6, sfnt_init_graphics_state) + (sfnt_make_interpreter, enum sfnt_interpreter_run_context) + (sfnt_interpret_trap, STACKSIZE, sfnt_set_srounding_state) + (sfnt_skip_code, sfnt_interpret_unimplemented, sfnt_interpret_fdef) + (sfnt_interpret_idef, sfnt_interpret_if, sfnt_interpret_else) + (sfnt_round_none, sfnt_round_to_grid, sfnt_round_to_double_grid) + (sfnt_round_down_to_grid, sfnt_round_up_to_grid) + (sfnt_round_to_half_grid, sfnt_round_super, sfnt_validate_gs) + (sfnt_interpret_run, sfnt_interpret_font_program) + (test_interpreter_profile, test_cvt_values, test_interpreter_cvt) + (test_interpreter_head, sfnt_make_test_interpreter) + (struct sfnt_interpreter_test, sfnt_run_interpreter_test) + (struct sfnt_generic_test_args, sfnt_generic_check) + (sfnt_check_srp0, sfnt_check_szp0, sfnt_check_sloop) + (struct sfnt_rounding_test_args, sfnt_check_rounding) + (sfnt_check_smd, sfnt_check_scvtci, sfnt_check_sswci) + (sfnt_check_ssw, sfnt_check_flipon, sfnt_check_flipoff) + (npushb_test_args, npushw_test_args, pushb_test_args) + (pushw_test_args, stack_overflow_test_args) + (stack_underflow_test_args, rtg_test_args, rtg_symmetric_test_args) + (rtg_1_test_args, rtg_1_symmetric_test_args, rthg_test_args) + (rthg_1_test_args, rtdg_test_args, rtdg_1_test_args) + (rtdg_2_test_args, rtdg_3_test_args, else_test_args) + (jmpr_test_args, dup_test_args, pop_test_args, clear_test_args) + (swap_test_args, depth_test_args, cindex_test_args) + (mindex_test_args, raw_test_args, loopcall_test_args) + (call_test_args, fdef_test_args, fdef_1_test_args, endf_test_args) + (ws_test_args, rs_test_args, wcvtp_test_args, rcvt_test_args) + (mppem_test_args, mps_test_args, debug_test_args, lt_test_args) + (all_tests, main): Check in WIP hinting code. + + * src/sfnt.h (enum sfnt_table): Add `cvt ' and `fpgm' tables. + +2023-01-29 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Check in missing files + * .gitignore: + * cross/lib/_Noreturn.h (_Noreturn): Add missing gnulib files. + + Update Android port + * INSTALL.android (module_target): Clarify documentation. + * cross/ndk-build/ndk-build-shared-library.mk: + * cross/ndk-build/ndk-build-static-library.mk: Fix building Neon objects. + * java/AndroidManifest.xml.in: Add a version code. + +2023-01-28 Po Lu + + Implement `restart-emacs' on Android + * java/org/gnu/emacs/EmacsService.java (restartEmacs): New + function. + * src/android.c (struct android_emacs_service) + (android_init_emacs_service): Add new method. + (android_restart_emacs): New function. + * src/android.h: Update prototypes. + * src/emacs.c (Fkill_emacs): Call android_restart_emacs whenever + appropriate. + +2023-01-28 Po Lu + + Add libtiff support to Android port + * INSTALL.android: Document how to build with libtiff. + + * build-aux/ndk-build-helper-1.mk (NDK_SO_NAME): + * build-aux/ndk-build-helper-2.mk (NDK_A_NAME): + * build-aux/ndk-build-helper-4.mk: Decrease number of duplicate + dependencies found. + * configure.ac (ANDROID_SDK_18_OR_EARLIER, XCONFIGURE, PNG_CFLAGS) + (HAVE_TIFF): Allow using libtiff on Android. + * cross/ndk-build/ndk-clear-vars.mk: Undefine additional + variables. + * cross/ndk-build/ndk-resolve.mk: Split CFLAGS resolution from + a-name resolution, and do not recursively add archive or shared + object names for dependencies of shared libraries. + + * src/Makefile.in (TIFF_CFLAGS): New variable. + (EMACS_CFLAGS): Use it. + +2023-01-28 Po Lu + + * src/image.c (syms_of_image): Fix typo. + +2023-01-28 Po Lu + + Update Android port + * doc/emacs/android.texi (Android File System): Describe an + easier way to disable scoped storage. + * java/AndroidManifest.xml.in: Add new permission to allow that. + * java/README: Add more text describing Java. + * java/org/gnu/emacs/EmacsContextMenu.java (Item): New fields + `isCheckable' and `isChecked'. + (EmacsContextMenu, addItem): New arguments. + (inflateMenuItems): Set checked status as appropriate. + + * java/org/gnu/emacs/EmacsCopyArea.java (perform): Disallow + operations where width and height are less than or equal to + zero. + * lisp/menu-bar.el (menu-bar-edit-menu): Make + execute-extended-command available as a menu item. + * src/androidmenu.c (android_init_emacs_context_menu) + (android_menu_show): + * src/menu.c (have_boxes): Implement menu check boxes. + +2023-01-28 Po Lu + + Set up fontset stuff on Android + * lisp/term/android-win.el (window-system-initialization): + Create default fontset. + + Fix file descriptor leaks + * src/sfntfont.c (sfntfont_read_cmap): + (sfntfont_open): Fix leaks of file descriptors. + + Update from gnulib + * cross/lib/stdalign.in.h (_GL_STDALIGN_H): + (_): + (__alignof_is_defined): + * cross/lib/vasnprintf.c: + * lib/gnulib.mk.in (ANDROID_MIN_SDK): + (HAVE_SPAWN_H): + (LIBGCCJIT_LIBS): + (NATIVE_COMPILATION_AOT): + (NEXT_AS_FIRST_DIRECTIVE_LIMITS_H): + (NEXT_LIMITS_H): + (SIZEOF_LONG): + (stdalign.h): + * ../../../../dev/null: + * lib/stdalign.in.h (_GL_STDALIGN_H): + (_): + (__alignof_is_defined): + * lib/vasnprintf.c: + * m4/gnulib-common.m4 (gl_COMMON_BODY): + * m4/stdalign.m4 (gl_ALIGNASOF): + * m4/stddef_h.m4: Update from gnulib. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-01-28 Po Lu + + Update Android port + * INSTALL.android: Document support for gnutls and libgmp. + * build-aux/ndk-build-helper-1.mk (NDK_SO_NAMES, NDK_INCLUDES) + (SYSTEM_LIBRARIES): + * build-aux/ndk-build-helper-2.mk: Recursively resolve and add + shared library dependencies; even those of static libraries. + * build-aux/ndk-module-extract.awk: Fix makefile_imports code. + * configure.ac (ANDROID_SDK_18_OR_EARLIER, XCONFIGURE) + (LIBGMP_CFLAGS): Enable GMP and gnutls on Android. + + * cross/ndk-build/Makefile.in (LOCAL_EXPORT_C_INCLUDES): + * cross/ndk-build/ndk-build-shared-library.mk: ($(call + objname,$(LOCAL_MODULE),$(basename $(1))))::($$(error + Unsupported suffix)::($(LOCAL_MODULE_FILENAME)): + * cross/ndk-build/ndk-build-static-library.mk: ($(call + objname,$(LOCAL_MODULE),$(basename $(1))))::($$(error + Unsupported suffix): + * cross/ndk-build/ndk-clear-vars.mk: + * cross/ndk-build/ndk-resolve.mk (NDK_SYSTEM_LIBRARIES): + (NDK_LOCAL_EXPORT_C_INCLUDES_$(LOCAL_MODULE)): + (NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE)): + Implement ``LOCAL_ASM_RULE'' and ``LOCAL_C_ADDITIONAL_FLAGS'' + extensions for libgmp. + + * doc/emacs/input.texi (Touchscreens): Document how to + horizontally scroll. + * java/org/gnu/emacs/EmacsActivity.java (attachWindow): Give the + view focus again if necessary. + (onPause): Call right super function. + * java/org/gnu/emacs/EmacsPreferencesActivity.java (onClick): + Clear dumpFileName lest Emacs try to load a nonexistent dump + file. + * java/org/gnu/emacs/EmacsView.java (onDetachedFromWindow) + (onAttachedToWindow): Call super functions. + (onCreateInputConnection): Make sure the IME never obscures + Emacs. + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, onKeyDown) + (onKeyUp): Improve tracking of quit keys. + + * lisp/isearch.el (isearch-mode): Bring up the onscreen + keyboard. + * lisp/touch-screen.el (touch-screen-current-tool): Add three + fields. + (touch-screen-handle-scroll): Allow hscrolling as well. + (touch-screen-handle-touch): Add additional fields to + `touch-screen-current-tool'. + * src/Makefile.in (LIBGMP_CFLAGS, EMACS_CFLAGS): Add new + variable. + * src/android.c (android_run_select_thread): + (android_write_event): Use pthread_cond_broadcast because + pthread_cond_signal does nothing on some Android + versions/devices? + +2023-01-26 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + * doc/emacs/input.texi (On-Screen Keyboards): Fix typo. + + Update Android port + * INSTALL.android: Describe that apksigner is also required. + * configure.ac: Correctly add cross/Makefile to + SUBDIR_MAKEFILES. + * cross/Makefile.in: (config.status): Depend on + $(top_srcdir)/config.status. + * doc/emacs/input.texi (On-Screen Keyboards): Document how to + quit without a physical keyboard. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + function `quit'. + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): New field + `lastVolumeButtonPress'. + (onKeyDown): Quit if necessary. + * m4/ndk-build.m4 (ndk_where_cc): Fix search if CC is not a + single word. + * src/android.c (android_open): Remove unused variable. + (quit): New function. + * src/androidmenu.c (android_process_events_for_menu): Allow + quitting the menu. + * src/xterm.c (handle_one_xevent, x_term_init, syms_of_xterm): + Implement features described above, so they work on free + operating systems. + * src/xterm.h (struct x_display_info): New fields `quit_keysym', + `quit_keysym_time'. + +2023-01-26 Po Lu + + Update Android port + * INSTALL.android: Document how to install sqlite3. + * build-aux/ndk-build-helper-1.mk (SYSTEM_LIBRARIES): + * build-aux/ndk-build-helper-2.mk (SYSTEM_LIBRARIES): Add liblog + and libandroid. + * configure.ac (SQLITE3_LIBS, HAVE_SQLITE3) + (HAVE_SQLITE3_LOAD_EXTENSION): Support on Android. + (APKSIGNER): Look for this new required binary. + + * cross/ndk-build/ndk-build-shared-library.mk (objname): + * cross/ndk-build/ndk-build-static-library.mk (objname): Avoid + duplicate rules by prefixing objects with module type. + + * cross/ndk-build/ndk-build.mk.in (NDK_BUILD_SHARED): Fix + definition. + * cross/ndk-build/ndk-resolve.mk: + (NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE)): Handle new system + libraries. + + * doc/emacs/android.texi (Android File System): Document Android + 10 system restriction. + + * java/AndroidManifest.xml.in: Target Android 33, not 28. + * java/Makefile.in (SIGN_EMACS_V2, APKSIGNER): New variables. + ($(APK_NAME)): Make sure to apply a ``version 2 signature'' to + the package as well. + + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + argument apiLevel. + * java/org/gnu/emacs/EmacsNoninteractive.java (main): + * java/org/gnu/emacs/EmacsThread.java (run): Pass API level. + * m4/ndk-build.m4 (ndk_package_mape): Add package mapping for + sqlite3. + * src/Makefile.in (SQLITE3_CFLAGS): New substition. + (EMACS_CFLAGS): Add that variable. + + * src/android.c (android_api_level): New variable. + (initEmacs): Set it. + (android_file_access_p): Make static. + (android_hack_asset_fd): Adjust for restrictions in Android 29 + and later. + (android_close_on_exec): New function. + (android_open): Adjust to not duplicate file descriptor even if + CLOEXEC. + (android_faccessat): Use fstatat at-func emulation. + + * src/android.h: Update prototypes. + * src/dired.c (file_name_completion_dirp): + * src/fileio.c (file_access_p, Faccess_file): Now that + sys_faccessat takes care of everything, stop calling + android_file_access_p. + +2023-01-26 Po Lu + + Update Android port + * .gitignore: Ignore lib/math.h. + * INSTALL.android: Update accordingly. + * build-aux/ndk-build-helper-1.mk: + * build-aux/ndk-build-helper-2.mk: + * build-aux/ndk-build-helper.mk: + * build-aux/ndk-module-extract.awk: Handle C++ modules. + * configure.ac: Enable libxml2 on Android. + + * cross/ndk-build/Makefile.in: + * cross/ndk-build/ndk-build-shared-library.mk: + * cross/ndk-build/ndk-build-static-library.mk: + * cross/ndk-build/ndk-build.mk.in: + * cross/ndk-build/ndk-resolve.mk: Fix dependency resolution of + includes. + + * java/org/gnu/emacs/EmacsView.java (popupMenu): Fix minimum SDK + version for actual popup menus. + * lib/math.h: Delete file. + + * m4/ndk-build.m4 (ndk_SEARCH_MODULE, ndk_CHECK_MODULES): Look + for nasm and C++ libraries. + + * src/android.c (faccessat): Rename to `android_faccessat'. + * src/android.h: Update prototypes. + * src/dired.c (file_name_completion_dirp): + * src/fileio.c (file_access_p, Faccess_file, file_directory_p): + * src/lisp.h: + * src/lread.c (openp): + * src/process.c (allocate_pty): Use sys_faccessat. + * src/sysdep.c (sys_faccessat): New function. + +2023-01-26 Po Lu + + Remove unused file + * cross/ndk-build/ndk-build.in: Delete unused file. + +2023-01-25 Po Lu + + Update Android port + * java/org/gnu/emacs/EmacsDrawLine.java: Fix this again. Gosh, + how does Android do this. + * java/org/gnu/emacs/EmacsNoninteractive.java (main): Port to + Android 2.3.3. + + * java/org/gnu/emacs/EmacsSdk11Clipboard.java + (EmacsSdk11Clipboard): Port to Android 4.0.3. + * java/org/gnu/emacs/EmacsService.java (getClipboardManager): + New function. + + * src/alloc.c (find_string_data_in_pure): Fix Android alignment + issue. + + * src/android-emacs.c (main): Port to Android 4.4. + * src/android.c (initEmacs): Align stack to 32 bytes, so it ends + up aligned to 16 even though gcc thinks the stack is already + aligned to 16 bytes. + + * src/callproc.c (init_callproc): Use /system/bin/sh instead of + /bin/sh by default. + +2023-01-25 Po Lu + + Remove extra header + * cross/lib/math.h: Delete header. + + Minor fixes to Android port + * java/Makefile.in: (emacs.apk-in): Don't call cp with empty + args. + * java/org/gnu/emacs/EmacsDrawLine.java (perform): Fix for + PostScript filling semantics. + * src/Makefile.in (android-emacs): Build android-emacs directly. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-01-25 Po Lu + + Update Android port + * doc/emacs/android.texi (Android Startup, Android Environment): + Document that restrictions on starting Emacs have been lifted. + + * java/README: Document Java for Emacs developers and how the + Android port works. + + * java/org/gnu/emacs/EmacsApplication.java (EmacsApplication) + (findDumpFile): New function. + (onCreate): Factor out dump file finding functions to there. + + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Update + function declarations. + * java/org/gnu/emacs/EmacsNoninteractive.java + (EmacsNoninteractive): New class. + + * java/org/gnu/emacs/EmacsService.java (EmacsService, getApkFile) + (onCreate): Pass classpath to setEmacsParams. + * java/org/gnu/emacs/EmacsThread.java (EmacsThread): Make run an + override. + * lisp/loadup.el: Don't dump on Android when noninteractive. + * lisp/shell.el (shell--command-completion-data): Handle + inaccessible directories. + * src/Makefile.in (android-emacs): Link with gnulib. + * src/android-emacs.c (main): Implement to launch app-process + and then EmacsNoninteractive. + * src/android.c (setEmacsParams): New argument `class_path'. + Don't set stuff up when running noninteractive. + * src/android.h (initEmacs): Likewise. + * src/androidfont.c (init_androidfont): + * src/androidselect.c (init_androidselect): Don't initialize + when running noninteractive. + * src/emacs.c (load_pdump): New argument `dump_file'. + (android_emacs_init): Give new argument `dump_file' to + `load_pdump'. + * src/sfntfont-android.c (init_sfntfont_android): Don't + initialize when running noninteractive. + +2023-01-25 Po Lu + + Import gnulib modules printf-posix and vasprintf-posix + These are neccessary because Android's printf is missing basic format + modifiers such as t. + + * admin/merge-gnulib (GNULIB_MODULES): Add printf-posix and + vasprintf-posix. Update from gnulib. + * configure.ac (CFLAGS): Add -DHAVE_CONFIG_H. + +2023-01-24 Po Lu + + Make binaries distributed with Emacs work on Android + * doc/lispref/processes.texi (Subprocess Creation): Document + variables containing program names. + * etc/NEWS: Document new variables. + * java/Makefile.in (CROSS_BINS): Add missing etags binary. + * lisp/cedet/semantic/db-ebrowse.el + (semanticdb-create-ebrowse-database): + * lisp/gnus/mail-source.el (mail-source-movemail-program): + * lisp/hexl.el (hexl-program): + * lisp/htmlfontify.el (hfy-etags-bin): + * lisp/ielm.el (inferior-emacs-lisp-mode): + * lisp/mail/rmail.el (rmail-autodetect): + (rmail-insert-inbox-text): + * lisp/org/org-ctags.el (org-ctags-path-to-ctags): + * lisp/progmodes/cperl-mode.el (cperl-etags): + * lisp/speedbar.el (speedbar-fetch-etags-command): + * lisp/textmodes/reftex-global.el (reftex-create-tags-file): Use + new variables. + * src/callproc.c (syms_of_callproc): New variables naming + binaries redistributed with Emacs. + +2023-01-24 Po Lu + + Enable libjpeg on Android + * INSTALL.android: Update documentation. + * build-aux/ndk-build-helper-1.mk: When building shared + libraries, do not link libemacs.so with dependent archive files. + * build-aux/ndk-build-helper-2.mk: Add whole archive + dependencies as well. + + * configure.ac (HAVE_JPEG): Enable on Android. + + * cross/ndk-build/ndk-build-shared-library.mk: Link the shared + object with archive file dependencies. + * cross/ndk-build/ndk-build-static-library.mk: Build all code + position-independently. + * cross/ndk-build/ndk-resolve.mk: Separately resolve a names of + archive and whole archive dependencies. + + * src/Makefile.in (JPEG_CFLAGS): New variable. + (EMACS_CFLAGS): Add it. + +2023-01-24 Po Lu + + Update Android port + * INSTALL.android: Update. + * build-aux/ndk-build-helper-1.mk: Fix typo. + * configure.ac: Enable --with-json on Android. + * cross/ndk-build/ndk-build-shared-library.mk: + (NDK_CFLAGS_$(LOCAL_MODULE)): + (LOCAL_MODULE_FILENAME): + * cross/ndk-build/ndk-build-static-library.mk: + (ALL_OBJECT_FILES$(LOCAL_MODULE)): + (LOCAL_MODULE_FILENAME): Recursively resolve dependencies. + * cross/ndk-build/ndk-resolve.mk: New function. + + * doc/emacs/android.texi (Android Startup): Document how Emacs + is dumped during initial startup. + + * java/Makefile.in (filename): Fix build with multiple shared + libraries. + * java/README: Improve commentary. + * java/org/gnu/emacs/EmacsApplication.java (onCreate): Look and + set dump file. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + function getFingerprint. + * java/org/gnu/emacs/EmacsPreferencesActivity.java (onCreate): + Add option to erase the dump file. + * java/org/gnu/emacs/EmacsService.java (browseUrl): New + function. + * java/org/gnu/emacs/EmacsThread.java (run): Specify dump file + if found. + * lisp/loadup.el: Always dump during loadup on Android. + + * lisp/net/browse-url.el (browse-url--browser-defcustom-type): + (browse-url-default-browser): + (browse-url-default-android-browser): New browse url type. + + * m4/ndk-build.m4 (ndk_package_map): Map jansson to libjansson. + * src/android.c (struct android_emacs_service): New method + `browse_url'. + (getFingerprint): New function. + (android_init_emacs_service): Initialize new method. + (android_browse_url): New function. + + * src/android.h: Update prototypes. + + * src/androidselect.c (Fandroid_browse_url): New function. + (syms_of_androidselect): Define it. + + * src/emacs.c (load_pdump): Don't look in fancy places on + Android. + * src/pdumper.c (Fdump_emacs_portable): Allow dumping while + interactive on Android. + (syms_of_pdumper): New variable `pdumper-fingerprint'. + + * src/sfntfont-android.c (sfntfont_android_composite_bitmap): + Fix unused variables. + +2023-01-24 Po Lu + + Update from gnulib + Update from gnulib. In addition, + + * admin/merge-gnulib: Fix paths for rename. + +2023-01-24 Po Lu + + Improve lib-src/Makefile.in + * lib-src/Makefile.in (DONT_INSTALL): + (clean): Correctly define asset-directory-tool. + + Merge remote-tracking branch 'origin/master' into feature/android + + Fix distclean target + * cross/Makefile.in (distclean bootstrap-clean): Remove Makefile. + +2023-01-24 Po Lu + + Update Android port + * .gitignore: Update with new files. Do not ignore std*.in.h. + * INSTALL.android: Explain how to build Emacs with external + dependencies. + + * Makefile.in (xcompile, cross): Rename to `cross'. + (clean_dirs): Clean cross, not xcompile. + + * README: Document new directories. + + * build-aux/ndk-build-helper-1.mk (build_kind, NDK_SO_NAMES): + * build-aux/ndk-build-helper-2.mk (build_kind, NDK_SO_NAMES): + * build-aux/ndk-build-helper-3.mk (build_kind): + * build-aux/ndk-build-helper-4.mk: + * build-aux/ndk-build-helper.mk (NDK_BUILD_DIR, my-dir): + * build-aux/ndk-module-extract.awk: New files. + * configure.ac: Set up libgif, libwebp, and libpng for + ndk-build. + * cross/ndk-build/Makefile.in (srcdir, NDK_BUILD_ANDROID_MK): + * cross/ndk-build/ndk-build-executable.mk: + * cross/ndk-build/ndk-build-shared-library.mk (eq, objname): + * cross/ndk-build/ndk-build-static-library.mk (eq, objname): + * cross/ndk-build/ndk-build.in (NDK_BUILD_MODULES): + * cross/ndk-build/ndk-build.mk.in (NDK_BUILD_MODULES) + (NDK_BUILD_SHARED): + * cross/ndk-build/ndk-clear-vars.mk: + * cross/ndk-build/ndk-prebuilt-shared-library.mk: + * cross/ndk-build/ndk-prebuilt-static-library.mk: New files. + * doc/emacs/android.texi (Android, Android Environment): + Document clipboard support on Android. + * doc/emacs/emacs.texi (Top): Update menus. + * etc/MACHINES: Document Android. + * java/AndroidManifest.xml.in: Respect new + `--with-android-debug' option. + * java/Makefile.in (CROSS_BINS, CROSS_LIBS): Adjust for rename. + Include ndk-build.mk.:(emacs.apk-in): Depend on shared + libraries. Then, package shared libraries. + * java/org/gnu/emacs/EmacsClipboard.java (EmacsClipboard): New + class. + * java/org/gnu/emacs/EmacsFontDriver.java: Update comment to say + this is unused. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + function `sendExpose'. + * java/org/gnu/emacs/EmacsSdk11Clipboard.java + (EmacsSdk11Clipboard): + * java/org/gnu/emacs/EmacsSdk8Clipboard.java + (EmacsSdk8Clipboard): New classes. + * java/org/gnu/emacs/EmacsView.java (EmacsView, handleDirtyBitmap) + (onDetachedFromWindow): When window is reattached, expose the + frame. + + * lib/Makefile.in (VPATH): + (ALL_CFLAGS): Adjust for rename. + + * lisp/term/android-win.el (android-clipboard-exists-p) + (android-get-clipboard, android-set-clipboard) + (android-clipboard-owner-p, android-primary-selection) + (android-get-clipboard-1, android-get-primary) + (android-selection-bounds, android-encode-select-string) + (gui-backend-get-selection, gui-backend-selection-exists-p) + (gui-backend-selection-owner-p, gui-backend-set-selection): New + functions. + + * m4/ndk-build.m4: New file. + * src/Makefile.in (GIF_CFLAGS, ANDROID_LDFLAGS): New variables. + (EMACS_CFLAGS): Add GIF_CFLAGS. Include + ndk-build.mk. + (libemacs.so): Depend on and link with required + libraries. + + * src/android.c (android_check_compressed_file): New function. + (android_open): Work around Android platform bug. + (sendExpose): New function. + (android_readdir): Set d_type if this is a directory. + + * src/androidgui.h (enum android_event_type) + (struct android_expose_event, union android_event): Add expose + events. + + * src/androidselect.c (struct android_emacs_clipboard) + (android_init_emacs_clipboard, Fandroid_clipboard_owner_p) + (Fandroid_set_clipboard, Fandroid_get_clipboard) + (Fandroid_clipboard_exists_p, init_androidselect) + (syms_of_androidselect): New file. + + * src/androidterm.c (handle_one_android_event): Handle + exposures. + * src/androidterm.h: Update prototypes. + * src/emacs.c (android_emacs_init): Initialize androidselect. + +2023-01-24 Po Lu + + Update android port + * xcompile: Move to cross. + * cross: New directory. + + Update android port + * xcompile: Move to cross. + * cross: New directory. + +2023-01-21 Po Lu + + Improve touch-screen support + * doc/lispref/commands.texi (Touchscreen Events): Document + changes. + * lisp/touch-screen.el (touch-screen-current-tool): Update doc + string. + (touch-screen-precision-scroll): New user option. + (touch-screen-handle-scroll): Use traditional scrolling by + default. + (touch-screen-handle-touch): Adust format of + touch-screen-current-tool. + (touch-screen-track-tap): Don't print waiting for events. + (touch-screen-track-drag): Likewise. Also, don't call UPDATE + until threshold is reached. + (touch-screen-drag-mode-line-1, touch-screen-drag-mode-line): + Improve window dragging. + + * src/fileio.c (Fverify_visited_file_modtime): Fix fs check. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-01-21 Po Lu + + Update Android port + * doc/emacs/android.texi (Android File System): Document that + ls-lisp is now used by default. + * java/org/gnu/emacs/EmacsThread.java (EmacsThread): Name the + thread something meaningful. + * lisp/loadup.el (featurep): Load ls-lisp on Android. + * lisp/ls-lisp.el (ls-lisp-use-insert-directory-program): + Default to off on Android. + * src/android.c (android_is_directory): New fucntion. + (android_fstatat): Handle directories created by + `android_opendir'. + (android_open): Return meaningful file mode. + (struct android_dir): New fields `next', `asset_file' and `fd'. + (android_opendir): Populate those fields. + (android_dirfd): New function. + (android_closedir): Close file descriptor if set. + (android_lookup_asset_directory_fd): New function. + + * src/android.h: Update prototypes. + * src/androidfont.c (androidfont_check_init): New function. + (androidfont_list, androidfont_match, androidfont_draw) + (androidfont_open_font, androidfont_close_font) + (androidfont_has_char, androidfont_encode_char) + (androidfont_text_extents, androidfont_list_family): Initialize + font driver if necessary. + (init_androidfont): Don't initialize Java font if necessary. + + * src/dired.c (open_directory): Return android_dirfd if + appropriate. + (directory_files_internal, file_name_completion_dirp): Implement + correctly for Android. + + * src/fileio.c (check_mutable_filename): New function. + (Fcopy_file, Fdelete_directory_internal, Fdelete_file) + (Frename_file, Fadd_name_to_file, Fmake_symbolic_link) + (Fset_file_modes, Fset_file_times, Ffile_newer_than_file_p) + (Fverify_visited_file_modtime, Fset_visited_file_modtime): Check + that files being written to do not lie in /assets. + + * src/sfntfont-android.c (GET_SCANLINE_BUFFER) + (sfntfont_android_u255to256, sfntfont_android_over_8888_1) + (sfntfont_android_over_8888, sfntfont_android_composite_bitmap): + Optimize on 64-bit ARM devices. + (sfntfont_android_put_glyphs): Optimize away memset if + background need not be filled. + +2023-01-20 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Update Android port + * src/android.c (android_run_select_thread, android_select) + (android_ftruncate): + * src/android.h (ftruncate): Fix compilation on Android 16 and + up. + + Update Android port + * src/android.c (android_run_select_thread, android_init_events) + (android_select): Add alternative android_select implementation + for API 16 and lower. + * src/androidterm.c (handle_one_android_event): Fix + use-after-frees. + + Remove unused file + * xcompile/lib/gnulib.mk.in: Delete. + +2023-01-20 Po Lu + + Update Android port + * .gitignore: Don't ignore verbose.mk.android. + * doc/emacs/Makefile.in (EMACSSOURCES): Add android.texi and + input.texi. + * doc/emacs/android.texi (Android): Document support for the + on-screen keyboard. + (Android Startup): Document how to start Emacs with -Q on + Android. + (Android Environment): Document how Emacs works around the + system ``task killer''. Document changes to frame deletion + behavior. + * doc/emacs/emacs.texi (Top): + * doc/emacs/input.texi (Other Input Devices, On-Screen + Keyboards): Document how to use Emacs with virtual keyboards. + * doc/lispref/commands.texi (Touchscreen Events): Document + changes to `touch-screen-track-drag'. + * doc/lispref/frames.texi (Frames, On-Screen Keyboards): New + node. + * java/AndroidManifest.xml.in: Add settings activity and + appropriate OSK adjustment mode. + * java/org/gnu/emacs/EmacsActivity.java (onCreate): Allow + creating Emacs with -Q. + (onDestroy): Don't remove if killed by the system. + * java/org/gnu/emacs/EmacsContextMenu.java (inflateMenuItems): + Fix context menus again. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Make all + event sending functions return long. + * java/org/gnu/emacs/EmacsPreferencesActivity.java + (EmacsPreferencesActivity): New class. + * java/org/gnu/emacs/EmacsService.java (EmacsService) + (onStartCommand, onCreate, startEmacsService): Start as a + foreground service if necessary to bypass system restrictions. + * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): + * java/org/gnu/emacs/EmacsThread.java (EmacsThread, run): + * java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout) + (onDetachedFromWindow): + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, viewLayout): + Implement frame resize synchronization.. + * java/org/gnu/emacs/EmacsWindowAttachmentManager.java + (EmacsWindowAttachmentManager, removeWindowConsumer): Adjust + accordingly for changes to frame deletion behavior. + * lisp/frame.el (android-toggle-on-screen-keyboard) + (frame-toggle-on-screen-keyboard): New function. + * lisp/minibuffer.el (minibuffer-setup-on-screen-keyboard) + (minibuffer-exit-on-screen-keyboard): New functions. + (minibuffer-setup-hook, minibuffer-exit-hook): Add new functions + to hooks. + + * lisp/touch-screen.el (touch-screen-relative-xy): Accept new + value of window `frame'. Return frame coordinates in that case. + (touch-screen-set-point-commands): New variable. + (touch-screen-handle-point-up): Respect that variable. + (touch-screen-track-drag): Return `no-drag' where appropriate. + (touch-screen-drag-mode-line-1, touch-screen-drag-mode-line): + Refactor to use `no-drag'. + + * src/android.c (struct android_emacs_window): New methods. + Make all event sending functions return the event serial. + (android_toggle_on_screen_keyboard, android_window_updated): New + functions. + * src/android.h: Update prototypes. + * src/androidfns.c (Fandroid_toggle_on_screen_keyboard) + (syms_of_androidfns): New function. + * src/androidgui.h (struct android_any_event) + (struct android_key_event, struct android_configure_event) + (struct android_focus_event, struct android_window_action_event) + (struct android_crossing_event, struct android_motion_event) + (struct android_button_event, struct android_touch_event) + (struct android_wheel_event, struct android_iconify_event) + (struct android_menu_event): Add `serial' fields. + + * src/androidterm.c (handle_one_android_event) + (android_frame_up_to_date): + * src/androidterm.h (struct android_output): Implement frame + resize synchronization. + +2023-01-20 Po Lu + + Check in missing file + * xcompile/verbose.mk.android: New file. + +2023-01-19 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-01-19 Po Lu + + Update Android port + * .gitignore: Add new files. + * INSTALL.android: Explain how to build Emacs for ancient + versions of Android. + * admin/merge-gnulib (GNULIB_MODULES): Add getdelim. + * build-aux/config.guess (timestamp, version): + * build-aux/config.sub (timestamp, version): Autoupdate. + * configure.ac (BUILD_DETAILS, ANDROID_MIN_SDK): + (ANDROID_STUBIFY): Allow specifying CFLAGS via ANDROID_CFLAGS. + Add new configure tests for Android API version when not + explicitly specified. + + * doc/emacs/android.texi (Android): Add reference to ``Other + Input Devices''. + (Android File System): Remove restrictions on directory-files on + the assets directory. + * doc/emacs/emacs.texi (Top): Add Other Input Devices to menu. + * doc/emacs/input.texi (Other Input Devices): New node. + * doc/lispref/commands.texi (Touchscreen Events): Document + changes to touchscreen input events. + * doc/lispref/frames.texi (Pop-Up Menus): Likewise. + * etc/NEWS: Announce changes. + * java/Makefile.in: Use lib-src/asset-directory-tool to generate + an `directory-tree' file placed in /assets. + * java/debug.sh: Large adjustments to support Android 2.2 and + later. + + * java/org/gnu/emacs/EmacsContextMenu.java (inflateMenuItems): + * java/org/gnu/emacs/EmacsCopyArea.java (perform): + * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): + * java/org/gnu/emacs/EmacsDrawLine.java (perform): + * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): + * java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable): + * java/org/gnu/emacs/EmacsFillPolygon.java (perform): + * java/org/gnu/emacs/EmacsFillRectangle.java (perform): + * java/org/gnu/emacs/EmacsGC.java (EmacsGC): + * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): + (destroyHandle): + * java/org/gnu/emacs/EmacsSdk7FontDriver.java (draw): Avoid + redundant canvas saves and restores. + * java/org/gnu/emacs/EmacsService.java (run): + * java/org/gnu/emacs/EmacsView.java (EmacsView): + (handleDirtyBitmap): + * java/org/gnu/emacs/EmacsWindow.java (changeWindowBackground) + (EmacsWindow): Make compatible with Android 2.2 and later. + + * lib-src/Makefile.in (DONT_INSTALL): Add asset-directory-tool + on Android.:(asset-directory-tool{EXEEXT}): New target. + * lib-src/asset-directory-tool.c (struct directory_tree, xmalloc) + (main_1, main_2, main): New file. + + * lib, m4: Merge from gnulib. This will be reverted before + merging to master. + + * lisp/button.el (button-map): + (push-button): + * lisp/frame.el (display-popup-menus-p): Improve touchscreen + support. + * lisp/subr.el (event-start): + (event-end): Handle touchscreen events. + * lisp/touch-screen.el (touch-screen-handle-timeout): + (touch-screen-handle-point-update): + (touch-screen-handle-point-up): + (touch-screen-track-tap): + (touch-screen-track-drag): + (touch-screen-drag-mode-line-1): + (touch-screen-drag-mode-line): New functions. + ([mode-line touchscreen-begin]): + ([bottom-divider touchscreen-begin]): Bind new events. + + * lisp/wid-edit.el (widget-event-point): + (widget-keymap): + (widget-event-start): + (widget-button--check-and-call-button): + (widget-button-click): Improve touchscreen support. + + * src/alloc.c (make_lisp_symbol): Avoid ICE on Android NDK GCC. + (mark_pinned_symbols): Likewise. + + * src/android.c (struct android_emacs_window): New struct. + (window_class): New variable. + (android_run_select_thread): Add workaround for Android platform + bug. + (android_extract_long, android_scan_directory_tree): New + functions. + (android_file_access_p): Use those functions instead. + (android_init_emacs_window): New function. + (android_init_emacs_gc_class): Update signature of `markDirty'. + (android_change_gc, android_set_clip_rectangles): Tell the GC + whether or not clip rects were dirtied. + (android_swap_buffers): Do not look up method every time. + (struct android_dir): Adjust for new directory tree lookup. + (android_opendir, android_readdir, android_closedir): Likewise. + (android_four_corners_bilinear): Fix coding style. + (android_ftruncate): New function. + * src/android.h: Update prototypes. Replace ftruncate with + android_ftruncate when necessary. + + * src/androidterm.c (handle_one_android_event): Pacify GCC. Fix + touch screen tool bar bug. + * src/emacs.c (using_utf8): Fix compilation error. + * src/fileio.c (Ffile_system_info): Return Qnil when fsusage.o + is not built. + * src/filelock.c (BOOT_TIME_FILE): Fix definition for Android. + * src/frame.c (Fx_parse_geometry): Fix uninitialized variable + uses. + * src/keyboard.c (lispy_function_keys): Fix `back'. + * src/menu.c (x_popup_menu_1): Handle touch screen events. + (Fx_popup_menu): Document changes. + + * src/sfnt.c (main): Improve tests. + + * src/sfntfont-android.c (sfntfont_android_put_glyphs): Fix + minor problem. + (init_sfntfont_android): Check for + HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL. + * src/sfntfont.c (struct sfnt_font_desc): New fields `adstyle' + and `languages'. + (sfnt_parse_style): Append tokens to adstyle. + (sfnt_parse_languages): New function. + (sfnt_enum_font_1): Parse supported languages and adstyle. + (sfntfont_list_1): Handle new fields. + (sfntfont_text_extents): Fix uninitialized variable use. + (syms_of_sfntfont, mark_sfntfont): Adjust accordingly. + +2023-01-17 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-01-17 Po Lu + + Update Android port + * doc/emacs/android.texi (Android Fonts): Document that TTC + format fonts are now supported. + * doc/emacs/emacs.texi (Top): Fix menus. + * doc/lispref/commands.texi (Touchscreen Events) + (Key Sequence Input): Document changes to touchscreen events. + * etc/DEBUG: Describe how to debug 64 bit binaries on Android. + + * java/org/gnu/emacs/EmacsCopyArea.java (perform): Explicitly + recycle copy bitmap. + * java/org/gnu/emacs/EmacsDialog.java (EmacsDialog): New class. + * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Use 5 + point PolyLine like X, because Android behaves like Postscript + on some devices and X elsewhere. + * java/org/gnu/emacs/EmacsFillRectangle.java (perform): + Explicitly recycle copy bitmap. + * java/org/gnu/emacs/EmacsPixmap.java (destroyHandle): + Explicitly recycle bitmap and GC if it is big. + * java/org/gnu/emacs/EmacsView.java (EmacsView): Make + `bitmapDirty' a boolean. + (handleDirtyBitmap): Reimplement in terms of that boolean. + Explicitly recycle old bitmap and GC. + (onLayout): Fix lock up. + (onDetachedFromWindow): Recycle bitmap and GC. + + * java/org/gnu/emacs/EmacsWindow.java (requestViewLayout): + Update call to explicitlyDirtyBitmap. + + * src/android.c (android_run_select_thread, android_select): + Really fix android_select. + (android_build_jstring): New function. + * src/android.h: Update prototypes. + * src/androidmenu.c (android_process_events_for_menu): Totally + unblock input before process_pending_signals. + (android_menu_show): Remove redundant unblock_input and + debugging code. + (struct android_emacs_dialog, android_init_emacs_dialog) + (android_dialog_show, android_popup_dialog, init_androidmenu): + Implement popup dialogs on Android. + + * src/androidterm.c (android_update_tools) + (handle_one_android_event, android_frame_up_to_date): Allow + tapping tool bar items. + (android_create_terminal): Add dialog hook. + (android_wait_for_event): Adjust call to android_select. + * src/androidterm.h (struct android_touch_point): New field + `tool_bar_p'. + * src/keyboard.c (read_key_sequence, head_table) + (syms_of_keyboard): Prefix touchscreen events with posn. + * src/keyboard.h (EVENT_HEAD): Handle touchscreen events. + * src/process.c (wait_reading_process_output): Adjust call to + android_select. + * src/sfnt.c (sfnt_read_table_directory): If the first long + turns out to be ttcf, return -1. + (sfnt_read_ttc_header): New function. + (main): Test TTC support. + + * src/sfnt.h (struct sfnt_ttc_header): New structure. + (enum sfnt_ttc_tag): New enum. + + * src/sfntfont-android.c (struct + sfntfont_android_scanline_buffer): New structure. + (GET_SCANLINE_BUFFER): New macro. Try to avoid so much malloc + upon accessing the scanline buffer. + (sfntfont_android_put_glyphs): Do not use SAFE_ALLOCA to + allocate the scaline buffer. + (Fandroid_enumerate_fonts): Enumerate ttc fonts too. + + * src/sfntfont.c (struct sfnt_font_desc): New field `offset'. + (sfnt_enum_font_1): Split out enumeration code from + sfnt_enum_font. + (sfnt_enum_font): Read TTC tables and enumerate each font + therein. + (sfntfont_open): Seek to the offset specified. + + * xcompile/Makefile.in (maintainer-clean): Fix depends here. + +2023-01-16 Po Lu + + Fix display of glyphs with word-sized component offsets on Android + * src/sfnt.c (sfnt_decompose_compound_glyph): Handle correctly + the Y offset in components with ARG_1_AND_2_ARE_WORDS. + (main): Update debugging code. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-01-16 Po Lu + + Update Android port + * doc/emacs/android.texi (Android, Android Environment): Improve + documentation. + * doc/lispref/commands.texi (Touchscreen Events): Document + changes to touchscreen support. + * doc/lispref/display.texi (Defining Faces, Window Systems): + * doc/lispref/frames.texi (Frame Layout, Font and Color + Parameters): + * doc/lispref/os.texi (System Environment): Document Android in + various places. + + * java/org/gnu/emacs/EmacsWindow.java (figureChange): Fix crash. + * lisp/loadup.el: ("touch-screen"): Load touch-screen.el. + * lisp/pixel-scroll.el: Autoload two functions. + * lisp/term/android-win.el: Add require 'touch-screen. + * lisp/touch-screen.el (touch-screen-current-tool) + (touch-screen-current-timer, touch-screen-delay) + (touch-screen-relative-xy, touch-screen-handle-scroll) + (touch-screen-handle-timeout, touch-screen-handle-point-update) + (touch-screen-handle-point-up, touch-screen-handle-touch) + (global-map, touch-screen): New file. + * src/android.c (android_run_debug_thread): Fix build on 64 bit + systems. + (JNICALL, android_put_pixel): Likewise. + (android_transform_coordinates, android_four_corners_bilinear) + (android_fetch_pixel_bilinear, android_project_image_bilinear) + (android_fetch_pixel_nearest_24, android_fetch_pixel_nearest_1) + (android_project_image_nearest): New functions. + * src/androidgui.h (struct android_transform): New structure. + * src/androidterm.c (android_note_mouse_movement): Remove + obsolete TODO. + (android_get_scale_factor): New function. + (android_draw_underwave): Scale underwave correctly. + * src/dispextern.h: Support native image transforms on Android. + * src/image.c (matrix_identity, matrix_rotate) + (matrix_mirror_horizontal, matrix_translate): New functions. + (image_set_transform): Implement native image transforms on + Android. + (Fimage_transforms_p): Implement on Android. + + * src/keyboard.c (make_lispy_event, syms_of_keyboard): Handle + touch screen- menu bar events. + * src/sfnt.c: Fix typo in comment. + * src/sfntfont-android.c (sfntfont_android_blend, U255TO256) + (sfntfont_android_put_glyphs): Avoid redundant swizzling. + * src/sfntfont.c (sfntfont_lookup_char): Fix build on 64 bit + systems. + +2023-01-15 Po Lu + + Implement submenus on Android + * java/org/gnu/emacs/EmacsActivity.java (onCreate): Set the + default theme to Theme.DeviceDefault.NoActionBar if possible. + (onContextMenuClosed): Add hack for Android bug. + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) + (onMenuItemClick): Set flag upon submenu selection. + (inflateMenuItems): Set onClickListener for submenus as well. + (display1): Clear new flag. + * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Fix + rectangle bounds. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): + * java/org/gnu/emacs/EmacsService.java (onCreate): Pass cache + directory. + (sync): New function. + * src/android.c (struct android_emacs_service): New method + `sync'. + (setEmacsParams, initEmacs): Handle cache directory. + (android_init_emacs_service): Initialize new method `sync'. + (android_sync): New function. + * src/androidfns.c (Fx_show_tip): Call both functions. + * src/androidgui.h: Update prototypes. + * src/androidmenu.c (struct android_menu_subprefix) + (android_free_subprefixes, android_menu_show): Handle submenu + prefixes correctly. + * src/androidterm.c (handle_one_android_event): Clear help echo + on MotionNotify like on X. + * src/menu.c (single_menu_item): Enable submenus on Android. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-01-15 Po Lu + + Implement toolkit menus on Android + * java/org/gnu/emacs/EmacsActivity.java (onContextMenuClosed): + New function. + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): + New field `itemAlreadySelected'. + (onMenuItemClick): New function. + (inflateMenuItems): Attach onClickListener as appropriate. + (display1): Clear itemAlreadySelected. + (display): Fix runnable synchronization. + * java/org/gnu/emacs/EmacsNative.java (sendContextMenu): New + function. + * java/org/gnu/emacs/EmacsView.java (popupMenu): + (cancelPopupMenu): Set popupactive correctly. + + * src/android.c (android_run_select_thread): Fix android_select + again. + (android_wait_event): New function. + * src/android.h: Update prototypes. + * src/androidgui.h (enum android_event_type): New + `ANDROID_CONTEXT_MENU' event. + (struct android_menu_event, union android_event): Add new event. + + * src/androidmenu.c (struct android_emacs_context_menu): New + structure. + (android_init_emacs_context_menu): Add `dismiss' method. + (struct android_dismiss_menu_data): New structure. + (android_dismiss_menu, android_process_events_for_menu): New + functions. + (android_menu_show): Set an actual item ID. + (popup_activated): Define when stubify as well. + (Fmenu_or_popup_active_p): New function. + (syms_of_androidmenu): New function. + + * src/androidterm.c (handle_one_android_event): Handle context + menu events. + * src/androidterm.h (struct android_display_info): New field for + menu item ID. + * src/emacs.c (android_emacs_init): Call syms_of_androidmenu. + * src/xdisp.c (note_mouse_highlight): Return if popup_activated + on Android as well. + +2023-01-14 Po Lu + + Fix android_select + * src/android.c (android_run_select_thread, android_select): + Handle EINTR in sem_wait and fix sigsets. + + Add temporary gnulib patch + * xcompile/lib/fpending.c (__fpending): Fix gnulib problem. + + Drop unneeded changes to gnulib + * xcompile/lib/fpending.c (__fpending): + * xcompile/lib/open.c: + * xcompile/lib/unistd.c (_GL_UNISTD_INLINE): Remove Android + patches. + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-01-14 Po Lu + + Update Android port + * java/Makefile.in (clean): Fix distclean and bootstrap-clean rules. + * java/debug.sh (jdb_port): + (attach_existing): + (num_pids): + (line): Add new options to upload a gdbserver binary to the device. + + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): Make + focusedActivities public. + * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): + New class. + * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): Fix + bounds computation. + * java/org/gnu/emacs/EmacsGC.java (markDirty): Set stroke width + explicitly. + * java/org/gnu/emacs/EmacsService.java (EmacsService) + (getLocationOnScreen, nameKeysym): New functions. + * java/org/gnu/emacs/EmacsView.java (EmacsView): Disable focus + highlight. + (onCreateContextMenu, popupMenu, cancelPopupMenu): New + functions. + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): Implement a + kind of ``override redirect'' window for tooltips. + * src/android.c (struct android_emacs_service): New method + `name_keysym'. + (android_run_select_thread, android_init_events): + (android_select): Release select thread on semaphores instead of + signals to avoid one nasty race on SIGUSR2 delivery. + (android_init_emacs_service): Initialize new method. + (android_create_window): Handle CW_OVERRIDE_REDIRECT. + (android_move_resize_window, android_map_raised) + (android_translate_coordinates, android_get_keysym_name) + (android_build_string, android_exception_check): New functions. + * src/android.h: Update prototypes. + + * src/androidfns.c (android_set_parent_frame, Fx_create_frame) + (unwind_create_tip_frame, android_create_tip_frame) + (android_hide_tip, compute_tip_xy, Fx_show_tip, Fx_hide_tip) + (syms_of_androidfns): Implement tooltips and iconification + reporting. + + * src/androidgui.h (enum android_window_value_mask): Add + CWOverrideRedirect. + (struct android_set_window_attributes): Add `override_redirect'. + (ANDROID_IS_MODIFIER_KEY): Recognize Caps Lock. + + * src/androidmenu.c (struct android_emacs_context_menu): New + struct. + (android_init_emacs_context_menu, android_unwind_local_frame) + (android_push_local_frame, android_menu_show, init_androidmenu): + New functions. + + * src/androidterm.c (handle_one_android_event): Fix NULL pointer + dereference. + (android_fullscreen_hook): Handle fullscreen correctly. + (android_draw_box_rect): Fix top line. + (get_keysym_name): Implement function. + (android_create_terminal): Remove scroll bar stubs and add menu + hook. + + * src/androidterm.h: Update prototypes. + * src/emacs.c (android_emacs_init): Initialize androidmenu.c. + * xcompile/Makefile.in: Fix clean rules. + +2023-01-14 Po Lu + + Improve reliability of Android build system + * .gitignore: Add new files. + * INSTALL.android: New file. + * Makefile.in (clean_dirs): Clean xcompile as well. + * admin/merge-gnulib (avoided_flags): Import gnulib into Android + directory as well. + * doc/emacs/android.texi (Android): + * doc/emacs/emacs.texi (Top): New node `Android'. + * java/org/gnu/emacs/EmacsThread.java (run): Use right + executable name. + * lib/Makefile.in (ANDROID_CFLAGS): Use better way to refer to + /src. + (vpath): Delete ugly block of vpath statements. + (mostlyclean): Remove Makefile.android. + * lib/fpending.c (__fpending): + * lib/open.c: + * lib/unistd.c (_GL_UNISTD_INLINE): Revert changes to gnulib in + lib/. + * src/android.h: + * src/androidterm.c: Fix build. + * xcompile/Makefile.in (LIB_SRCDIR): + (LIBSRC_BINARIES, src/verbose.mk): + (PRE_BUILD_DEPS, PHONY): Use gnulib in xcompile/lib/ as opposed + to lib/. + * xcompile/README: Adjust README. + + Check in gnulib with Android patches + * xcompile/lib: Check-in gnulib with patches for Android. + +2023-01-13 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + Fix crashes in Android port + * java/org/gnu/emacs/EmacsService.java (queryTree): Fix NULL + pointer dereference. + * src/android.c (android_query_tree): Set *nchildren_return. + + * .gitignore: Add AndroidManifest.xml. + + * java/AndroidManifest.xml: Remove file that is now generated. + + * src/frame.c (make_monitor_attribute_list): Allow source to be NULL. + +2023-01-13 Po Lu + + Update Android port + * configure.ac (ANDROID_MIN_SDK): New variable. + (DX): Remove and replace with D8. + (XCONFIGURE): Check for the minimum version of Android the cross + compiler compiles for. Generate java/AndroidManifest.xml from + java/AndroidManifest.xml.in. Allow using Zlib on Android. + + * java/AndroidManifest.xml.in: New file. Use the minimum SDK + detected by configure. + + * java/Makefile.in (top_srcdir, version): New variables. + (DX, D8): Replace with D8. + (ANDROID_MIN_SDK, APK_NAME): New variables. + (.PHONY): + (.PRECIOUS): + (classes.dex): + (emacs.apk): Generate $(APK_NAME) instead of `emacs.apk'. + + * java/debug.sh: New option --attach-existing. Attach to an + existing Emacs instance when specified. + + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): New + field `isPaused'. + (invalidateFocus1): Fix infinite recursion. + (detachWindow): Deiconify window. + (attachWindow): Iconify the window if the activity is paused. + (onCreate): Use the ``no title bar'' theme. + (onPause, onResume): New functions. + * java/org/gnu/emacs/EmacsNative.java (sendTouchUp, sendTouchDown) + (sendTouchMove, sendWheel, sendIconified, sendDeiconified): New + functions. + * java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7Typeface): + (list): Remove logging for code that is mostly going to be unused. + * java/org/gnu/emacs/EmacsService.java (ringBell, queryTree) + (getScreenWidth, getScreenHeight, detectMouse): New functions. + * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView) + (surfaceChanged, surfaceCreated, surfaceDestroyed): Add extra + debug logging. Avoid deadlock in surfaceCreated. + + * java/org/gnu/emacs/EmacsView.java (EmacsView): Try very hard + to make the SurfaceView respect Z order. It didn't work. + (handleDirtyBitmap): Copy over the contents from the old bitmap. + (explicitlyDirtyBitmap): New function. + (onLayout): Don't dirty bitmap if unnecessary. + (damageRect, swapBuffers): Don't synchronize so hard. + (onTouchEvent): Call window.onTouchEvent instead. + (moveChildToBack, raise, lower): New functions. + + * java/org/gnu/emacs/EmacsWindow.java (Coordinate): New + subclass. + (pointerMap, isMapped, isIconified, dontFocusOnMap) + (dontAcceptFocus): New fields. + (EmacsWindow): Don't immediately register unmapped window. + (viewLayout): Send configure event outside the lock. + (requestViewLayout): Explicitly dirty the bitmap. + (mapWindow): Register the window now. Respect dontFocusOnMap. + (unmapWindow): Unregister the window now. + (figureChange, onTouchEvent): New functions. + (onSomeKindOfMotionEvent): Handle scroll wheel events. + (reparentTo, makeInputFocus, raise, lower, getWindowGeometry) + (noticeIconified, noticeDeiconified, setDontAcceptFocus) + (setDontFocusOnMap, getDontFocusOnMap): New functions. + * java/org/gnu/emacs/EmacsWindowAttachmentManager.java + (registerWindow, detachWindow): Synchronize. + (noticeIconified, noticeDeiconified): New functions. + (copyWindows): New function. + + * lisp/frame.el (frame-geometry, frame-edges) + (mouse-absolute-pixel-position, set-mouse-absolute-pixel-position) + (frame-list-z-order, frame-restack, display-mouse-p) + (display-monitor-attributes-list): Implement on Android. + + * lisp/mwheel.el (mouse-wheel-down-event): + (mouse-wheel-up-event): + (mouse-wheel-left-event): + (mouse-wheel-right-event): Define on Android. + + * src/android.c (struct android_emacs_service): New methods + `ringBell', `queryTree', `getScreenWidth', `getScreenHeight', + and `detectMouse'. + (struct android_event_queue, android_init_events) + (android_next_event, android_write_event): Remove write limit. + (android_file_access_p): Handle directories correcty. + (android_close): Fix coding style. + (android_fclose): New function. + (android_init_emacs_service): Initialize new methods. + (android_reparent_window): Implement function. + (android_bell, android_set_input_focus, android_raise_window) + (android_lower_window, android_query_tree, android_get_geometry) + (android_get_screen_width, android_get_screen_height) + (android_get_mm_width, android_get_mm_height, android_detect_mouse) + (android_set_dont_focus_on_map, android_set_dont_accept_focus): + New functions. + (struct android_dir): New structure. + (android_opendir, android_readdir, android_closedir): New + functions. + (emacs_abort): Implement here on Android and poke debuggerd into + generating a tombstone. + + * src/android.h: Update prototypes. + + * src/androidfns.c (android_set_parent_frame): New function. + (android_default_font_parameter): Use sane font size by default. + (Fx_display_pixel_width, Fx_display_pixel_height) + (Fx_display_mm_width, Fx_display_mm_height) + (Fx_display_monitor_attributes_list): Rename to start with + `android-'. Implement. Fiddle with documentation to introduce + Android specific nuances. + (Fandroid_display_monitor_attributes_list): New function. + (Fx_frame_geometry, frame_geometry): New function. + (Fandroid_frame_geometry): Implement correctly. + (Fx_frame_list_z_order): Rename to start with `android-'. + (android_frame_list_z_order, Fandroid_frame_list_z_order): + Implement. + (Fx_frame_restack): Rename to start with `android-'. + (Fandroid_frame_restack): ``Implement''. + (Fx_mouse_absolute_pixel_position): Rename to start with + `android-'. + (Fandroid_mouse_absolute_pixel_position): ``Implement''. + (Fx_set_mouse_absolute_pixel_position): Rename to start with + `android-'. + (Fandroid_set_mouse_absolute_pixel_position): ``Implement''. + (Fandroid_detect_mouse): New function. + (android_set_menu_bar_lines): Use FRAME_ANDROID_DRAWABLE when + clearing area. + (android_set_no_focus_on_map, android_set_no_accept_focus): New + functions. + (android_frame_parm_handlers): Register new frame parameter + handlers. + (syms_of_androidfns): Update appropriately. + + * src/androidfont.c (androidfont_draw): Use + FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW. + + * src/androidgui.h (enum android_event_type): New events. + (struct android_touch_event, struct android_wheel_event) + (struct android_iconify_event): New structures. + (union android_event): Add new events. + + * src/androidterm.c (android_clear_frame): Use + FRAME_ANDROID_DRAWABLE instead of FRAME_ANDROID_WINDOW. + (android_flash, android_ring_bell): Implement bell ringing. + (android_toggle_invisible_pointer): Don't TODO function that + can't be implemented. + (show_back_buffer, android_flush_dirty_back_buffer_on): Check if + a buffer flip is required before doing the flip. + (android_lower_frame, android_raise_frame): Implement functions. + (android_update_tools, android_find_tool): New functions. + (handle_one_android_event): Handle new iconification, wheel and + touch events. + (android_read_socket): Implement pending-autoraise-frames. + (android_frame_up_to_date): Implement bell ringing. + (android_buffer_flipping_unblocked_hook): Check if a buffer flip + is required before doing the flip. + (android_focus_frame, android_frame_highlight) + (android_frame_unhighlight): New function. + (android_frame_rehighlight): Implement functions. + (android_iconify_frame): Always display error. + (android_set_alpha): Update commentary. + (android_free_frame_resources): Free frame touch points. + (android_scroll_run, android_flip_and_flush) + (android_clear_rectangle, android_draw_fringe_bitmap) + (android_draw_glyph_string_background, android_fill_triangle) + (android_clear_point, android_draw_relief_rect) + (android_draw_box_rect, android_draw_glyph_string_bg_rect) + (android_draw_image_foreground, android_draw_stretch_glyph_string) + (android_draw_underwave, android_draw_glyph_string_foreground) + (android_draw_composite_glyph_string_foreground) + (android_draw_glyphless_glyph_string_foreground) + (android_draw_glyph_string, android_clear_frame_area) + (android_clear_under_internal_border, android_draw_hollow_cursor) + (android_draw_bar_cursor, android_draw_vertical_window_border) + (android_draw_window_divider): Use FRAME_ANDROID_DRAWABLE + instead of FRAME_ANDROID_WINDOW for drawing operations. + + * src/androidterm.h (struct android_touch_point): New structure. + (struct android_output): New fields. + (FRAME_ANDROID_NEED_BUFFER_FLIP): New macro. + + * src/dired.c (emacs_readdir, open_directory) + (directory_files_internal_unwind, read_dirent) + (directory_files_internal, file_name_completion): Add + indirection over readdir and opendir. Use android variants on + Android. + + * src/dispnew.c (Fopen_termscript): + * src/fileio.c (fclose_unwind): Use emacs_fclose. + (Faccess_file): Call android_file_access_p. + (file_accessible_directory_p): Append right suffix to Android + assets directory. + (do_auto_save_unwind): Use emacs_fclose. + * src/keyboard.c (lispy_function_keys): Use right function key + for page up and page down. + (Fopen_dribble_file): Use emacs_fclose. + + * src/lisp.h: New prototype emacs_fclose. + + * src/lread.c (close_infile_unwind): Use emacs_fclose. + + * src/sfnt.c (sfnt_curve_is_flat): Fix area-squared computation. + (sfnt_prepare_raster): Compute raster width and height + consistently with outline building. + (sfnt_build_outline_edges): Use the same offsets used to set + offy and offx. + (main): Adjust debug code. + + * src/sfntfont-android.c (sfntfont_android_saturate32): Delete + function. + (sfntfont_android_blend, sfntfont_android_blendrgb): Remove + unnecessary debug code. + (sfntfont_android_composite_bitmap): Prevent out of bounds + write. + (sfntfont_android_put_glyphs): Use FRAME_ANDROID_DRAWABLE. + (init_sfntfont_android): Initialize Monospace Serif font to + something sensible. + * src/sfntfont.c (sfntfont_text_extents): Clear glyph metrics + before summing up pcm. + (sfntfont_draw): Use s->font instead of s->face->font. + + * src/sysdep.c (emacs_fclose): Wrap around android_fclose on + android. + + * src/term.c (Fsuspend_tty): + (delete_tty): Use emacs_fclose. + * src/verbose.mk.in (AM_V_DX): Replace with D8 version. + +2023-01-11 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-01-11 Po Lu + + Bring up the sfnt-android font driver + * configure.ac (ANDROID_CFLAGS): Add sfnt-related font objects + to ANDROID_OBJ when not building stubs. + * lisp/startup.el (android-fonts-enumerated): New variable. + (normal-top-level): Set it. Also enumerate fonts as early as + possible upon startup. + + * src/alloc.c (cleanup_vector): Only finalize Android font + entities. + (garbage_collect): Mark sfntfont.c. + + * src/android.c (struct android_emacs_drawable): New field + `damage_rect'. + (android_init_emacs_drawable): Initialize + Lorg/gnu/emacs/EmacsDrawable;#damageRect(Landroid/graphics/rect;)V. + (android_create_gc): Initialize cached GC fields. + (android_free_gc): Free cached GC clip rectangles. + (android_change_gc): Cache fields as appropriate. + (android_set_clip_rectangles): Set cached clip rectangles for + easy access from C. + (android_get_gc_values): Use cached values. + (android_get_image): Remove obsolete comment. + (android_lock_bitmap, android_damage_window): New functions that + don't parallel anything on X. + + * src/android.h: Update prototypes. + + * src/androidfns.c (android_default_font_parameter): Set Droid + Sans Mono as the default monospace font. + (Fx_create_frame): Register the sfntfont driver. + + * src/androidgui.h (struct android_gc): Add C side caches for + clip rectangles and the foreground and background colors. + + * src/androidterm.h: Update prototypes. + + * src/dispextern.h (struct gui_box): New struct. + (gui_union_rectangles): New function. + + * src/emacs.c (android_emacs_init): Initialize Android font + stuff late. + * src/font.c (font_make_entity): Clear `is_android' field on + Android. + (font_make_entity_android): Set `is_android' field. + * src/font.h (struct font_entity): New field `is_android'. + + * src/print.c (print_vectorlike): Don't print private data, + which could include Lisp_Misc. + + * src/sfnt.c (sfnt_read_cmap_format_0, sfnt_read_cmap_format_2) + (sfnt_read_cmap_format_4, sfnt_read_cmap_format_6) + (sfnt_read_cmap_format_8, sfnt_read_cmap_format_12): Remove + buggy pragmas. + (sfnt_lookup_glyph_4_1): New function. + (sfnt_lookup_glyph_4): Handle malformed lookup tables found on + Android. + (sfnt_lookup_glyph): Fix overflow problems in glyph checks. + (sfnt_read_glyph): Handle empty glyphs. This implements some + behavior which everyone else seems to as well, but I can't find + documented in the TrueType Reference Manual. + (sfnt_free_glyph): Export correctly. + (sfnt_transform_coordinates): Make faster. + (sfnt_lerp_half): Fix lerping in some cases. + (sfnt_decompose_glyph): Handle empty glyphs correctly. Close + contours manually instead of waiting for the edge building + process to do that. This lets curves be handled correctly. + (struct sfnt_build_glyph_outline_context): Move internal struct + back to sfnt.c. + (sfnt_build_append): Fix detection of initial entry. + (sfnt_curve_to_and_build_1): Fix De Casteljau implementation. + (sfnt_curve_to_and_build): Use fixed point arithmetic to scale + outlines. + (sfnt_build_glyph_outline): Clear reference counts. Use fixed + point arithmetic. + (sfnt_prepare_raster): Align rasters to 4 bytes, + SFNT_POLY_ALIGNMENT. Fix calculation of offx and offy. + (sfnt_step_edge_by): Step edge by previously computed step_x. + (sfnt_build_outline_edges): Adjust for already closed contours. + Ignore edges abandoned after grid fit. Also precompute step_x + to avoid multiplication on each span rastered. + (sfnt_poly_edges): Improve alignment. + (sfnt_fill_span): Rewrite to avoid control flow in while loop. + (sfnt_poly_span): Remove unnecessary code. + (sfnt_raster_glyph_outline): Use raster stride instead of width. + (sfnt_test_edge, sfnt_test_raster, main): Improve debugging + code. + + * src/sfnt.h (struct sfnt_glyph_outline): Add refcount field to + outline. + (struct sfnt_build_glyph_outline_context): Remove private + struct. + (struct sfnt_raster): Add refcount field to raster. + (struct sfnt_edge): Improve doc. Add `source_x' field used when + built with TEST. + (SFNT_CEIL_FIXED): New macro. + + * src/sfntfont-android.c (sfntfont_android_saturate32) + (sfntfont_android_scale32, sfntfont_android_mul8x2) + (sfntfont_android_blend, U255TO256) + (sfntfont_android_composite_bitmap, sfntfont_android_union_boxes) + (sfntfont_android_put_glyphs, sfntfont_android_get_cache): New + functions. + (android_sfntfont_driver): New font driver. + (Fandroid_enumerate_fonts): New function. + (syms_of_sfntfont_android_for_pdumper, init_sfntfont_android) + (syms_of_sfntfont_android): Initialize default fonts, special + family mapping and font driver. + * src/sfntfont.c (struct sfnt_font_desc): New fields + `char_cache', `cmap_invalid' and `subtable'. + (sfnt_setup_coding_system): Improve commentary. Add default + branch. Fix return value. + (sfnt_safe_encode_coding_object_1) + (sfnt_safe_encode_coding_object_2): + (sfnt_safe_encode_coding_object): Use decode_coding_object + instead of encode_coding_object. + (sfnt_decode_font_string): Adjust for rename. + (sfnt_decode_foundry_name): New function. + (sfnt_weight_descriptions, sfnt_slant_descriptions) + (sfnt_width_descriptions): Fix definitions. + (sfnt_parse_style): Make function work. + (sfnt_enum_font): Initialize designer, char-cache and subtable + platform ID. + (sfntfont_charset_for_name, mark_sfntfont) + (sfntfont_charset_for_cmap): New functions. + (syms_of_sfntfont): New variable `sfnt-default-family-alist'. + + * src/sfntfont.h (_SFNTFONT_H_): Update prototypes. + + * src/xdisp.c (gui_union_rectangles): New function. + +2023-01-08 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + + * configure.ac (ANDROID_OBJS): Add sfntfont files. + + Check in new files + * src/sfnt.h: + * src/sfntfont-android.c: + * src/sfntfont.c: + * src/sfntfont.h: New files. + +2023-01-08 Po Lu + + Update Android port + Note that the port currently does not work as of this check-in. + + * src/android.c (android_change_gc): Fix situations where clip + rects are cleared. + (android_create_pixmap_from_bitmap_data): Fix bitmap data + iteration. + * src/androidfns.c (Fx_show_tip, Fx_hide_tip): Remove annoying + errors. + + * src/androidgui.h (enum android_event_type): + (struct android_crossing_event): + (struct android_motion_event): + (struct android_button_event): + (union android_event): New crossing, motion and button events. + + * src/androidterm.c (android_note_mouse_movement) + (mouse_or_wdesc_frame, android_construct_mouse_click) + (handle_one_android_event, android_mouse_position) + (android_wait_for_event, android_set_window_size_1) + (android_bitmap_icon, android_free_frame_resources) + (syms_of_androidterm): New functions. Handle crossing, motion + and button events. + + * src/androidterm.h (struct android_display_info): New field + `last_mouse_movement_time'. + (struct android_output): Remove unused `need_buffer_flip' field. + + * src/emacs.c (android_emacs_init): Initialize sfntfont. + * src/frame.c (syms_of_frame): Set frame_inhibit_implied_resize + to some reasonable value. + * src/frame.h (GCALIGNED_STRUCT): Set wait_event_type on + Android. + + * src/sfnt.c (eassert): + (TEST_STATIC): + (available): + (enum sfnt_table): + (sfnt_table_names): + (SFNT_ENDOF): + (struct sfnt_table_directory): + (enum sfnt_scaler_type): + (sfnt_coerce_fixed): + (struct sfnt_hhea_table): + (struct sfnt_cmap_table): + (enum sfnt_platform_id): + (enum sfnt_unicode_platform_specific_id): + (enum sfnt_macintosh_platform_specific_id): + (enum sfnt_microsoft_platform_specific_id): + (struct sfnt_cmap_encoding_subtable): + (struct sfnt_cmap_encoding_subtable_data): + (struct sfnt_cmap_format_0): + (struct sfnt_cmap_format_2_subheader): + (struct sfnt_cmap_format_2): + (struct sfnt_cmap_format_4): + (struct sfnt_cmap_format_6): + (struct sfnt_cmap_format_8_or_12_group): + (struct sfnt_cmap_format_8): + (struct sfnt_cmap_format_12): + (struct sfnt_maxp_table): + (struct sfnt_loca_table_short): + (struct sfnt_loca_table_long): + (struct sfnt_glyf_table): + (struct sfnt_simple_glyph): + (struct sfnt_compound_glyph_component): + (struct sfnt_compound_glyph): + (struct sfnt_glyph): + (sfnt_read_table_directory): + (file): + (sfnt_read_cmap_table): + (sfnt_read_head_table): + (success): + (sfnt_read_hhea_table): + (sfnt_read_loca_table_short): + (sfnt_read_loca_table_long): + (sfnt_read_maxp_table): + (sfnt_read_glyf_table): + (sfnt_read_compound_glyph): + (sfnt_read_glyph): + (struct sfnt_point): + (sfnt_expand_compound_glyph_context): + (sfnt_decompose_compound_glyph): + (struct sfnt_glyph_outline): + (enum sfnt_glyph_outline_flags): + (struct sfnt_build_glyph_outline_context): + (sfnt_build_append): + (sfnt_build_glyph_outline): + (struct sfnt_raster): + (struct sfnt_edge): + (sfnt_prepare_raster): + (sfnt_build_outline_edges): + (sfnt_raster_glyph_outline): Move structures to sfnt.h. + + (struct sfnt_long_hor_metric): + (struct sfnt_hmtx_table): + (struct sfnt_glyph_metrics): + (sfnt_read_hmtx_table): + (sfnt_lookup_glyph_metrics): + (sfnt_read_name_table): + (sfnt_find_name): + (sfnt_read_meta_table): + (sfnt_find_metadata): + (sfnt_test_edge_ignore): New functions. + (main): Add new tests. + * src/xdisp.c (redisplay_tool_bar): + +2023-01-08 Po Lu + + Delete unused files + * java/org/gnu/emacs/EmacsPaintQueue.java + * java/org/gnu/emacs/EmacsPaintReq.java: Remove files. + + Update Java part of Android port + * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea, perform) + (paintTo): + * java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine): + * java/org/gnu/emacs/EmacsDrawPoint.java (EmacsDrawPoint): + * java/org/gnu/emacs/EmacsDrawRectangle.java (EmacsDrawRectangle) + (paintTo): + * java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable): + * java/org/gnu/emacs/EmacsFillPolygon.java (EmacsFillPolygon): + * java/org/gnu/emacs/EmacsFillRectangle.java + (EmacsFillRectangle): + * java/org/gnu/emacs/EmacsFontDriver.java (EmacsFontDriver): + * java/org/gnu/emacs/EmacsGC.java (EmacsGC): + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): + * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): + * java/org/gnu/emacs/EmacsSdk23FontDriver.java + (EmacsSdk23FontDriver): + * java/org/gnu/emacs/EmacsSdk7FontDriver.java + (EmacsSdk7FontDriver, textExtents1, textExtents, draw): + * java/org/gnu/emacs/EmacsService.java (EmacsService, copyArea): + * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): + * java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout) + (onFocusChanged): + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, run) + (resizeWindow, lockCanvas, getBitmap, onKeyDown, onKeyUp) + (onActivityDetached): Move rendering to main thread. Make + drawing operations completely static. + + Check in new file androidmenu.c + * src/androidmenu.c: New file. + +2023-01-07 Po Lu + + Check in new file sfnt.c + * src/sfnt.c (xmalloc, xrealloc, xfree, eassert, MIN) + (sfnt_table_names, SFNT_ENDOF, struct sfnt_table_directory) + (enum sfnt_scaler_type, sfnt_coerce_fixed, struct sfnt_hhea_table) + (struct sfnt_cmap_table, enum sfnt_platform_id) + (enum sfnt_unicode_platform_specific_id) + (enum sfnt_macintosh_platform_specific_id) + (enum sfnt_microsoft_platform_specific_id) + (struct sfnt_cmap_encoding_subtable) + (struct sfnt_cmap_encoding_subtable_data) + (struct sfnt_cmap_format_0, struct sfnt_cmap_format_2_subheader) + (struct sfnt_cmap_format_2, struct sfnt_cmap_format_4) + (struct sfnt_cmap_format_6, struct sfnt_cmap_format_8_or_12_group) + (struct sfnt_cmap_format_8, struct sfnt_cmap_format_12) + (struct sfnt_maxp_table, struct sfnt_loca_table_short) + (struct sfnt_loca_table_long, struct sfnt_glyf_table) + (struct sfnt_simple_glyph, struct sfnt_compound_glyph_component) + (struct sfnt_compound_glyph, struct sfnt_glyph, _sfnt_swap16) + (_sfnt_swap32, sfnt_swap16, sfnt_find_table) + (sfnt_read_cmap_format_0, sfnt_read_cmap_format_2) + (sfnt_read_cmap_format_4, sfnt_read_cmap_format_6) + (sfnt_read_cmap_format_8, sfnt_read_cmap_format_12) + (sfnt_read_cmap_table_1, sfnt_read_cmap_table, sfnt_lookup_glyph_0) + (sfnt_lookup_glyph_2, sfnt_bsearch_above, sfnt_compare_uint16) + (sfnt_lookup_glyph_4, sfnt_lookup_glyph_6, sfnt_lookup_glyph_8) + (sfnt_lookup_glyph_12, sfnt_lookup_glyph, sfnt_read_head_table) + (sfnt_read_hhea_table, sfnt_read_loca_table_short) + (sfnt_read_loca_table_long, sfnt_read_maxp_table) + (sfnt_read_glyf_table, sfnt_read_simple_glyph) + (sfnt_read_compound_glyph, sfnt_read_glyph, sfnt_free_glyph) + (struct sfnt_point, sfnt_transform_coordinates) + (struct sfnt_compound_glyph_context) + (sfnt_expand_compound_glyph_context, sfnt_round_fixed) + (sfnt_decompose_compound_glyph, sfnt_lerp_half) + (sfnt_decompose_glyph, struct sfnt_glyph_outline) + (enum sfnt_glyph_outline_flags) + (struct sfnt_build_glyph_outline_context, sfnt_build_append) + (sfnt_move_to_and_build, sfnt_line_to_and_build, sfnt_mul_fixed) + (sfnt_div_fixed, sfnt_ceil_fixed, sfnt_floor_fixed) + (sfnt_curve_is_flat, sfnt_curve_to_and_build_1) + (sfnt_curve_to_and_build, sfnt_build_glyph_outline) + (struct sfnt_raster, struct sfnt_edge, sfnt_poly_coverage) + (sfnt_poly_grid_ceil, sfnt_prepare_raster, sfnt_step_edge_by) + (sfnt_build_outline_edges, sfnt_compare_edges, sfnt_poly_edges) + (sfnt_saturate_short, sfnt_fill_span, sfnt_poly_span) + (sfnt_raster_span, sfnt_raster_edge, sfnt_raster_glyph_outline) + (struct sfnt_long_hor_metric, struct sfnt_hmtx_table) + (struct sfnt_glyph_metrics, sfnt_read_hmtx_table) + (sfnt_lookup_glyph_metrics, struct sfnt_test_dcontext) + (sfnt_test_move_to, sfnt_test_line_to, sfnt_test_curve_to) + (sfnt_test_get_glyph, sfnt_test_free_glyph, sfnt_test_span) + (sfnt_test_edge, sfnt_test_raster, main): Check in + 5000-line-long file written by me for reading TrueType and + OpenType fonts with TrueType outlines. + +2023-01-02 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2023-01-02 Po Lu + + Update Android port + * Makefile.in (java): Depend on info. + (MAKEFILE_NAME): + (config.status): Remove unneeded changes. + * configure.ac (BUILD_DETAILS, ANDROID_STUBIFY): Don't require a + C++ compiler on Android. + * java/AndroidManifest.xml: : Set launchMode + appropriately. : New activity. + * java/Makefile.in (CROSS_BINS): Add EmacsClient. + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity) + (onCreate): Use the window attachment manager. + * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea) + (paintTo): Implement clip masks correctly. + * java/org/gnu/emacs/EmacsDrawRectangle.java (getRect, paintTo): + Fix damage tracking rectangles. + * java/org/gnu/emacs/EmacsFontDriver.java (FontSpec, toString): + New function. + (FontMetrics, EmacsFontDriver): Fix signature of textExtents. + * java/org/gnu/emacs/EmacsMultitaskActivity.java + (EmacsMultitaskActivity): New file. + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New + functions sendFocusIn, sendFocusOut, sendWindowAction. + * java/org/gnu/emacs/EmacsPaintQueue.java (run): Fix clipping + handling. + * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): Add + constructor for mutable pixmaps. + * java/org/gnu/emacs/EmacsSdk23FontDriver.java + (EmacsSdk23FontDriver): New file. + * java/org/gnu/emacs/EmacsSdk7FontDriver.java + (EmacsSdk7FontDriver, Sdk7Typeface, Sdk7FontEntity, Sdk7FontObject) + (checkMatch, hasChar, encodeChar): Implement text display and + fix font metrics semantics. + + * java/org/gnu/emacs/EmacsService.java (EmacsService): Remove + availableChildren. + (getLibraryDirectory, onCreate): Pass pixel density to Emacs. + (clearArea): Fix arguments. Switch to using the window + attachment manager. + * java/org/gnu/emacs/EmacsSurfaceView.java (surfaceChanged) + (surfaceCreated): Flip buffers on surface attachment. + * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers): + New argument FORCE. Always swap if it is true. + (onKeyMultiple, onFocusChanged): New functions. + + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, destroyHandle) + (run): Switch to using the window attachment manager. + * java/org/gnu/emacs/EmacsWindowAttachmentManager.java + (EmacsWindowAttachmentManager): New file. + + * lisp/cus-edit.el (custom-button, custom-button-mouse) + (custom-button-pressed): + * lisp/faces.el (tool-bar): Define faces correctly on Android. + * src/android.c (struct android_emacs_pixmap): Add mutable + constructor. + (struct android_emacs_drawable): New structure. + (android_write_event): Check if event queue hasn't yet been + initialized. + (android_select): Set errno to EINTR if pselect fails. + (android_close): Remove unused debugging code. + (android_get_home_directory): New function. + (Java_org_gnu_emacs_EmacsNative_setEmacsParams): Set pixel + density and compute game path. + (android_init_emacs_drawable): New function. + (Java_org_gnu_emacs_EmacsNative_sendKeyPress): New argument + `unicode_char'. Pass it in events. + (Java_org_gnu_emacs_EmacsNative_sendKeyRelease): Likewise. + (Java_org_gnu_emacs_EmacsNative_sendFocusIn) + (Java_org_gnu_emacs_EmacsNative_sendFocusOut) + (Java_org_gnu_emacs_EmacsNative_sendWindowAction): New + functions. + (android_resolve_handle): Export function. + (android_change_gc): Clear clip rects under the right + circumstances. Set right clip mask field. + (android_create_pixmap_from_bitmap_data): Use correct alpha + channels. + (android_create_pixmap): Create mutable pixmap and avoid + redundant color array allocation. + (android_create_bitmap_from_data, android_create_image) + (android_destroy_image, android_put_pixel, android_get_pixel) + (android_get_image, android_put_image, faccessat): New + functions. + + * src/android.h: Update prototypes. + + * src/androidfns.c (android_default_font_parameter): Prefer + monospace to Droid Sans Mono. + * src/androidfont.c (struct android_emacs_font_driver): New + method `draw'. + (struct android_emacs_font_spec): New field `dpi'. + (struct androidfont_info): Add font metrics cache. + (android_init_font_driver, android_init_font_spec): Adjust + accordingly. + (androidfont_from_lisp, androidfont_from_java): Handle new + fields. + (androidfont_draw): Implement function. + (androidfont_open_font): Set pixel size correctly. + (androidfont_close_font): Free metrics cache. + (androidfont_cache_text_extents) + (androidfont_check_cached_extents): New functions. + (androidfont_text_extents): Cache glyph metrics somewhere for + future use. + (androidfont_list_family): Implement function. + + * src/androidgui.h (enum android_event_type): New focus and + window action events. + (enum android_modifier_mask): New masks. + (struct android_key_event): New field `unicode_char'. + (ANDROID_IS_MODIFIER_KEY): Newmacro. + (struct android_focus_event, struct + android_window_action_event): New structs. + (union android_event): Add new fields. + (enum android_image_format, struct android_image): New enums and + structs. + + * src/androidterm.c (android_android_to_emacs_modifiers) + (android_emacs_to_android_modifiers, android_lower_frame) + (android_raise_frame, android_new_focus_frame) + (android_focus_changed, android_detect_focus_change): New + functions. + (handle_one_android_event): Implement focus and key event + handling. + (android_frame_rehighlight): New function. + (android_frame_raise_lower): Implement accordingly. + (android_make_frame_invisible): Clear highlight_frame if + required. + (android_free_frame_resources): Clear x_focus_event_frame if + required. + (android_draw_fringe_bitmap, android_draw_image_foreground) + (android_draw_image_foreground_1) + (android_draw_image_glyph_string): Remove unnecessary code. + (android_create_terminal, android_term_init): Set the baud rate + to something sensible. + * src/androidterm.h (struct android_bitmap_record): Make + structure the same as on X. + (struct android_display_info): New focus tracking fields. + (struct android_output): Likewise. + * src/dispextern.h (struct image): Add ximg and mask_img on + Android. + + * src/emacs.c (android_emacs_init): Fix argc sorting iteration. + + * src/fileio.c (user_homedir): + (get_homedir): Implement correctly on Android. + + * src/font.h (PT_PER_INCH): Define correctly on Android. + + * src/fringe.c (X, swap_nibble, init_fringe_bitmap): Swap fringe + bitmaps correctly on Android. + + * src/image.c (GET_PIXEL, image_create_bitmap_from_data) + (image_create_bitmap_from_file, free_bitmap_record) + (image_unget_x_image_or_dc, struct image_type) + (prepare_image_for_display, image_clear_image_1) + (image_size_in_bytes, x_check_image_size) + (x_create_x_image_and_pixmap, x_destroy_x_image) + (image_check_image_size, image_create_x_image_and_pixmap_1) + (image_destroy_x_image, gui_put_x_image, image_put_x_image) + (image_get_x_image, image_unget_x_image) + (Create_Pixmap_From_Bitmap_Data, image_pixmap_draw_cross) + (MaskForeground, image_types, syms_of_image): Implement all of + the above on Android in terms of an API very similar to X. + + * src/keyboard.c (FUNCTION_KEY_OFFSET, lispy_function_keys): + Define on Android to something sensible. + + * src/lread.c (build_load_history): Fix problem. + +2022-12-31 Po Lu + + Merge remote-tracking branch 'origin/master' into feature/android + +2022-12-31 Po Lu + + Bring up the Android operating system and its window system + * .dir-locals.el (c-mode): Add ANDROID_EXPORT noise macro. + * .gitignore: Add new files to ignore. + * Makefile.in: Adjust for Android. + * admin/merge-gnulib: Add new warning. + * configure.ac: Detect Android. Run cross-configuration for + Android when appropriate. + + * etc/DEBUG: Document how to debug Emacs on Android. + + * java/AndroidManifest.xml: + * java/Makefile.in: + * java/README: + * java/debug.sh: + * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): + * java/org/gnu/emacs/EmacsApplication.java (EmacsApplication): + * java/org/gnu/emacs/EmacsCopyArea.java (EmacsCopyArea): + * java/org/gnu/emacs/EmacsDrawLine.java (EmacsDrawLine): + * java/org/gnu/emacs/EmacsDrawPoint.java (EmacsDrawPoint): + * java/org/gnu/emacs/EmacsDrawRectangle.java + (EmacsDrawRectangle): + * java/org/gnu/emacs/EmacsDrawable.java (EmacsDrawable): + * java/org/gnu/emacs/EmacsFillPolygon.java (EmacsFillPolygon): + * java/org/gnu/emacs/EmacsFillRectangle.java + (EmacsFillRectangle): + * java/org/gnu/emacs/EmacsFontDriver.java (EmacsFontDriver): + * java/org/gnu/emacs/EmacsGC.java (EmacsGC): + * java/org/gnu/emacs/EmacsHandleObject.java (EmacsHandleObject): + * java/org/gnu/emacs/EmacsNative.java (EmacsNative): + * java/org/gnu/emacs/EmacsPaintQueue.java (EmacsPaintQueue): + * java/org/gnu/emacs/EmacsPaintReq.java (EmacsPaintReq): + * java/org/gnu/emacs/EmacsPixmap.java (EmacsPixmap): + * java/org/gnu/emacs/EmacsSdk7FontDriver.java + (EmacsSdk7FontDriver): + * java/org/gnu/emacs/EmacsService.java (class Holder) + (EmacsService): + * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): + * java/org/gnu/emacs/EmacsThread.java (EmacsThread): + * java/org/gnu/emacs/EmacsView.java (EmacsView): + * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): New files + and classes. + + * lib-src/Makefile.in (srcdir): + * lib/Makefile.in (VPATH): + (HAVE_NATIVE_COMP): + (libgnu_a_SOURCES): + (DEPFLAGS): Configure correctly for cross-compiling. + + * lib/faccessat.c: + * lib/fpending.c (__fpending): + * lib/open.c: + * lib/unistd.c (_GL_UNISTD_INLINE): Temporary adjustments to + gnulib. + + * lisp/frame.el (display-graphic-p): + (display-screens): + (display-pixel-height): + (display-pixel-width): + (display-mm-height): + (display-mm-width): + (display-backing-store): + (display-save-under): + (display-planes): + (display-color-cells): + (display-visual-class): Adjust for new window system `android'. + + * lisp/image/wallpaper.el (x-open-connection): Add declaration. + * lisp/loadup.el (featurep): Load up files for Android. + * lisp/net/eww.el (eww-form-submit, eww-form-file) + (eww-form-checkbox, eww-form-select): Adjust faces for android. + * lisp/term/android-win.el: New file. + * src/Makefile.in: Add new targets emacs.so and android-emacs, + then adjust for cross compilation. + * src/alloc.c (cleanup_vector): Clean up Android font entities + as well. + (garbage_collect): Mark androidterm. + * src/android-emacs.c (main): + * src/android.c (ANDROID_THROW, enum android_fd_table_entry_flags) + (struct android_emacs_service, struct android_emacs_pixmap) + (struct android_graphics_point, struct android_event_container) + (struct android_event_queue, android_run_select_thread) + (android_handle_sigusr1, android_init_events, android_pending) + (android_next_event, android_write_event, android_select) + (android_run_debug_thread, android_user_full_name) + (android_get_asset_name, android_fstat, android_fstatat) + (android_file_access_p, android_hack_asset_fd, android_open) + (android_close, JNICALL, android_init_emacs_service) + (android_init_emacs_pixmap, android_init_graphics_point) + (MAX_HANDLE, struct android_handle_entry, android_alloc_id) + (android_destroy_handle, android_resolve_handle) + (android_resolve_handle2, android_change_window_attributes) + (android_create_window, android_set_window_background) + (android_destroy_window, android_init_android_rect_class) + (android_init_emacs_gc_class, android_create_gc, android_free_gc) + (android_change_gc, android_set_clip_rectangles) + (android_reparent_window, android_lookup_method) + (android_clear_window, android_map_window, android_unmap_window) + (android_resize_window, android_move_window, android_swap_buffers) + (android_get_gc_values, android_set_foreground) + (android_fill_rectangle, android_create_pixmap_from_bitmap_data) + (android_set_clip_mask, android_set_fill_style, android_copy_area) + (android_free_pixmap, android_set_background, android_fill_polygon) + (android_draw_rectangle, android_draw_point, android_draw_line) + (android_create_pixmap, android_set_ts_origin, android_clear_area): + * src/android.h (ANDROID_EXPORT): + * src/androidfns.c (android_display_info_for_name) + (check_android_display_info, check_x_display_info, gamma_correct) + (android_defined_color, android_decode_color) + (android_implicitly_set_name, android_explicitly_set_name) + (android_set_tool_bar_lines, android_change_tool_bar_height) + (android_set_tab_bar_lines, android_change_tab_bar_height) + (android_set_scroll_bar_default_height) + (android_set_scroll_bar_default_width, android_icon_verify) + (android_icon, android_make_gc, android_free_gcs) + (unwind_create_frame, do_unwind_create_frame) + (android_default_font_parameter, android_create_frame_window) + (Fx_create_frame, Fxw_color_defined_p, Fxw_color_values) + (Fxw_display_color_p, Fx_display_grayscale_p) + (Fx_display_pixel_width, Fx_display_pixel_height) + (Fx_display_planes, Fx_display_color_cells, Fx_display_screens) + (Fx_display_mm_width, Fx_display_mm_height) + (Fx_display_backing_store, Fx_display_visual_class) + (Fx_display_monitor_attributes_list, Fx_frame_geometry) + (Fx_frame_list_z_order, Fx_frame_restack) + (Fx_mouse_absolute_pixel_position) + (Fx_set_mouse_absolute_pixel_position, Fandroid_get_connection) + (Fx_display_list, Fx_show_tip, Fx_hide_tip) + (android_set_background_color, android_set_border_color) + (android_set_cursor_color, android_set_cursor_type) + (android_set_foreground_color) + (android_set_child_frame_border_width) + (android_set_internal_border_width, android_set_menu_bar_lines) + (android_set_mouse_color, android_set_title, android_set_alpha) + (android_frame_parm_handlers, syms_of_androidfns): + * src/androidfont.c (struct android_emacs_font_driver) + (struct android_emacs_font_spec, struct android_emacs_font_metrics) + (struct android_emacs_font_object, struct android_integer) + (struct androidfont_info, struct androidfont_entity) + (android_init_font_driver, android_init_font_spec) + (android_init_font_metrics, android_init_integer) + (android_init_font_object, androidfont_get_cache) + (androidfont_from_lisp, androidfont_from_java, androidfont_list) + (androidfont_match, androidfont_draw, androidfont_open_font) + (androidfont_close_font, androidfont_has_char) + (androidfont_encode_char, androidfont_text_extents) + (androidfont_list_family, androidfont_driver) + (syms_of_androidfont_for_pdumper, syms_of_androidfont) + (init_androidfont, android_finalize_font_entity): + * src/androidgui.h (_ANDROID_GUI_H_, struct android_rectangle) + (struct android_point, enum android_gc_function) + (enum android_gc_value_mask, enum android_fill_style) + (enum android_window_value_mask) + (struct android_set_window_attributes, struct android_gc_values) + (struct android_gc, enum android_swap_action, enum android_shape) + (enum android_coord_mode, struct android_swap_info) + (NativeRectangle, struct android_any_event) + (struct android_key_event, struct android_configure_event) + (union android_event): + * src/androidterm.c (android_window_to_frame, android_clear_frame) + (android_ring_bell, android_toggle_invisible_pointer) + (android_update_begin, android_update_end, show_back_buffer) + (android_flush_dirty_back_buffer_on, handle_one_android_event) + (android_read_socket, android_frame_up_to_date) + (android_buffer_flipping_unblocked_hook) + (android_query_frame_background_color, android_parse_color) + (android_alloc_nearest_color, android_query_colors) + (android_mouse_position, android_get_focus_frame) + (android_focus_frame, android_frame_rehighlight) + (android_frame_raise_lower, android_make_frame_visible) + (android_make_frame_invisible) + (android_make_frame_visible_invisible, android_fullscreen_hook) + (android_iconify_frame, android_set_window_size_1) + (android_set_window_size, android_set_offset, android_set_alpha) + (android_new_font, android_bitmap_icon, android_free_pixmap_hook) + (android_free_frame_resources, android_delete_frame) + (android_delete_terminal, android_scroll_run) + (android_after_update_window_line, android_flip_and_flush) + (android_clear_rectangle, android_reset_clip_rectangles) + (android_clip_to_row, android_draw_fringe_bitmap) + (android_set_cursor_gc, android_set_mouse_face_gc) + (android_set_mode_line_face_gc, android_set_glyph_string_gc) + (android_set_glyph_string_clipping) + (android_set_glyph_string_clipping_exactly) + (android_compute_glyph_string_overhangs) + (android_clear_glyph_string_rect) + (android_draw_glyph_string_background, android_fill_triangle) + (android_make_point, android_inside_rect_p, android_clear_point) + (android_draw_relief_rect, android_draw_box_rect) + (HIGHLIGHT_COLOR_DARK_BOOST_LIMIT, android_setup_relief_color) + (android_setup_relief_colors, android_draw_glyph_string_box) + (android_draw_glyph_string_bg_rect, android_draw_image_relief) + (android_draw_image_foreground, android_draw_image_foreground_1) + (android_draw_image_glyph_string) + (android_draw_stretch_glyph_string, android_draw_underwave) + (android_draw_glyph_string_foreground) + (android_draw_composite_glyph_string_foreground) + (android_draw_glyphless_glyph_string_foreground) + (android_draw_glyph_string, android_define_frame_cursor) + (android_clear_frame_area, android_clear_under_internal_border) + (android_draw_hollow_cursor, android_draw_bar_cursor) + (android_draw_window_cursor, android_draw_vertical_window_border) + (android_draw_window_divider, android_redisplay_interface) + (frame_set_mouse_pixel_position, get_keysym_name) + (android_create_terminal, android_term_init, syms_of_androidterm) + (mark_androidterm): + * src/androidterm.h (_ANDROID_TERM_H_, struct android_display_info) + (struct android_output, FRAME_ANDROID_OUTPUT, XSCROLL_BAR): New + files. + * src/dired.c (file_attributes): Do not use openat on Android. + * src/dispextern.h (No_Cursor): Define appropriately on Android. + (struct glyph_string, struct face): Make gc field of type struct + android_gc on Android. + * src/dispnew.c (clear_current_matrices, clear_desired_matrices) + (adjust_frame_glyphs_for_window_redisplay, free_glyphs) + (update_frame, scrolling, char_ins_del_cost, update_frame_line) + (init_display_interactive): Disable text terminal support + completely on Android. Fix non-toolkit menus for non-X systems. + * src/editfns.c (Fuser_full_name): Call android_user_full_name. + * src/emacs.c (android_emacs_init): Make main this on Android. + Prohibit argv sorting from exceeding end of argv. + * src/epaths.in: Add path definitions for Android. + + * src/fileio.c (file_access_p): Call android_file_access_p. + (file_name_directory): Avoid using openat on Android. + (Fcopy_file): Adjust to call sys_fstat instead. + (file_directory_p): + (Finsert_file_contents): + (write_region): Likewise. + * src/filelock.c: + * src/fns.c (Flocale_info): Pacify warning on Android. + * src/font.c (font_make_entity_android): New function. + * src/font.h: + * src/frame.c (Fframep): + (Fwindow_system): Handle new window system `android'. Update doc strings. + (Fmake_terminal_frame): Disable on Android. + (gui_display_get_resource): Disable get_string_resource_hook on Android. + (syms_of_frame): New defsym `android'. + + * src/frame.h (GCALIGNED_STRUCT): Add new output data for + Android. + (ENUM_BF): Expand enumerator size. + (FRAME_ANDROID_P, FRAME_WINDOW_P, MOUSE_HL_INFO): Add + definitions for Android. + + * src/image.c (GET_PIXEL): + (image_create_bitmap_from_file): + (image_create_x_image_and_pixmap_1): + (image_get_x_image): + (slurp_file): + (lookup_rgb_color): + (image_to_emacs_colors): + (image_from_emacs_colors): + (image_pixmap_draw_cross): + (image_disable_image): + (MaskForeground): + (gif_load): Add stubs for Android. + + * src/lisp.h: + * src/lread.c (safe_to_load_version, maybe_swap_for_eln1, openp): + * src/pdumper.c (pdumper_load): Call sys_fstat instead of fstat. + * src/process.c (wait_reading_process_output): Use + android_select instead of pselect. + * src/scroll.c: Disable on Android. + * src/sysdep.c (widen_foreground_group, reset_sys_modes) + (init_signals, emacs_fstatat, sys_fstat): New function. + (emacs_open, emacs_open_noquit, emacs_close): Implement + differently on Android. + (close_output_streams): Disable what is not required on Android. + + * src/term.c (OUTPUT1_IF, encode_terminal_code, string_cost) + (string_cost_one_line, per_line_cost, calculate_costs) + (struct fkey_table, tty_append_glyph, produce_glyphs) + (tty_capable_p, Fsuspend_tty, Fresume_tty, device, init_tty) + (maybe_fatal, syms_of_term): Disable text terminal support on + Android. + + * src/termhooks.h (enum output_method): Add android output + method. + (GCALIGNED_STRUCT, TERMINAL_FONT_CACHE): Define for Android. + + * src/terminal.c (Fterminal_live_p): Implement for Android. + + * src/verbose.mk.in (AM_V_GLOBALS): Add JAVAC and DX. + * src/xdisp.c (redisplay_internal): Disable text terminals on Android. + (display_menu_bar): + (display_tty_menu_item): + (draw_row_with_mouse_face): + (expose_frame): Make the non toolkit menu bar work on Android. + + * src/xfaces.c (GCGraphicsExposures): + (x_create_gc): + (x_free_gc): + (Fx_load_color_file): Define for Android. + + * xcompile/Makefile.in (top_srcdir): + (top_builddir): + * xcompile/README: + * xcompile/langinfo.h (nl_langinfo): New files. + +This ChangeLog only chronicles changes constituting the initial +development of the Android port. Refer to other ChangeLog files for a +narrative of unrelated modifications made to Emacs during that time, +and those made after the Android port was installed. + +;; Local Variables: +;; coding: utf-8 +;; End: + + Copyright (C) 2023 Free Software Foundation, Inc. + + This file is part of GNU Emacs. + + GNU Emacs 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 3 of the License, or + (at your option) any later version. + + GNU Emacs 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 GNU Emacs. If not, see . commit bfbdf4eb892935536fc665d6cc986fd669364263 Author: Po Lu Date: Fri Aug 4 14:29:55 2023 +0800 Optimize creation of multibyte menu items on Android * src/androidvfs.c (android_verify_jni_string): Move to android.c. * src/android.c (android_verify_jni_string): New function. (android_build_string): Forgo encoding menu text if TEXT is a multibyte string that's also a valid JNI string. * src/android.h: Update prototypes. diff --git a/src/android.c b/src/android.c index c30d7b58979..bd19107f53a 100644 --- a/src/android.c +++ b/src/android.c @@ -5480,6 +5480,69 @@ android_check_string (Lisp_Object text) return true; } +/* Verify that the specified NULL-terminated STRING is a valid JNI + ``UTF-8'' string. Return 0 if so, 1 otherwise. + + Do not perform GC, enabling NAME to be a direct reference to string + data. + + The native coding system used by the JVM to store strings derives + from UTF-8, but deviates from it in two aspects in an attempt to + better represent the UCS-16 based Java String format, and to let + strings contain NULL characters while remaining valid C strings: + NULL bytes are encoded as two-byte sequences, and Unicode surrogate + pairs encoded as two-byte sequences are prefered to four-byte + sequences when encoding characters above the BMP. */ + +int +android_verify_jni_string (const char *name) +{ + const unsigned char *chars; + + chars = (unsigned char *) name; + while (*chars) + { + /* Switch on the high 4 bits. */ + + switch (*chars++ >> 4) + { + case 0 ... 7: + /* The 8th bit is clean, so this is a regular C + character. */ + break; + + case 8 ... 0xb: + /* Invalid starting byte! */ + return 1; + + case 0xf: + /* The start of a four byte sequence. These aren't allowed + in Java. */ + return 1; + + case 0xe: + /* The start of a three byte sequence. Verify that its + continued. */ + + if ((*chars++ & 0xc0) != 0x80) + return 1; + + FALLTHROUGH; + + case 0xc ... 0xd: + /* The start of a two byte sequence. Verify that the + next byte exists and has its high bit set. */ + + if ((*chars++ & 0xc0) != 0x80) + return 1; + + break; + } + } + + return 0; +} + /* Given a Lisp string TEXT, return a local reference to an equivalent Java string. */ @@ -5492,12 +5555,18 @@ android_build_string (Lisp_Object text) jchar *characters; USE_SAFE_ALLOCA; - /* Directly encode TEXT if it contains no multibyte - characters. This is okay because the Java extended UTF - format is compatible with ASCII. */ - - if (SBYTES (text) == SCHARS (text) - && android_check_string (text)) + /* Directly encode TEXT if it contains no non-ASCII characters, or + is multibyte and a valid Modified UTF-8 string. This is okay + because the Java extended UTF format is compatible with + ASCII. */ + + if ((SBYTES (text) == SCHARS (text) + && android_check_string (text)) + /* If TEXT is a multibyte string, then it's using Emacs's + internal UTF-8 coding system, a significant subset of which + is compatible with JNI. */ + || (STRING_MULTIBYTE (text) + && !android_verify_jni_string (SSDATA (text)))) { string = (*android_java_env)->NewStringUTF (android_java_env, SSDATA (text)); diff --git a/src/android.h b/src/android.h index cecdfab002f..a052d3a3b21 100644 --- a/src/android.h +++ b/src/android.h @@ -105,6 +105,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. extern void android_set_dont_focus_on_map (android_window, bool); extern void android_set_dont_accept_focus (android_window, bool); +extern int android_verify_jni_string (const char *); extern jstring android_build_string (Lisp_Object); extern jstring android_build_jstring (const char *); extern void android_exception_check (void); diff --git a/src/androidvfs.c b/src/androidvfs.c index 2b467bc444f..0d99116c75c 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -3299,9 +3299,6 @@ android_authority_initial (char *name, size_t length) static struct android_vnode *android_saf_tree_from_name (char *, const char *, const char *); -/* Forward declaration. */ -static int android_verify_jni_string (const char *); - /* Ascertain and return whether or not AUTHORITY designates a content provider offering at least one directory tree accessible to Emacs. */ @@ -4437,66 +4434,6 @@ android_saf_move_document (const char *uri, char **doc_id, /* Chain of all open SAF directory streams. */ static struct android_saf_tree_vdir *all_saf_tree_vdirs; -/* Verify that the specified NULL-terminated STRING is a valid JNI - ``UTF-8'' string. Return 0 if so, 1 otherwise. - - The native coding system used by the JVM to store strings derives - from UTF-8, but deviates from it in two aspects in an attempt to - better represent the UCS-16 based Java String format, and to let - strings contain NULL characters while remaining valid C strings: - NULL bytes are encoded as two-byte sequences, and Unicode surrogate - pairs encoded as two-byte sequences are prefered to four-byte - sequences when encoding characters above the BMP. */ - -static int -android_verify_jni_string (const char *name) -{ - const unsigned char *chars; - - chars = (unsigned char *) name; - while (*chars) - { - /* Switch on the high 4 bits. */ - - switch (*chars++ >> 4) - { - case 0 ... 7: - /* The 8th bit is clean, so this is a regular C - character. */ - break; - - case 8 ... 0xb: - /* Invalid starting byte! */ - return 1; - - case 0xf: - /* The start of a four byte sequence. These aren't allowed - in Java. */ - return 1; - - case 0xe: - /* The start of a three byte sequence. Verify that its - continued. */ - - if ((*chars++ & 0xc0) != 0x80) - return 1; - - FALLTHROUGH; - - case 0xc ... 0xd: - /* The start of a two byte sequence. Verify that the - next byte exists and has its high bit set. */ - - if ((*chars++ & 0xc0) != 0x80) - return 1; - - break; - } - } - - return 0; -} - /* Find the document ID of the file within TREE_URI designated by NAME. commit 709195fea6a082e3512c14fe16c4f9ea2f99824c Author: Po Lu Date: Fri Aug 4 08:32:05 2023 +0800 Avoid encoding commonplace characters in tree names * java/org/gnu/emacs/EmacsService.java (getDocumentTrees): Don't encode some characters that need not be escaped within file names. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index a3dea368272..036bc9cf098 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1294,8 +1294,12 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) if (DocumentsContract.isTreeUri (uri) && uri.getAuthority ().equals (providerName) && permission.isReadPermission ()) - /* Make sure the tree document ID is encoded. */ - treeList.add (Uri.encode (DocumentsContract.getTreeDocumentId (uri))); + /* Make sure the tree document ID is encoded. Refrain from + encoding characters such as +:&?#, since they don't + conflict with file name separators or other special + characters. */ + treeList.add (Uri.encode (DocumentsContract.getTreeDocumentId (uri), + " +:&?#")); } return treeList.toArray (new String[0]); commit 1dedd84e4249e2c01628cb5143fb38b37abd7504 Merge: 01849bf8117 0c2152222ad Author: Po Lu Date: Fri Aug 4 07:50:56 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 01849bf811787a201fd2734178ca053d8e22d36a Author: Po Lu Date: Thu Aug 3 20:14:28 2023 +0800 * src/fileio.c (check_vfs_filename): Revert earlier change. diff --git a/src/fileio.c b/src/fileio.c index 1a7a7152844..a0483aa2528 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -184,12 +184,9 @@ #define emacs_fd_to_int(fds) ((fds).asset ? -1 : (fds).fd) /* Establish that ENCODED is not contained within a special directory whose contents are not eligible for Unix VFS operations. Signal a - `file-error' with REASON if it does. + `file-error' with REASON if it does. */ - If REASON is NULL, instead return whether ENCODED is contained - within such a directory. */ - -static bool +static void check_vfs_filename (Lisp_Object encoded, const char *reason) { #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY @@ -199,14 +196,7 @@ check_vfs_filename (Lisp_Object encoded, const char *reason) if (android_is_special_directory (name, "/assets") || android_is_special_directory (name, "/content")) - { - if (!reason) - return true; - - xsignal2 (Qfile_error, build_string (reason), encoded); - } - - return false; + xsignal2 (Qfile_error, build_string (reason), encoded); #endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ } commit 1c429b7fef2e472cbd1dd8e8f237179ed6616975 Merge: 6d44d08e044 588a0363d9a Author: Po Lu Date: Thu Aug 3 19:50:23 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 6d44d08e044dd2a74c6ac65cedb32a8f7a4f82de Author: Po Lu Date: Thu Aug 3 14:50:05 2023 +0800 Isolate fchmodat within the Android VFS layer * src/android.h: Update prototypes. * src/androidvfs.c (unix_vfs_ops, android_unix_chmod, afs_vfs_ops) (android_afs_chmod, content_vfs_ops, android_content_chmod) (authority_vfs_ops, android_authority_chmod, saf_root_vfs_ops) (android_saf_root_chmod, saf_tree_vfs_ops, android_saf_tree_chmod) (saf_file_vfs_ops, saf_new_vfs_ops, android_saf_new_chmod) (root_vfs_ops): Add `chmod' to the list of functions implemented by each vnode. (android_fchmodat): New function. * src/fileio.c (Fset_file_modes): Use `emacs_fchmodat'. * src/lisp.h: * src/sysdep.c (emacs_fchmodat): Delegate to android_fchmodat on Android. diff --git a/src/android.h b/src/android.h index 945bd649c18..cecdfab002f 100644 --- a/src/android.h +++ b/src/android.h @@ -74,6 +74,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. extern int android_renameat_noreplace (int, const char *, int, const char *); extern int android_rename (const char *, const char *); +extern int android_fchmodat (int, const char *, mode_t, int); diff --git a/src/androidvfs.c b/src/androidvfs.c index e3b0b895df3..2b467bc444f 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -206,6 +206,12 @@ Copyright (C) 2023 Free Software Foundation, Inc. /* Make a directory designated by VNODE, like Unix `mkdir'. */ int (*mkdir) (struct android_vnode *, mode_t); + /* Change the access mode of the provided VNODE to MODE. Value is + the same as with `chmod'. FLAGS is passed verbatim from the call + to the delegating at-func, and is probably + AT_SYMLINK_NOFOLLOW. */ + int (*chmod) (struct android_vnode *, mode_t, int); + /* Open the specified VNODE as a directory. Value is a ``directory handle'', or NULL upon failure. */ struct android_vdir *(*opendir) (struct android_vnode *); @@ -616,6 +622,7 @@ android_vfs_canonicalize_name (char *name, size_t *length) static int android_unix_stat (struct android_vnode *, struct stat *); static int android_unix_access (struct android_vnode *, int); static int android_unix_mkdir (struct android_vnode *, mode_t); +static int android_unix_chmod (struct android_vnode *, mode_t, int); static struct android_vdir *android_unix_opendir (struct android_vnode *); /* Vector of VFS operations associated with Unix filesystem VFS @@ -633,6 +640,7 @@ android_vfs_canonicalize_name (char *name, size_t *length) android_unix_stat, android_unix_access, android_unix_mkdir, + android_unix_chmod, android_unix_opendir, }; @@ -878,6 +886,16 @@ android_unix_mkdir (struct android_vnode *vnode, mode_t mode) return mkdir (vp->name, mode); } +static int +android_unix_chmod (struct android_vnode *vnode, mode_t mode, + int flags) +{ + struct android_unix_vnode *vp; + + vp = (struct android_unix_vnode *) vnode; + return fchmodat (AT_FDCWD, vp->name, mode, flags); +} + static struct dirent * android_unix_readdir (struct android_vdir *vdir) { @@ -1585,6 +1603,7 @@ android_hack_asset_fd (AAsset *asset) static int android_afs_stat (struct android_vnode *, struct stat *); static int android_afs_access (struct android_vnode *, int); static int android_afs_mkdir (struct android_vnode *, mode_t); +static int android_afs_chmod (struct android_vnode *, mode_t, int); static struct android_vdir *android_afs_opendir (struct android_vnode *); /* Vector of VFS operations associated with asset VFS nodes. */ @@ -1601,6 +1620,7 @@ android_hack_asset_fd (AAsset *asset) android_afs_stat, android_afs_access, android_afs_mkdir, + android_afs_chmod, android_afs_opendir, }; @@ -2109,6 +2129,14 @@ android_afs_mkdir (struct android_vnode *vnode, mode_t mode) return -1; } +static int +android_afs_chmod (struct android_vnode *vnode, mode_t mode, + int flags) +{ + errno = EROFS; + return -1; +} + static struct dirent * android_afs_readdir (struct android_vdir *vdir) { @@ -2348,6 +2376,7 @@ android_afs_get_directory_name (int dirfd) static int android_content_stat (struct android_vnode *, struct stat *); static int android_content_access (struct android_vnode *, int); static int android_content_mkdir (struct android_vnode *, mode_t); +static int android_content_chmod (struct android_vnode *, mode_t, int); static struct android_vdir *android_content_opendir (struct android_vnode *); /* Vector of VFS operations associated with the content VFS node. */ @@ -2364,6 +2393,7 @@ android_afs_get_directory_name (int dirfd) android_content_stat, android_content_access, android_content_mkdir, + android_content_chmod, android_content_opendir, }; @@ -2571,6 +2601,14 @@ android_content_mkdir (struct android_vnode *vnode, mode_t mode) return 0; } +static int +android_content_chmod (struct android_vnode *vnode, mode_t mode, + int flags) +{ + errno = EACCES; + return 0; +} + static struct dirent * android_content_readdir (struct android_vdir *vdir) { @@ -2823,6 +2861,7 @@ android_check_content_access (const char *uri, int mode) static int android_authority_stat (struct android_vnode *, struct stat *); static int android_authority_access (struct android_vnode *, int); static int android_authority_mkdir (struct android_vnode *, mode_t); +static int android_authority_chmod (struct android_vnode *, mode_t, int); static struct android_vdir *android_authority_opendir (struct android_vnode *); /* Vector of VFS operations associated with the content VFS node. */ @@ -2839,6 +2878,7 @@ android_check_content_access (const char *uri, int mode) android_authority_stat, android_authority_access, android_authority_mkdir, + android_authority_chmod, android_authority_opendir, }; @@ -3118,6 +3158,14 @@ android_authority_mkdir (struct android_vnode *vnode, mode_t mode) return -1; } +static int +android_authority_chmod (struct android_vnode *vnode, mode_t mode, + int flags) +{ + errno = EACCES; + return -1; +} + static struct android_vdir * android_authority_opendir (struct android_vnode *vnode) { @@ -3223,6 +3271,7 @@ android_authority_initial (char *name, size_t length) static int android_saf_root_stat (struct android_vnode *, struct stat *); static int android_saf_root_access (struct android_vnode *, int); static int android_saf_root_mkdir (struct android_vnode *, mode_t); +static int android_saf_root_chmod (struct android_vnode *, mode_t, int); static struct android_vdir *android_saf_root_opendir (struct android_vnode *); /* Vector of VFS operations associated with the SAF root VFS node. */ @@ -3239,6 +3288,7 @@ android_authority_initial (char *name, size_t length) android_saf_root_stat, android_saf_root_access, android_saf_root_mkdir, + android_saf_root_chmod, android_saf_root_opendir, }; @@ -3520,6 +3570,14 @@ android_saf_root_mkdir (struct android_vnode *vnode, mode_t mode) return -1; } +static int +android_saf_root_chmod (struct android_vnode *vnode, mode_t mode, + int flags) +{ + errno = EACCES; + return -1; +} + static struct dirent * android_saf_root_readdir (struct android_vdir *vdir) { @@ -4347,6 +4405,7 @@ android_saf_move_document (const char *uri, char **doc_id, static int android_saf_tree_stat (struct android_vnode *, struct stat *); static int android_saf_tree_access (struct android_vnode *, int); static int android_saf_tree_mkdir (struct android_vnode *, mode_t); +static int android_saf_tree_chmod (struct android_vnode *, mode_t, int); static struct android_vdir *android_saf_tree_opendir (struct android_vnode *); /* Vector of VFS operations associated with SAF tree VFS nodes. */ @@ -4363,6 +4422,7 @@ android_saf_move_document (const char *uri, char **doc_id, android_saf_tree_stat, android_saf_tree_access, android_saf_tree_mkdir, + android_saf_tree_chmod, android_saf_tree_opendir, }; @@ -5100,6 +5160,24 @@ android_saf_tree_mkdir (struct android_vnode *vnode, mode_t mode) return -1; } +static int +android_saf_tree_chmod (struct android_vnode *vnode, mode_t mode, + int flags) +{ + /* Return EACCESS should MODE contain unusual bits besides S_IFDIR | + S_IRUSR | S_IXUSR. */ + + if (mode & ~(S_IFDIR | S_IRUSR | S_IXUSR)) + { + errno = EACCES; + return -1; + } + + /* Otherwise, no further action is necessary, as SAF nodes already + pretend to be S_IFDIR | S_IRUSR | S_IXUSR. */ + return 0; +} + /* Open a database Cursor containing each directory entry within the supplied SAF tree vnode VP. @@ -5523,6 +5601,7 @@ #define android_saf_file_vnode android_saf_tree_vnode android_saf_tree_stat, android_saf_tree_access, android_saf_tree_mkdir, + android_saf_tree_chmod, android_saf_file_opendir, }; @@ -5761,6 +5840,7 @@ #define android_saf_new_vnode android_saf_tree_vnode static int android_saf_new_stat (struct android_vnode *, struct stat *); static int android_saf_new_access (struct android_vnode *, int); static int android_saf_new_mkdir (struct android_vnode *, mode_t); +static int android_saf_new_chmod (struct android_vnode *, mode_t, int); static struct android_vdir *android_saf_new_opendir (struct android_vnode *); /* Vector of VFS operations associated with SAF new VFS nodes. */ @@ -5777,6 +5857,7 @@ #define android_saf_new_vnode android_saf_tree_vnode android_saf_new_stat, android_saf_new_access, android_saf_new_mkdir, + android_saf_new_chmod, android_saf_new_opendir, }; @@ -6038,6 +6119,14 @@ android_saf_new_mkdir (struct android_vnode *vnode, mode_t mode) return 0; } +static int +android_saf_new_chmod (struct android_vnode *vnode, mode_t mode, + int flags) +{ + errno = ENOENT; + return -1; +} + static struct android_vdir * android_saf_new_opendir (struct android_vnode *vnode) { @@ -6125,6 +6214,7 @@ NATIVE_NAME (safPostRequest) (JNIEnv *env, jobject object) android_unix_stat, android_unix_access, android_unix_mkdir, + android_unix_chmod, android_unix_opendir, }; @@ -6810,6 +6900,42 @@ android_faccessat (int dirfd, const char *restrict pathname, return rc; } +/* Like `android_fstatat', but set file modes instead of + checking file status and respect FLAGS. */ + +int +android_fchmodat (int dirfd, const char *pathname, mode_t mode, + int flags) +{ + char buffer[PATH_MAX + 1]; + struct android_vnode *vp; + int rc; + + if (dirfd == AT_FDCWD || pathname[0] == '/') + goto vfs; + + /* Now establish whether DIRFD is a file descriptor corresponding to + an open VFS directory stream. */ + + if (!android_fstatat_1 (dirfd, pathname, buffer, PATH_MAX + 1)) + { + pathname = buffer; + goto vfs; + } + + /* Fall back to fchmodat. */ + return fchmodat (dirfd, pathname, mode, flags); + + vfs: + vp = android_name_file (pathname); + if (!vp) + return -1; + + rc = (*vp->ops->chmod) (vp, mode, flags); + (*vp->ops->close) (vp); + return rc; +} + /* Like `fdopen', but if FD is a parcel file descriptor, ``detach'' it from the original. diff --git a/src/fileio.c b/src/fileio.c index 1ccb871ce49..1a7a7152844 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -3665,17 +3665,9 @@ DEFUN ("set-file-modes", Fset_file_modes, Sset_file_modes, 2, 3, return call4 (handler, Qset_file_modes, absname, mode, flag); encoded = ENCODE_FILE (absname); - - /* Silently ignore attempts to change the access modes of files - within /contents on Android, preventing errors within backup file - creation. */ - - if (check_vfs_filename (encoded, NULL)) - return Qnil; - char *fname = SSDATA (encoded); mode_t imode = XFIXNUM (mode) & 07777; - if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0) + if (emacs_fchmodat (AT_FDCWD, fname, imode, nofollow) != 0) report_file_error ("Doing chmod", absname); return Qnil; diff --git a/src/lisp.h b/src/lisp.h index 5802984a5e7..447912581d7 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -5095,6 +5095,7 @@ maybe_disable_address_randomization (int argc, char **argv) extern int emacs_renameat_noreplace (int, const char *, int, const char *); extern int emacs_rename (const char *, const char *); +extern int emacs_fchmodat (int, const char *, mode_t, int); extern ptrdiff_t emacs_read (int, void *, ptrdiff_t); extern ptrdiff_t emacs_read_quit (int, void *, ptrdiff_t); extern ptrdiff_t emacs_write (int, void const *, ptrdiff_t); diff --git a/src/sysdep.c b/src/sysdep.c index 0a1905c9196..a995bc66741 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -2719,6 +2719,16 @@ emacs_rename (const char *src, const char *dst) #endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */ } +int +emacs_fchmodat (int fd, const char *path, mode_t mode, int flags) +{ +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) + return fchmodat (fd, path, mode, flags); +#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ + return android_fchmodat (fd, path, mode, flags); +#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */ +} + /* Maximum number of bytes to read or write in a single system call. This works around a serious bug in Linux kernels before 2.6.16; see . commit 91a7e9d83f212e478958c2bafd59057ec816cba0 Author: Po Lu Date: Thu Aug 3 10:41:40 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsSafThread.java (CacheToplevel): (EmacsSafThread): (DocIdEntry): (getCache): (pruneCache): (cacheDirectoryFromCursor): (run): (documentIdFromName1): (statDocument1): (openDocumentDirectory1): (openDocument1): Introduce a file status cache and populate it with files within directories as they are opened. * java/org/gnu/emacs/EmacsService.java (createDocument): (createDirectory): (moveDocument): Invalidate the file status cache wherever needed. * src/fileio.c (check_vfs_filename): (Fset_file_modes): Permit `set-file-modes' to silently fail on asset and content files. diff --git a/java/org/gnu/emacs/EmacsSafThread.java b/java/org/gnu/emacs/EmacsSafThread.java index 007ea9acfbd..29cd3fa6bc7 100644 --- a/java/org/gnu/emacs/EmacsSafThread.java +++ b/java/org/gnu/emacs/EmacsSafThread.java @@ -139,11 +139,39 @@ private static final class CacheToplevel /* Map between document names and children. */ HashMap children; + /* Map between document names and file status. */ + HashMap statCache; + /* Map between document IDs and cache items. */ HashMap idCache; }; - private final class DocIdEntry + private static final class StatCacheEntry + { + /* The time at which this cache entry was created. */ + long time; + + /* Flags, size, and modification time of this file. */ + long flags, size, mtime; + + /* Whether or not this file is a directory. */ + boolean isDirectory; + + public + StatCacheEntry () + { + time = SystemClock.uptimeMillis (); + } + + public boolean + isValid () + { + return ((SystemClock.uptimeMillis () - time) + < CACHE_INVALID_TIME * 1000); + } + }; + + private static final class DocIdEntry { /* The document ID. */ String documentId; @@ -162,10 +190,14 @@ private final class DocIdEntry containing this entry, and TOPLEVEL is the toplevel representing it. SIGNAL is a cancellation signal. + RESOLVER is the content provider used to retrieve file + information. + Value is NULL if the file cannot be found. */ public CacheEntry - getCacheEntry (Uri tree, CacheToplevel toplevel, + getCacheEntry (ContentResolver resolver, Uri tree, + CacheToplevel toplevel, CancellationSignal signal) { Uri uri; @@ -272,6 +304,7 @@ private static final class CacheEntry toplevel = new CacheToplevel (); toplevel.children = new HashMap (); + toplevel.statCache = new HashMap (); toplevel.idCache = new HashMap (); cacheToplevels.put (uri, toplevel); return toplevel; @@ -311,7 +344,9 @@ private static final class CacheEntry pruneCache () { Iterator iter; + Iterator statIter; CacheEntry tem; + StatCacheEntry stat; for (CacheToplevel toplevel : cacheToplevels.values ()) { @@ -339,6 +374,25 @@ private static final class CacheEntry iter.remove (); } + + statIter = toplevel.statCache.values ().iterator (); + + while (statIter.hasNext ()) + { + /* Get the cache entry. */ + stat = statIter.next (); + + /* If it's not valid anymore, remove it. Iterating over a + collection whose contents are being removed is + undefined unless the removal is performed using the + iterator's own `remove' function, so tem.remove cannot + be used here. */ + + if (stat.isValid ()) + continue; + + statIter.remove (); + } } postPruneMessage (); @@ -379,8 +433,60 @@ private static final class CacheEntry return cacheEntry; } + /* Cache file status for DOCUMENTID within TOPLEVEL. Value is the + new cache entry. CURSOR is the cursor from where to retrieve the + file status, in the form of the columns COLUMN_FLAGS, + COLUMN_SIZE, COLUMN_MIME_TYPE and COLUMN_LAST_MODIFIED. */ + + private StatCacheEntry + cacheFileStatus (String documentId, CacheToplevel toplevel, + Cursor cursor) + { + StatCacheEntry entry; + int flagsIndex, columnIndex, typeIndex; + int sizeIndex, mtimeIndex; + String type; + + /* Obtain the indices for columns wanted from this cursor. */ + flagsIndex = cursor.getColumnIndex (Document.COLUMN_FLAGS); + sizeIndex = cursor.getColumnIndex (Document.COLUMN_SIZE); + typeIndex = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + mtimeIndex = cursor.getColumnIndex (Document.COLUMN_LAST_MODIFIED); + + /* COLUMN_LAST_MODIFIED is allowed to be absent in a + conforming documents provider. */ + if (flagsIndex < 0 || sizeIndex < 0 || typeIndex < 0) + return null; + + /* Get the file status from CURSOR. */ + entry = new StatCacheEntry (); + entry.flags = cursor.getInt (flagsIndex); + type = cursor.getString (typeIndex); + + if (type == null) + return null; + + entry.isDirectory = type.equals (Document.MIME_TYPE_DIR); + + if (cursor.isNull (sizeIndex)) + /* The size is unknown. */ + entry.size = -1; + else + entry.size = cursor.getLong (sizeIndex); + + /* mtimeIndex is potentially unset, since document providers + aren't obligated to provide modification times. */ + + if (mtimeIndex >= 0 && !cursor.isNull (mtimeIndex)) + entry.mtime = cursor.getLong (mtimeIndex); + + /* Finally, add this entry to the cache and return. */ + toplevel.statCache.put (documentId, entry); + return entry; + } + /* Cache the type and as many of the children of the directory - designated by DOC_ID as possible into TOPLEVEL. + designated by DOCUMENTID as possible into TOPLEVEL. CURSOR should be a cursor representing an open directory stream, with its projection consisting of at least the display name, @@ -435,6 +541,12 @@ private static final class CacheEntry idEntry.documentId = id; entry.children.put (id, idEntry); + /* Cache the file status for ID within TOPELVEL too; if a + directory listing is being requested, it's very likely + that a series of calls for file status will follow. */ + + cacheFileStatus (id, toplevel, cursor); + /* If this constituent is a directory, don't cache any information about it. It cannot be cached without knowing its children. */ @@ -499,6 +611,7 @@ private static final class CacheEntry toplevel = getCache (uri); toplevel.idCache.remove (documentId); + toplevel.statCache.remove (documentId); /* If the parent of CACHENAME is cached, remove it. */ @@ -570,6 +683,7 @@ private static final class CacheEntry toplevel = getCache (uri); toplevel.idCache.remove (documentId); + toplevel.statCache.remove (documentId); /* Now remove DOCUMENTID from CACHENAME's cache entry, if any. */ @@ -619,6 +733,27 @@ private static final class CacheEntry }); } + /* Invalidate the file status cache entry for DOCUMENTID within URI. + Call this when the contents of a file (i.e. the constituents of a + directory file) may have changed, but the document's display name + has not. */ + + public void + postInvalidateStat (final Uri uri, final String documentId) + { + handler.post (new Runnable () { + @Override + public void + run () + { + CacheToplevel toplevel; + + toplevel = getCache (uri); + toplevel.statCache.remove (documentId); + } + }); + } + /* ``Prototypes'' for nested functions that are run within the SAF @@ -857,7 +992,8 @@ public abstract Object runObject (CancellationSignal signal) /* Fetch just the information for this document. */ if (cache == null) - cache = idEntry.getCacheEntry (uri, toplevel, signal); + cache = idEntry.getCacheEntry (resolver, uri, toplevel, + signal); if (cache == null) { @@ -1082,114 +1218,105 @@ type is either NULL (in which case id should also be NULL) or statDocument1 (String uri, String documentId, CancellationSignal signal) { - Uri uriObject; + Uri uriObject, tree; String[] projection; long[] stat; - int flagsIndex, columnIndex, typeIndex; - int sizeIndex, mtimeIndex, flags; - long tem; - String tem1; Cursor cursor; + CacheToplevel toplevel; + StatCacheEntry cache; - uriObject = Uri.parse (uri); + tree = Uri.parse (uri); if (documentId == null) - documentId = DocumentsContract.getTreeDocumentId (uriObject); + documentId = DocumentsContract.getTreeDocumentId (tree); /* Create a document URI representing DOCUMENTID within URI's authority. */ uriObject - = DocumentsContract.buildDocumentUriUsingTree (uriObject, documentId); + = DocumentsContract.buildDocumentUriUsingTree (tree, documentId); - /* Now stat this document. */ + /* See if the file status cache currently contains this + document. */ - projection = new String[] { - Document.COLUMN_FLAGS, - Document.COLUMN_LAST_MODIFIED, - Document.COLUMN_MIME_TYPE, - Document.COLUMN_SIZE, - }; + toplevel = getCache (tree); + cache = toplevel.statCache.get (documentId); - cursor = resolver.query (uriObject, projection, null, - null, null, signal); - - if (cursor == null) - return null; - - /* Obtain the indices for columns wanted from this cursor. */ - flagsIndex = cursor.getColumnIndex (Document.COLUMN_FLAGS); - sizeIndex = cursor.getColumnIndex (Document.COLUMN_SIZE); - typeIndex = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); - mtimeIndex = cursor.getColumnIndex (Document.COLUMN_LAST_MODIFIED); - - if (!cursor.moveToFirst () - /* COLUMN_LAST_MODIFIED is allowed to be absent in a - conforming documents provider. */ - || flagsIndex < 0 || sizeIndex < 0 || typeIndex < 0) + if (cache == null || !cache.isValid ()) { - cursor.close (); - return null; - } + /* Stat this document and enter its information into the + cache. */ - /* Create the array of file status. */ - stat = new long[3]; + projection = new String[] { + Document.COLUMN_FLAGS, + Document.COLUMN_LAST_MODIFIED, + Document.COLUMN_MIME_TYPE, + Document.COLUMN_SIZE, + }; - try - { - flags = cursor.getInt (flagsIndex); + cursor = resolver.query (uriObject, projection, null, + null, null, signal); - stat[0] |= S_IRUSR; - if ((flags & Document.FLAG_SUPPORTS_WRITE) != 0) - stat[0] |= S_IWUSR; + if (cursor == null) + return null; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N - && (flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0) - stat[0] |= S_IFCHR; + try + { + if (!cursor.moveToFirst ()) + return null; - if (cursor.isNull (sizeIndex)) - stat[1] = -1; /* The size is unknown. */ - else - stat[1] = cursor.getLong (sizeIndex); + cache = cacheFileStatus (documentId, toplevel, cursor); + } + finally + { + cursor.close (); + } - tem1 = cursor.getString (typeIndex); + /* If cache is still null, return null. */ - /* Check if this is a directory file. */ - if (tem1.equals (Document.MIME_TYPE_DIR) - /* Files shouldn't be specials and directories at the same - time, but Android doesn't forbid document providers - from returning this information. */ - && (stat[0] & S_IFCHR) == 0) - { - /* Since FLAG_SUPPORTS_WRITE doesn't apply to directories, - just assume they're writable. */ - stat[0] |= S_IFDIR | S_IWUSR | S_IXUSR; + if (cache == null) + return null; + } - /* Directory files cannot be modified if - FLAG_DIR_SUPPORTS_CREATE is not set. */ + /* Create the array of file status and populate it with the + information within cache. */ + stat = new long[3]; - if ((flags & Document.FLAG_DIR_SUPPORTS_CREATE) == 0) - stat[0] &= ~S_IWUSR; - } + stat[0] |= S_IRUSR; + if ((cache.flags & Document.FLAG_SUPPORTS_WRITE) != 0) + stat[0] |= S_IWUSR; - /* If this file is neither a character special nor a - directory, indicate that it's a regular file. */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N + && (cache.flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0) + stat[0] |= S_IFCHR; - if ((stat[0] & (S_IFDIR | S_IFCHR)) == 0) - stat[0] |= S_IFREG; + stat[1] = cache.size; - if (mtimeIndex >= 0 && !cursor.isNull (mtimeIndex)) - { - /* Content providers are allowed to not provide mtime. */ - tem = cursor.getLong (mtimeIndex); - stat[2] = tem; - } - } - finally + /* Check if this is a directory file. */ + if (cache.isDirectory + /* Files shouldn't be specials and directories at the same + time, but Android doesn't forbid document providers + from returning this information. */ + && (stat[0] & S_IFCHR) == 0) { - cursor.close (); + /* Since FLAG_SUPPORTS_WRITE doesn't apply to directories, + just assume they're writable. */ + stat[0] |= S_IFDIR | S_IWUSR | S_IXUSR; + + /* Directory files cannot be modified if + FLAG_DIR_SUPPORTS_CREATE is not set. */ + + if ((cache.flags & Document.FLAG_DIR_SUPPORTS_CREATE) == 0) + stat[0] &= ~S_IWUSR; } + /* If this file is neither a character special nor a + directory, indicate that it's a regular file. */ + + if ((stat[0] & (S_IFDIR | S_IFCHR)) == 0) + stat[0] |= S_IFREG; + + stat[2] = cache.mtime; return stat; } @@ -1389,6 +1516,9 @@ In addition, arbitrary runtime exceptions (such as Document.COLUMN_DISPLAY_NAME, Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, + Document.COLUMN_FLAGS, + Document.COLUMN_LAST_MODIFIED, + Document.COLUMN_SIZE, }; cursor = resolver.query (uriObject, projection, null, null, @@ -1441,6 +1571,7 @@ In addition, arbitrary runtime exceptions (such as Uri treeUri, documentUri; String mode; ParcelFileDescriptor fileDescriptor; + CacheToplevel toplevel; treeUri = Uri.parse (uri); @@ -1450,35 +1581,26 @@ In addition, arbitrary runtime exceptions (such as documentUri = DocumentsContract.buildDocumentUriUsingTree (treeUri, documentId); - if (write || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) - { - /* Select the mode used to open the file. `rw' means open - a stat-able file, while `rwt' means that and to - truncate the file as well. */ + /* Select the mode used to open the file. */ + if (write) + { if (truncate) mode = "rwt"; else mode = "rw"; - - fileDescriptor - = resolver.openFileDescriptor (documentUri, mode, - signal); } else - { - /* Select the mode used to open the file. `openFile' - below means always open a stat-able file. */ + mode = "r"; - if (truncate) - /* Invalid mode! */ - return null; - else - mode = "r"; + fileDescriptor + = resolver.openFileDescriptor (documentUri, mode, + signal); - fileDescriptor = resolver.openFile (documentUri, mode, - signal); - } + /* Every time a document is opened, remove it from the file status + cache. */ + toplevel = getCache (treeUri); + toplevel.statCache.remove (documentId); return fileDescriptor; } diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 8554dadd06e..a3dea368272 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1586,7 +1586,7 @@ In addition, arbitrary runtime exceptions (such as String mimeType, separator, mime, extension; int index; MimeTypeMap singleton; - Uri directoryUri, docUri; + Uri treeUri, directoryUri, docUri; /* Try to get the MIME type for this document. Default to ``application/octet-stream''. */ @@ -1608,15 +1608,15 @@ In addition, arbitrary runtime exceptions (such as } /* Now parse URI. */ - directoryUri = Uri.parse (uri); + treeUri = Uri.parse (uri); if (documentId == null) - documentId = DocumentsContract.getTreeDocumentId (directoryUri); + documentId = DocumentsContract.getTreeDocumentId (treeUri); /* And build a file URI referring to the directory. */ directoryUri - = DocumentsContract.buildChildDocumentsUriUsingTree (directoryUri, + = DocumentsContract.buildChildDocumentsUriUsingTree (treeUri, documentId); docUri = DocumentsContract.createDocument (resolver, @@ -1626,6 +1626,11 @@ In addition, arbitrary runtime exceptions (such as if (docUri == null) return null; + /* Invalidate the file status of the containing directory. */ + + if (storageThread != null) + storageThread.postInvalidateStat (treeUri, documentId); + /* Return the ID of the new document. */ return DocumentsContract.getDocumentId (docUri); } @@ -1638,18 +1643,18 @@ In addition, arbitrary runtime exceptions (such as throws FileNotFoundException { int index; - Uri directoryUri, docUri; + Uri treeUri, directoryUri, docUri; /* Now parse URI. */ - directoryUri = Uri.parse (uri); + treeUri = Uri.parse (uri); if (documentId == null) - documentId = DocumentsContract.getTreeDocumentId (directoryUri); + documentId = DocumentsContract.getTreeDocumentId (treeUri); /* And build a file URI referring to the directory. */ directoryUri - = DocumentsContract.buildChildDocumentsUriUsingTree (directoryUri, + = DocumentsContract.buildChildDocumentsUriUsingTree (treeUri, documentId); /* If name ends with a directory separator character, delete @@ -1669,7 +1674,12 @@ In addition, arbitrary runtime exceptions (such as if (docUri == null) return null; - /* Return the ID of the new document. */ + /* Return the ID of the new document, but first invalidate the + state of the containing directory. */ + + if (storageThread != null) + storageThread.postInvalidateStat (treeUri, documentId); + return DocumentsContract.getDocumentId (docUri); } @@ -1763,7 +1773,15 @@ In addition, arbitrary runtime exceptions (such as /* Now invalidate the caches for both DIRNAME and DOCID. */ if (storageThread != null) - storageThread.postInvalidateCacheDir (uri1, docId, dirName); + { + storageThread.postInvalidateCacheDir (uri1, docId, dirName); + + /* Invalidate the stat cache entries for both the source and + destination directories, since their contents have + changed. */ + storageThread.postInvalidateStat (uri1, dstId); + storageThread.postInvalidateStat (uri1, srcId); + } return (name != null ? DocumentsContract.getDocumentId (name) diff --git a/src/fileio.c b/src/fileio.c index 5ce933ec45b..1ccb871ce49 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -184,9 +184,12 @@ #define emacs_fd_to_int(fds) ((fds).asset ? -1 : (fds).fd) /* Establish that ENCODED is not contained within a special directory whose contents are not eligible for Unix VFS operations. Signal a - `file-error' with REASON if it does. */ + `file-error' with REASON if it does. -static void + If REASON is NULL, instead return whether ENCODED is contained + within such a directory. */ + +static bool check_vfs_filename (Lisp_Object encoded, const char *reason) { #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY @@ -194,11 +197,16 @@ check_vfs_filename (Lisp_Object encoded, const char *reason) name = SSDATA (encoded); - if (android_is_special_directory (name, "/assets")) - xsignal2 (Qfile_error, build_string (reason), encoded); + if (android_is_special_directory (name, "/assets") + || android_is_special_directory (name, "/content")) + { + if (!reason) + return true; + + xsignal2 (Qfile_error, build_string (reason), encoded); + } - if (android_is_special_directory (name, "/content")) - xsignal2 (Qfile_error, build_string (reason), encoded); + return false; #endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ } @@ -3657,8 +3665,14 @@ DEFUN ("set-file-modes", Fset_file_modes, Sset_file_modes, 2, 3, return call4 (handler, Qset_file_modes, absname, mode, flag); encoded = ENCODE_FILE (absname); - check_vfs_filename (encoded, "Trying to change access modes of file" - " within special directory"); + + /* Silently ignore attempts to change the access modes of files + within /contents on Android, preventing errors within backup file + creation. */ + + if (check_vfs_filename (encoded, NULL)) + return Qnil; + char *fname = SSDATA (encoded); mode_t imode = XFIXNUM (mode) & 07777; if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0) commit 60dda3105c9c7eb66480a9cc8a947f6c6bed1a8e Merge: 8ff8a7fd5c5 daa174e56be Author: Po Lu Date: Thu Aug 3 08:25:47 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 8ff8a7fd5c50aaa7721a562a11836b4ec733ba5e Author: Po Lu Date: Wed Aug 2 09:09:53 2023 +0800 Fix reporting of key events containing SYM and META * doc/emacs/android.texi (Android)::(What is Android?): (Android Startup, Android File System, Android Environment) (Android Windowing, Android Fonts, Android Troubleshooting): Improve section titles. (Android Windowing): Describe the relation between keyboard modifiers reported by Android and those in key events. * java/org/gnu/emacs/EmacsWindow.java (onKeyDown, onKeyUp): Clear META_SYM_ON and META_META_MASK when retrieving ASCII characters. * src/androidgui.h: Add ANDROID_META_MASK. * src/androidterm.c (android_android_to_emacs_modifiers) (android_emacs_to_android_modifiers): Transform META to Alt, and vice versa. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 4b8f36a65eb..9d352f3a484 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -26,7 +26,7 @@ Android @end menu @node What is Android? -@section Android history +@section Android History Android is an operating system for mobile devices developed by the Open Handset Alliance, a group of companies interested in developing @@ -59,7 +59,7 @@ What is Android? hope this taste of freedom will inspire users to escape from them. @node Android Startup -@section Starting up Emacs on Android +@section Starting Emacs on Android Emacs is not installed on Android devices from source code or through a package manager. Instead, Emacs is compiled for Android on @@ -155,7 +155,7 @@ Android Startup opened. @node Android File System -@section What files Emacs can access under Android +@section What Files Emacs Can Access on Android @cindex /assets directory, android Emacs exposes a special directory on Android systems: the name of @@ -281,7 +281,7 @@ Android File System on some proprietary versions of Android. @node Android Document Providers -@section Accessing files from other programs under Android +@section Accessing Files from Other Programs on Android @cindex document providers, Android @cindex /content/storage directory, Android @@ -399,7 +399,7 @@ Android Environment $ adb shell "settings put global settings_enable_monitor_phantom_procs false" @end example -@section Running Emacs in the background +@section Running Emacs in the Background @cindex emacs killed, android @cindex emacs in the background, android @@ -429,7 +429,7 @@ Android Environment list of such troublesome manufacturers and sometimes workarounds at @url{https://dontkillmyapp.com/}. -@section Android permissions +@section Android Permissions @cindex external storage, android Android also defines a permissions system that determines what @@ -526,7 +526,7 @@ Android Environment @end itemize @node Android Windowing -@section The Android window system +@section The Android Window System Android has an unusual window system; there, all windows are maximized or full-screen, and only one window can be displayed at a @@ -650,8 +650,21 @@ Android Windowing menu in the system settings, but this procedure may vary by device. +@cindex keyboard modifiers, android + There is a direct relation between physical modifier keys and Emacs +modifiers (@pxref{Modifier Keys}) reported within key events, subject +to a single exception: if @key{Alt} on your keyboard is depressed, +then the @key{Meta} modifier will be reported by Emacs in its place, +and vice versa. This irregularity is since most keyboards posses no +special @key{Meta} key, and the @key{Alt} modifier is seldom employed +in Emacs. + + Bear in mind that Android uses a different name for the @key{Super} +modifier: it is referred to as @key{SYM} on Android keyboards and +within the Settings keymap menu. + @node Android Fonts -@section Font backends and selection under Android +@section Font Backends and Selection under Android @cindex fonts, android Emacs supports two font backends under Android: they are respectively @@ -693,7 +706,7 @@ Android Fonts to provide that style. @node Android Troubleshooting -@section What to do when something goes wrong on Android +@section Troubleshooting Startup Problems on Android @cindex troubleshooting, android @cindex emacs -Q, android @@ -741,7 +754,7 @@ Android Troubleshooting your initialization or dump files from there instead. @node Android Software -@section Installing extra software on Android +@section Installing Extra Software on Android @cindex installing extra software on Android @cindex installing Unix software on Android diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 8118479319e..a1f70644e16 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -616,10 +616,13 @@ private static class Coordinate state = eventModifiers (event); - /* Ignore meta-state understood by Emacs for now, or Ctrl+C will - not be recognized as an ASCII key press event. */ + /* Ignore meta-state understood by Emacs for now, or key presses + such as Ctrl+C and Meta+C will not be recognized as an ASCII + key press event. */ + state_1 - = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK); + = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK + | KeyEvent.META_SYM_ON | KeyEvent.META_META_MASK); synchronized (eventStrings) { @@ -646,10 +649,13 @@ private static class Coordinate /* Compute the event's modifier mask. */ state = eventModifiers (event); - /* Ignore meta-state understood by Emacs for now, or Ctrl+C will - not be recognized as an ASCII key press event. */ + /* Ignore meta-state understood by Emacs for now, or key presses + such as Ctrl+C and Meta+C will not be recognized as an ASCII + key press event. */ + state_1 - = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK); + = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK + | KeyEvent.META_SYM_ON | KeyEvent.META_META_MASK); EmacsNative.sendKeyRelease (this.handle, event.getEventTime (), diff --git a/src/androidgui.h b/src/androidgui.h index 265ec29b678..14225f7bf80 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -263,6 +263,7 @@ #define PWinGravity (1L << 9) /* program specified window gravity */ ANDROID_CONTROL_MASK = 4096, ANDROID_ALT_MASK = 2, ANDROID_SUPER_MASK = 4, + ANDROID_META_MASK = 65536, }; struct android_key_event diff --git a/src/androidterm.c b/src/androidterm.c index bcb6cd6db45..f74463f88cd 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -365,7 +365,8 @@ android_android_to_emacs_modifiers (struct android_display_info *dpyinfo, return (((state & ANDROID_CONTROL_MASK) ? ctrl_modifier : 0) | ((state & ANDROID_SHIFT_MASK) ? shift_modifier : 0) | ((state & ANDROID_ALT_MASK) ? meta_modifier : 0) - | ((state & ANDROID_SUPER_MASK) ? super_modifier : 0)); + | ((state & ANDROID_SUPER_MASK) ? super_modifier : 0) + | ((state & ANDROID_META_MASK) ? alt_modifier : 0)); } static int @@ -375,7 +376,8 @@ android_emacs_to_android_modifiers (struct android_display_info *dpyinfo, return (((state & ctrl_modifier) ? ANDROID_CONTROL_MASK : 0) | ((state & shift_modifier) ? ANDROID_SHIFT_MASK : 0) | ((state & meta_modifier) ? ANDROID_ALT_MASK : 0) - | ((state & super_modifier) ? ANDROID_SUPER_MASK : 0)); + | ((state & super_modifier) ? ANDROID_SUPER_MASK : 0) + | ((state & alt_modifier) ? ANDROID_META_MASK : 0)); } static void android_frame_rehighlight (struct android_display_info *); commit f7052599877de9e855d743c68c88cc5729456f1f Merge: e41349dd93f 009236502ef Author: Po Lu Date: Wed Aug 2 08:10:59 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit e41349dd93ffec2b1e383cb4c4dfdb59f6e7edac Author: Po Lu Date: Tue Aug 1 21:06:06 2023 +0800 Update Android port * doc/emacs/android.texi (Android File System): Describe how to access real files named /assets or /contents if so required. * java/org/gnu/emacs/EmacsService.java (validAuthority): * src/android.c (android_init_emacs_service): * src/android.h: New function. * src/androidvfs.c (android_saf_valid_authority_p): New function. Wrap the Java function. (android_saf_root_stat, android_saf_root_access): Don't return success if no authority by vp->authority's name exists. (android_saf_tree_from_name): Check validity of string data before giving it to JNI. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 0330e9b5890..4b8f36a65eb 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -219,6 +219,15 @@ Android File System Document Providers}.) @end itemize + Despite ordinary installations of Android not having files within +the (normally read-only) root directory named @file{content} or +@file{assets}, you may want to access real files by these names if the +Android installation in use has been customized. These files will +conflict with the aformentioned special directories, but can +nevertheless be accessed by writing their names relative to the +``parent'' directory of the root directory, as so illustrated: +@file{/../content}, @file{/../assets}. + The external storage directory is found at @file{/sdcard}. The other directories are not found at any fixed location (but see below), although the app data directory is typically symlinked to @@ -268,10 +277,8 @@ Android File System After you disable or enable this setting as appropriate and grant Emacs the ``Files and Media'' permission, it will be able to access -files under @file{/sdcard} as usual. - - These settings are not present on many proprietary versions of -Android. +files under @file{/sdcard} as usual. These settings are not present +on some proprietary versions of Android. @node Android Document Providers @section Accessing files from other programs under Android diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 3c1bb0855f4..8554dadd06e 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1769,4 +1769,29 @@ In addition, arbitrary runtime exceptions (such as ? DocumentsContract.getDocumentId (name) : null); } + + /* Return if there is a content provider by the name of AUTHORITY + supplying at least one tree URI Emacs retains persistent rights + to access. */ + + public boolean + validAuthority (String authority) + { + List permissions; + Uri uri; + + permissions = resolver.getPersistedUriPermissions (); + + for (UriPermission permission : permissions) + { + uri = permission.getUri (); + + if (DocumentsContract.isTreeUri (uri) + && permission.isReadPermission () + && uri.getAuthority ().equals (authority)) + return true; + } + + return false; + } }; diff --git a/src/android.c b/src/android.c index 2b785319549..c30d7b58979 100644 --- a/src/android.c +++ b/src/android.c @@ -1592,6 +1592,8 @@ #define FIND_METHOD(c_name, name, signature) \ "(Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;)Ljava/lang/String;"); + FIND_METHOD (valid_authority, "validAuthority", + "(Ljava/lang/String;)Z"); #undef FIND_METHOD } diff --git a/src/android.h b/src/android.h index 8440fb9bc75..945bd649c18 100644 --- a/src/android.h +++ b/src/android.h @@ -284,6 +284,7 @@ #define android_is_special_directory(name, dir) ((const char *) NULL) jmethodID delete_document; jmethodID rename_document; jmethodID move_document; + jmethodID valid_authority; }; extern JNIEnv *android_java_env; diff --git a/src/androidvfs.c b/src/androidvfs.c index eeef5ea5db0..e3b0b895df3 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -3249,6 +3249,43 @@ android_authority_initial (char *name, size_t length) static struct android_vnode *android_saf_tree_from_name (char *, const char *, const char *); +/* Forward declaration. */ +static int android_verify_jni_string (const char *); + +/* Ascertain and return whether or not AUTHORITY designates a content + provider offering at least one directory tree accessible to + Emacs. */ + +static bool +android_saf_valid_authority_p (const char *authority) +{ + jobject string; + jboolean valid; + jmethodID method; + + /* Make certain AUTHORITY can actually be represented as a Java + string. */ + + if (android_verify_jni_string (authority)) + return false; + + /* Build a string containing AUTHORITY. */ + + string = (*android_java_env)->NewStringUTF (android_java_env, + authority); + android_exception_check (); + + method = service_class.valid_authority; + valid + = (*android_java_env)->CallNonvirtualBooleanMethod (android_java_env, + emacs_service, + service_class.class, + method, string); + android_exception_check_1 (string); + ANDROID_DELETE_LOCAL_REF (string); + return valid; +} + static struct android_vnode * android_saf_root_name (struct android_vnode *vnode, char *name, size_t length) @@ -3311,9 +3348,6 @@ android_saf_root_name (struct android_vnode *vnode, char *name, return android_saf_tree_from_name (component_end, component, vp->authority); - /* Otherwise, find the first component of NAME and create a vnode - representing it as an authority. */ - /* Create the vnode. */ vp = xmalloc (sizeof *vp); vp->vnode.ops = &saf_root_vfs_ops; @@ -3414,6 +3448,22 @@ android_saf_root_rename (struct android_vnode *src, android_saf_root_stat (struct android_vnode *vnode, struct stat *statb) { + struct android_saf_root_vnode *vp; + + /* Verify that the authority actually exists and return ENOENT + otherwise, lest `locate-dominating-file' & co call an operation + that doesn't require listing URIs under this authority, such as + access. */ + + vp = (struct android_saf_root_vnode *) vnode; + + if (vp->authority + && !android_saf_valid_authority_p (vp->authority)) + { + errno = ENOENT; + return -1; + } + /* Make up some imaginary statistics for this vnode. */ memset (statb, 0, sizeof *statb); @@ -3428,6 +3478,8 @@ android_saf_root_stat (struct android_vnode *vnode, static int android_saf_root_access (struct android_vnode *vnode, int mode) { + struct android_saf_root_vnode *vp; + /* Validate MODE. */ if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK))) @@ -3444,6 +3496,20 @@ android_saf_root_access (struct android_vnode *vnode, int mode) return -1; } + /* Verify that the authority actually exists and return ENOENT + otherwise, lest `locate-dominating-file' & co call an operation + that doesn't require listing URIs under this authority, such as + access. */ + + vp = (struct android_saf_root_vnode *) vnode; + + if (vp->authority + && !android_saf_valid_authority_p (vp->authority)) + { + errno = ENOENT; + return -1; + } + return 0; } @@ -5309,9 +5375,9 @@ android_saf_tree_opendir (struct android_vnode *vnode) AUTHORITY is the name of the content provider authority that is offering TREE. - Value is NULL if no document tree or provider by those names - exists, or some other error takes place (for example, if TREE and - AUTHORITY aren't encoded correctly.) */ + Value is NULL and errno is set if no document tree or provider by + those names exists, or some other error takes place (for example, + if TREE and AUTHORITY aren't encoded correctly.) */ static struct android_vnode * android_saf_tree_from_name (char *name, const char *tree, @@ -5323,7 +5389,18 @@ android_saf_tree_from_name (char *name, const char *tree, const char *uri; struct android_vnode *vp; - /* Assume that TREE and NAME are in ``modified UTF-8 format''. */ + /* It's not a given that NAME and TREE are actually in the modified + UTF-8 format used by the JVM to encode strings, and the JVM + aborts when encountering a string that is not. Make sure they + are valid before continuing. */ + + if (android_verify_jni_string (name) + || android_verify_jni_string (authority)) + { + errno = ENOENT; + return NULL; + } + tree_string = (*android_java_env)->NewStringUTF (android_java_env, tree); android_exception_check (); commit b022398b8f0a03f0e1b3ec8df41a439cdbe5bd19 Author: Po Lu Date: Tue Aug 1 14:54:30 2023 +0800 Micro-optimize PUSHW/PUSHB * src/sfnt.c (CHECK_STACK_AVAILABLE): New macro. (PUSH): (PUSH_UNCHECKED): Always define to unchecked versions, even if TEST. (PUSH2_UNCHECKED): New macro. (NPUSHB): (NPUSHW): (PUSHB): (PUSHW): Check the number of remaining stack elements once. (stack_overflow_test_args): Expect zero stack arguments. diff --git a/src/sfnt.c b/src/sfnt.c index 3f1639b3734..876db70bcda 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -5708,6 +5708,17 @@ #define CHECK_STACK_ELEMENTS(n) \ TRAP ("stack underflow"); \ } +#define CHECK_STACK_AVAILABLE(n) \ + { \ + char *stack_end; \ + \ + stack_end \ + = (char *) interpreter->twilight_x; \ + if (((char *) (interpreter->SP + (n)) \ + > stack_end)) \ + TRAP ("stack overflow"); \ + } + #define CHECK_PREP() \ if (!is_prep) \ TRAP ("instruction executed not valid" \ @@ -5756,7 +5767,7 @@ #define LOOK() \ ? (TRAP ("stack underflow"), 0) \ : *(interpreter->SP - 1)) -#ifndef TEST +#if !defined TEST || !0 #define PUSH(value) \ { \ @@ -5774,7 +5785,7 @@ #define PUSH_UNCHECKED(value) \ interpreter->SP++; \ } -#else +#else /* TEST && 0 */ #define PUSH(value) \ { \ @@ -5792,12 +5803,14 @@ #define PUSH(value) \ #define PUSH_UNCHECKED(value) PUSH (value) -#endif +#endif /* TEST && 0 */ -#define PUSH2(high, low) \ +#define PUSH2_UNCHECKED(high, low) \ { \ - PUSH ((int16_t) ((int8_t) high) << 8 \ - | low); \ + int16_t word; \ + \ + word = (((int8_t) high) << 8 | low); \ + PUSH_UNCHECKED (word); \ } \ #define SRP0() \ @@ -6097,6 +6110,7 @@ #define ENDF() \ #define NPUSHB() \ { \ int b, nbytes, IP; \ + unsigned char *ip; \ \ if ((IP = interpreter->IP + 1) \ >= interpreter->num_instructions) \ @@ -6109,8 +6123,10 @@ #define NPUSHB() \ > interpreter->num_instructions) \ TRAP ("args to NPUSHB lie outside IS"); \ \ + CHECK_STACK_AVAILABLE (nbytes); \ + ip = interpreter->instructions; \ for (b = IP + 1; b < IP + 1 + nbytes; ++b) \ - PUSH (interpreter->instructions[b]); \ + PUSH_UNCHECKED (ip[b]); \ \ interpreter->IP += nbytes + 1; \ } @@ -6118,6 +6134,7 @@ #define NPUSHB() \ #define NPUSHW() \ { \ int b, nbytes, IP; \ + unsigned char *ip; \ \ if ((IP = interpreter->IP + 1) \ >= interpreter->num_instructions) \ @@ -6130,10 +6147,11 @@ #define NPUSHW() \ > interpreter->num_instructions) \ TRAP ("args to NPUSHW lie outside IS"); \ \ + CHECK_STACK_AVAILABLE (nbytes / 2); \ + ip = interpreter->instructions; \ for (b = IP + 1; b < IP + 1 + nbytes; \ b += 2) \ - PUSH2 (interpreter->instructions[b], \ - interpreter->instructions[b + 1]); \ + PUSH2_UNCHECKED (ip[b], ip[b + 1]); \ \ interpreter->IP += nbytes + 1; \ } @@ -6634,6 +6652,7 @@ #define GXAXIS() \ #define PUSHB() \ { \ int b, nbytes, IP; \ + unsigned char *ip; \ \ IP = interpreter->IP; \ nbytes = opcode - 0xb0 + 1; \ @@ -6642,8 +6661,10 @@ #define PUSHB() \ > interpreter->num_instructions) \ TRAP ("args to PUSHB lie outside IS"); \ \ + CHECK_STACK_AVAILABLE (nbytes); \ + ip = interpreter->instructions; \ for (b = IP + 1; b < IP + nbytes + 1; ++b) \ - PUSH (interpreter->instructions[b]); \ + PUSH_UNCHECKED (ip[b]); \ \ interpreter->IP += nbytes; \ } @@ -6651,6 +6672,7 @@ #define PUSHB() \ #define PUSHW() \ { \ int b, nbytes, IP; \ + unsigned char *ip; \ \ IP = interpreter->IP; \ nbytes = (opcode - 0xb8 + 1) * 2; \ @@ -6659,10 +6681,11 @@ #define PUSHW() \ > interpreter->num_instructions) \ TRAP ("args to PUSHW lie outside IS"); \ \ + CHECK_STACK_AVAILABLE (nbytes / 2); \ + ip = interpreter->instructions; \ for (b = IP + 1; b < IP + nbytes + 1; \ b += 2) \ - PUSH2 (interpreter->instructions[b], \ - interpreter->instructions[b + 1]); \ + PUSH2_UNCHECKED (ip[b], ip[b + 1]); \ \ interpreter->IP += nbytes; \ } @@ -16080,8 +16103,8 @@ sfnt_check_instctrl (struct sfnt_interpreter *interpreter, static struct sfnt_generic_test_args stack_overflow_test_args = { - (uint32_t[100]) { }, - 100, + (uint32_t[]) { }, + 0, true, 0, }; commit 202ddc0137ac523f4b455ce8cfc7eb01d6384349 Author: Po Lu Date: Tue Aug 1 08:44:24 2023 +0800 ; * src/android.c (ANDROID_THROW): Remove unused macro. diff --git a/src/android.c b/src/android.c index f60ff5acb54..2b785319549 100644 --- a/src/android.c +++ b/src/android.c @@ -68,9 +68,6 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #endif /* __aarch64__ */ -#define ANDROID_THROW(env, class, msg) \ - ((*(env))->ThrowNew ((env), (*(env))->FindClass ((env), class), msg)) - struct android_emacs_pixmap { jclass class; commit deb8b933bce29f99c90f5f305d0d1b75c6d27527 Merge: bd5532e0db5 bfb7c58ac5c Author: Po Lu Date: Tue Aug 1 08:31:14 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit bd5532e0db53ad6c9842213507dfa6b958051f57 Author: Po Lu Date: Mon Jul 31 20:33:27 2023 +0800 Update Android port * src/sfnt.c (ISECT): Micro-optimize this instruction. * src/sfntfont.c (sfnt_parse_style): Avoid GC safety problem. diff --git a/src/sfnt.c b/src/sfnt.c index 10321a09c8b..3f1639b3734 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -7019,11 +7019,13 @@ #define ISECT() \ { \ uint32_t a0, a1, b0, b1, p; \ \ - a0 = POP (); \ - a1 = POP (); \ - b0 = POP (); \ - b1 = POP (); \ - p = POP (); \ + CHECK_STACK_ELEMENTS (5); \ + \ + a0 = POP_UNCHECKED (); \ + a1 = POP_UNCHECKED (); \ + b0 = POP_UNCHECKED (); \ + b1 = POP_UNCHECKED (); \ + p = POP_UNCHECKED (); \ \ sfnt_interpret_isect (interpreter, \ a0, a1, b0, b1, p); \ diff --git a/src/sfntfont.c b/src/sfntfont.c index 72434d5b5d8..6927b185721 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -453,6 +453,7 @@ sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc) { char *style, *single, *saveptr; int i; + USE_SAFE_ALLOCA; /* Fill in default values. slant seems to not be consistent with Fontconfig. */ @@ -462,9 +463,9 @@ sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc) /* Split the style into tokens delimited by spaces. Attempt to find a token specifying each of the weight, slant, or width attributes - using their respective descriptions arrays as a reference. GC - must not happen inside this block. */ - style = SSDATA (Fdowncase (style_name)); + using their respective descriptions arrays as a reference. */ + + SAFE_ALLOCA_STRING (style, Fdowncase (style_name)); saveptr = NULL; while ((single = strtok_r (style, " ", &saveptr))) @@ -541,6 +542,8 @@ sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc) next: continue; } + + SAFE_FREE (); } /* Parse the list of design languages in META, a font metadata table, commit 1e54e565e1f6899d9dbb240d72c30b8af5152232 Author: Po Lu Date: Mon Jul 31 17:59:26 2023 +0800 ; Update Android port * src/sfntfont.c (sfnt_parse_style): Fix misworded commentary. diff --git a/src/sfntfont.c b/src/sfntfont.c index 826c830ece5..72434d5b5d8 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -460,9 +460,10 @@ sfnt_parse_style (Lisp_Object style_name, struct sfnt_font_desc *desc) desc->slant = 100; desc->width = 100; - /* Split the style into spaces. As long as no weight, slant, or - width is encountered, look in the corresponding descriptions - array. GC must not happen inside this block. */ + /* Split the style into tokens delimited by spaces. Attempt to find + a token specifying each of the weight, slant, or width attributes + using their respective descriptions arrays as a reference. GC + must not happen inside this block. */ style = SSDATA (Fdowncase (style_name)); saveptr = NULL; commit 9cf166db63b7383a94dc47a6a5251c0dbe1dae9b Author: Po Lu Date: Mon Jul 31 14:18:12 2023 +0800 Initialize Android API level earlier * java/org/gnu/emacs/EmacsNative.java (EmacsNative): * java/org/gnu/emacs/EmacsNoninteractive.java (main): * java/org/gnu/emacs/EmacsService.java (run): * java/org/gnu/emacs/EmacsThread.java (run): * src/android.c (initEmacs, setEmacsParams): Set `android_api_level' within setEmacsParams, not in initEmacs. * src/androidvfs.c: Pacify compiler warnings. diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index ea200037218..7d72a9f192e 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -66,7 +66,9 @@ public final class EmacsNative classPath must be the classpath of this app_process process, or NULL. - emacsService must be the EmacsService singleton, or NULL. */ + emacsService must be the EmacsService singleton, or NULL. + + apiLevel is the version of Android being run. */ public static native void setEmacsParams (AssetManager assetManager, String filesDir, String libDir, @@ -75,18 +77,16 @@ public static native void setEmacsParams (AssetManager assetManager, float pixelDensityY, float scaledDensity, String classPath, - EmacsService emacsService); + EmacsService emacsService, + int apiLevel); /* Initialize Emacs with the argument array ARGV. Each argument must contain a NULL terminated string, or else the behavior is undefined. DUMPFILE is the dump file to use, or NULL if Emacs is to load - loadup.el itself. - - APILEVEL is the version of Android being used. */ - public static native void initEmacs (String argv[], String dumpFile, - int apiLevel); + loadup.el itself. */ + public static native void initEmacs (String argv[], String dumpFile); /* Abort and generate a native core dump. */ public static native void emacsAbort (); diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java index aa6fa41ba97..1c7513e1cc9 100644 --- a/java/org/gnu/emacs/EmacsNoninteractive.java +++ b/java/org/gnu/emacs/EmacsNoninteractive.java @@ -190,14 +190,14 @@ public final class EmacsNoninteractive EmacsNative.setEmacsParams (assets, filesDir, libDir, cacheDir, 0.0f, - 0.0f, 0.0f, null, null); + 0.0f, 0.0f, null, null, + Build.VERSION.SDK_INT); /* Now find the dump file that Emacs should use, if it has already been dumped. */ EmacsApplication.findDumpFile (context); /* Start Emacs. */ - EmacsNative.initEmacs (args, EmacsApplication.dumpFileName, - Build.VERSION.SDK_INT); + EmacsNative.initEmacs (args, EmacsApplication.dumpFileName); } }; diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index e714f75fdf2..3c1bb0855f4 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -291,7 +291,8 @@ invocation of app_process (through android-emacs) can cacheDir, (float) pixelDensityX, (float) pixelDensityY, (float) scaledDensity, - classPath, EmacsService.this); + classPath, EmacsService.this, + Build.VERSION.SDK_INT); } }, extraStartupArgument, /* If any file needs to be opened, open it now. */ diff --git a/java/org/gnu/emacs/EmacsThread.java b/java/org/gnu/emacs/EmacsThread.java index c003ea95c50..5307015b46f 100644 --- a/java/org/gnu/emacs/EmacsThread.java +++ b/java/org/gnu/emacs/EmacsThread.java @@ -22,7 +22,6 @@ import java.lang.Thread; import java.util.Arrays; -import android.os.Build; import android.util.Log; public final class EmacsThread extends Thread @@ -78,7 +77,6 @@ public final class EmacsThread extends Thread /* Run the native code now. */ Log.d (TAG, "run: " + Arrays.toString (args)); - EmacsNative.initEmacs (args, EmacsApplication.dumpFileName, - Build.VERSION.SDK_INT); + EmacsNative.initEmacs (args, EmacsApplication.dumpFileName); } }; diff --git a/src/android.c b/src/android.c index 8c0232a51f8..f60ff5acb54 100644 --- a/src/android.c +++ b/src/android.c @@ -1281,7 +1281,8 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, jfloat pixel_density_y, jfloat scaled_density, jobject class_path, - jobject emacs_service_object) + jobject emacs_service_object, + jint api_level) { JNI_STACK_ALIGNMENT_PROLOGUE; @@ -1289,6 +1290,10 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, pthread_t thread; const char *java_string; + /* Set the Android API level early, as it is used by + `android_vfs_init'. */ + android_api_level = api_level; + /* This function should only be called from the main thread. */ android_pixel_density_x = pixel_density_x; @@ -1771,7 +1776,7 @@ #define FIND_METHOD(c_name, name, signature) \ JNIEXPORT void JNICALL NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, - jobject dump_file_object, jint api_level) + jobject dump_file_object) { /* android_emacs_init is not main, so GCC is not nice enough to add the stack alignment prologue. @@ -1788,9 +1793,6 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, const char *c_argument; char *dump_file; - /* Set the Android API level. */ - android_api_level = api_level; - android_java_env = env; nelements = (*env)->GetArrayLength (env, argv); diff --git a/src/androidvfs.c b/src/androidvfs.c index 9acc8f2b139..eeef5ea5db0 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -5976,6 +5976,14 @@ android_saf_new_opendir (struct android_vnode *vnode) /* Semaphore posted upon the completion of an SAF operation. */ static sem_t saf_completion_sem; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#else /* GNUC */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#endif /* __clang__ */ + JNIEXPORT jint JNICALL NATIVE_NAME (safSyncAndReadInput) (JNIEnv *env, jobject object) { @@ -6010,6 +6018,12 @@ NATIVE_NAME (safPostRequest) (JNIEnv *env, jobject object) sem_post (&saf_completion_sem); } +#ifdef __clang__ +#pragma clang diagnostic pop +#else /* GNUC */ +#pragma GCC diagnostic pop +#endif /* __clang__ */ + /* Root vnode. This vnode represents the root inode, and is a regular commit 5a8130ab967cb296d028539d10c749ee35f62e6a Author: Po Lu Date: Mon Jul 31 10:50:12 2023 +0800 Implement cross-directory SAF rename operations * java/org/gnu/emacs/EmacsService.java (renameDocument): Don't catch UnsupportedOperationException; handle ENOSYS in android_saf_rename_document instead. (moveDocument): New function. * lisp/subr.el (y-or-n-p): Always change the text conversion style. * src/android.c (android_init_emacs_service) (android_exception_check_4): New function. * src/android.h: Update Java function table. * src/androidvfs.c (android_saf_rename_document): Handle ENOSYS here by setting errno to EXDEV. (android_saf_move_document): New function. (android_document_id_from_name): Take const `dir_name'. (android_saf_tree_rename): Use delete-move-rename to implement cross-directory renames. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 07e585ad37c..e714f75fdf2 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1713,26 +1713,59 @@ In addition, arbitrary runtime exceptions (such as tree = Uri.parse (uri); uriObject = DocumentsContract.buildDocumentUriUsingTree (tree, docId); - try - { - if (DocumentsContract.renameDocument (resolver, uriObject, - name) - != null) - { - /* Invalidate the cache. */ - if (storageThread != null) - storageThread.postInvalidateCacheDir (tree, docId, - name); - return 0; - } - } - catch (UnsupportedOperationException e) + if (DocumentsContract.renameDocument (resolver, uriObject, + name) + != null) { - ;; + /* Invalidate the cache. */ + if (storageThread != null) + storageThread.postInvalidateCacheDir (tree, docId, + name); + return 0; } - /* Handle unsupported operation exceptions specially, so - `android_rename' can return ENXDEV. */ + /* Handle errors specially, so `android_saf_rename_document' can + return ENXDEV. */ return -1; } + + /* Move the document designated by DOCID from the directory under + DIR_NAME designated by SRCID to the directory designated by + DSTID. If the ID of the document being moved changes as a + consequence of the movement, return the new ID, else NULL. + + URI is the document tree containing all three documents. */ + + public String + moveDocument (String uri, String docId, String dirName, + String dstId, String srcId) + throws FileNotFoundException + { + Uri uri1, docId1, dstId1, srcId1; + Uri name; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) + throw new UnsupportedOperationException ("Documents aren't capable" + + " of being moved on Android" + + " versions before 7.0."); + + uri1 = Uri.parse (uri); + docId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, docId); + dstId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, dstId); + srcId1 = DocumentsContract.buildDocumentUriUsingTree (uri1, srcId); + + /* Move the document; this function returns the new ID of the + document should it change. */ + name = DocumentsContract.moveDocument (resolver, docId1, + srcId1, dstId1); + + /* Now invalidate the caches for both DIRNAME and DOCID. */ + + if (storageThread != null) + storageThread.postInvalidateCacheDir (uri1, docId, dirName); + + return (name != null + ? DocumentsContract.getDocumentId (name) + : null); + } }; diff --git a/lisp/subr.el b/lisp/subr.el index 4346f99fa38..36aeeabea47 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3796,11 +3796,10 @@ y-or-n-p ;; Protect this-command when called from pre-command-hook (bug#45029) (this-command this-command) (str (progn - (when (active-minibuffer-window) - ;; If the minibuffer is already active, the - ;; selected window might not change. Disable - ;; text conversion by hand. - (set-text-conversion-style text-conversion-style)) + ;; If the minibuffer is already active, the + ;; selected window might not change. Disable + ;; text conversion by hand. + (set-text-conversion-style text-conversion-style) (read-from-minibuffer prompt nil keymap nil (or y-or-n-p-history-variable t))))) diff --git a/src/android.c b/src/android.c index 0bdbc4e0c4b..8c0232a51f8 100644 --- a/src/android.c +++ b/src/android.c @@ -1586,6 +1586,10 @@ #define FIND_METHOD(c_name, name, signature) \ FIND_METHOD (rename_document, "renameDocument", "(Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;Ljava/lang/String;)I"); + FIND_METHOD (move_document, "moveDocument", + "(Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;)Ljava/lang/String;"); #undef FIND_METHOD } @@ -5667,6 +5671,29 @@ android_exception_check_3 (jobject object, jobject object1, memory_full (0); } +/* Like android_exception_check_3, except it takes more than three + local reference arguments. */ + +void +android_exception_check_4 (jobject object, jobject object1, + jobject object2, jobject object3) +{ + if (likely (!(*android_java_env)->ExceptionCheck (android_java_env))) + return; + + __android_log_print (ANDROID_LOG_WARN, __func__, + "Possible out of memory error. " + " The Java exception follows: "); + /* Describe exactly what went wrong. */ + (*android_java_env)->ExceptionDescribe (android_java_env); + (*android_java_env)->ExceptionClear (android_java_env); + ANDROID_DELETE_LOCAL_REF (object); + ANDROID_DELETE_LOCAL_REF (object1); + ANDROID_DELETE_LOCAL_REF (object2); + ANDROID_DELETE_LOCAL_REF (object3); + memory_full (0); +} + /* Check for JNI problems based on the value of OBJECT. Signal out of memory if OBJECT is NULL. OBJECT1 means the diff --git a/src/android.h b/src/android.h index 591f1a1e43c..8440fb9bc75 100644 --- a/src/android.h +++ b/src/android.h @@ -110,6 +110,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. extern void android_exception_check_1 (jobject); extern void android_exception_check_2 (jobject, jobject); extern void android_exception_check_3 (jobject, jobject, jobject); +extern void android_exception_check_4 (jobject, jobject, jobject, jobject); extern void android_exception_check_nonnull (void *, jobject); extern void android_exception_check_nonnull_1 (void *, jobject, jobject); @@ -282,6 +283,7 @@ #define android_is_special_directory(name, dir) ((const char *) NULL) jmethodID create_directory; jmethodID delete_document; jmethodID rename_document; + jmethodID move_document; }; extern JNIEnv *android_java_env; diff --git a/src/androidvfs.c b/src/androidvfs.c index c529e1fb30f..9acc8f2b139 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -4036,7 +4036,8 @@ android_saf_delete_document (const char *tree, const char *doc_id, } /* Declared further below. */ -static int android_document_id_from_name (const char *, char *, char **); +static int android_document_id_from_name (const char *, const char *, + char **); /* Rename the document designated by DOC_ID inside the directory tree identified by URI, which should be within the directory by the name @@ -4078,8 +4079,17 @@ android_saf_rename_document (const char *uri, const char *doc_id, dir1, name1); /* Check for exceptions. */ + if (android_saf_exception_check (4, uri1, doc_id1, dir1, name1)) - return -1; + { + /* Substitute EXDEV for ENOSYS, so callers fall back on + delete-then-copy. */ + + if (errno == ENOSYS) + errno = EXDEV; + + return -1; + } /* Delete unused local references. */ ANDROID_DELETE_LOCAL_REF (uri1); @@ -4100,6 +4110,109 @@ android_saf_rename_document (const char *uri, const char *doc_id, return 0; } +/* Move the document designated by *DOC_ID from the directory under + DIR_NAME to the directory designated by DST_ID. All three + directories are located within the tree identified by the given + URI. + + If the document's ID changes as a result of the movement, free + *DOC_ID and store the new document ID within. + + Value is 0 upon success, -1 otherwise with errno set. */ + +static int +android_saf_move_document (const char *uri, char **doc_id, + const char *dir_name, const char *dst_id) +{ + char *src_id, *id; + jobject uri1, doc_id1, dir_name1, dst_id1, src_id1; + jstring result; + jmethodID method; + int rc; + const char *new_id; + + /* Obtain the name of the source directory. */ + src_id = NULL; + rc = android_document_id_from_name (uri, dir_name, &src_id); + + if (rc != 1) + { + /* This file is either not a directory or nonexistent. */ + xfree (src_id); + + switch (rc) + { + case 0: + errno = ENOTDIR; + return -1; + + case -1: + case -2: + errno = ENOENT; + return -1; + + default: + emacs_abort (); + } + } + + /* Build Java strings for all five arguments. */ + id = *doc_id; + uri1 = (*android_java_env)->NewStringUTF (android_java_env, uri); + android_exception_check (); + doc_id1 = (*android_java_env)->NewStringUTF (android_java_env, id); + android_exception_check_1 (uri1); + dir_name1 = (*android_java_env)->NewStringUTF (android_java_env, dir_name); + android_exception_check_2 (doc_id1, uri1); + dst_id1 = (*android_java_env)->NewStringUTF (android_java_env, dst_id); + android_exception_check_3 (dir_name1, doc_id1, uri1); + src_id1 = (*android_java_env)->NewStringUTF (android_java_env, src_id); + xfree (src_id); + android_exception_check_4 (dst_id1, dir_name1, doc_id1, uri1); + + /* Do the rename. */ + method = service_class.move_document; + result = (*android_java_env)->CallObjectMethod (android_java_env, + emacs_service, + method, uri1, + doc_id1, dir_name1, + dst_id1, src_id1); + if (android_saf_exception_check (5, src_id1, dst_id1, dir_name1, + doc_id1, uri1)) + { + /* Substitute EXDEV for ENOSYS, so callers fall back on + delete-then-copy. */ + + if (errno == ENOSYS) + errno = EXDEV; + + return -1; + } + + /* Delete unused local references. */ + ANDROID_DELETE_LOCAL_REF (src_id1); + ANDROID_DELETE_LOCAL_REF (dst_id1); + ANDROID_DELETE_LOCAL_REF (dir_name1); + ANDROID_DELETE_LOCAL_REF (doc_id1); + ANDROID_DELETE_LOCAL_REF (uri1); + + if (result) + { + /* The document ID changed. Free id and replace *DOC_ID with + the new ID. */ + xfree (id); + new_id = (*android_java_env)->GetStringUTFChars (android_java_env, + result, NULL); + android_exception_check_nonnull ((void *) new_id, result); + *doc_id = xstrdup (new_id); + (*android_java_env)->ReleaseStringUTFChars (android_java_env, result, + new_id); + ANDROID_DELETE_LOCAL_REF (result); + } + + return 0; +} + /* SAF directory vnode. A file within a SAF directory tree is @@ -4282,7 +4395,7 @@ android_verify_jni_string (const char *name) ID lookup to be canceled. */ static int -android_document_id_from_name (const char *tree_uri, char *name, +android_document_id_from_name (const char *tree_uri, const char *name, char **id) { jobjectArray result; @@ -4658,7 +4771,9 @@ android_saf_tree_rename (struct android_vnode *src, { char *last, *dst_last; struct android_saf_tree_vnode *vp, *vdst; - char path[PATH_MAX], *fill; + char path[PATH_MAX], path1[PATH_MAX]; + char *fill, *dst_id; + int rc; /* If dst isn't a tree, file or new vnode, return EXDEV. */ @@ -4739,8 +4854,118 @@ android_saf_tree_rename (struct android_vnode *src, directory to the other, and possibly then recreated under a new name. */ - errno = EXDEV; /* TODO */ - return -1; + /* The names of the source and destination directories will have + to be copied to path. */ + + if (last - vp->name >= PATH_MAX + || dst_last - vdst->name >= PATH_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + fill = mempcpy (path, vp->name, last - vp->name); + *fill = '\0'; + + /* If vdst doesn't already exist, its document_id field is + already the name of its parent directory. */ + + if (dst->type == ANDROID_VNODE_SAF_NEW) + { + /* First, move the document. This will update + VP->document_id if it changes. */ + + if (android_saf_move_document (vp->tree_uri, + &vp->document_id, + path, + vdst->document_id)) + return -1; + + fill = mempcpy (path, vdst->name, dst_last - vdst->name); + *fill = '\0'; + + /* Next, rename the document, if its display name differs + from that of the source. */ + + if (strcmp (dst_last + 1, last + 1) + /* By now vp->document_id is already in the destination + directory. */ + && android_saf_rename_document (vp->tree_uri, + vp->document_id, + path, + dst_last + 1)) + return -1; + + return 0; + } + + /* Retrieve the ID designating the destination document's parent + directory. */ + + fill = mempcpy (path1, vdst->name, dst_last - vdst->name); + *fill = '\0'; + + rc = android_document_id_from_name (vp->tree_uri, + path1, &dst_id); + + if (rc != 1) + { + /* This file is either not a directory or nonexistent. */ + + switch (rc) + { + case 0: + errno = ENOTDIR; + goto error; + + case -1: + /* dst_id is not set here, as the penultimate component + also couldn't be located. */ + errno = ENOENT; + return -1; + + case -2: + errno = ENOENT; + goto error; + + default: + emacs_abort (); + } + } + + /* vdst already exists, so it needs to be deleted first. */ + + if (android_saf_delete_document (vdst->tree_uri, + vdst->document_id, + vdst->name)) + goto error; + + /* First, move the document. This will update + VP->document_id if it changes. */ + + if (android_saf_move_document (vp->tree_uri, + &vp->document_id, + path, dst_id)) + goto error; + + /* Next, rename the document, if its display name differs from + that of the source. */ + + if (strcmp (dst_last + 1, last + 1) + /* By now vp->document_id is already in the destination + directory. */ + && android_saf_rename_document (vp->tree_uri, + vp->document_id, + path1, + dst_last + 1)) + goto error; + + xfree (dst_id); + return 0; + + error: + xfree (dst_id); + return 1; } /* Otherwise, do this simple rename. The name of the parent commit 2ad50c7ff5093e7a1d3a5a06f042430e7d46c117 Merge: 37f68e86962 1f3995f65a0 Author: Po Lu Date: Mon Jul 31 08:42:03 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 37f68e8696200895832ee1f18b0cd1c0998bb207 Author: Po Lu Date: Sun Jul 30 13:39:27 2023 +0800 Partially implement rename operations on SAF files * java/org/gnu/emacs/EmacsSafThread.java (postInvalidateCacheDir): * java/org/gnu/emacs/EmacsService.java (renameDocument): New functions. * src/android.c (android_init_emacs_service): * src/android.h (struct android_emacs_service): Link to new JNI function. * src/androidvfs.c (android_saf_rename_document): New function. (android_saf_tree_rename): Implement in terms of that function if possible. diff --git a/java/org/gnu/emacs/EmacsSafThread.java b/java/org/gnu/emacs/EmacsSafThread.java index b0d014ffe94..007ea9acfbd 100644 --- a/java/org/gnu/emacs/EmacsSafThread.java +++ b/java/org/gnu/emacs/EmacsSafThread.java @@ -134,7 +134,7 @@ public final class EmacsSafThread extends HandlerThread } - private final class CacheToplevel + private static final class CacheToplevel { /* Map between document names and children. */ HashMap children; @@ -232,7 +232,7 @@ private final class DocIdEntry } }; - private final class CacheEntry + private static final class CacheEntry { /* The type of this document. */ String type; @@ -545,6 +545,80 @@ private final class CacheEntry }); } + /* Invalidate the cache entry denoted by DOCUMENT_ID, within the + document tree URI. + Call this after deleting a document or directory. + + At the same time, remove the child referring to DOCUMENTID from + within CACHENAME's cache entry if it exists. */ + + public void + postInvalidateCacheDir (final Uri uri, final String documentId, + final String cacheName) + { + handler.post (new Runnable () { + @Override + public void + run () + { + CacheToplevel toplevel; + HashMap children; + String[] components; + CacheEntry entry; + DocIdEntry idEntry; + Iterator iter; + + toplevel = getCache (uri); + toplevel.idCache.remove (documentId); + + /* Now remove DOCUMENTID from CACHENAME's cache entry, if + any. */ + + children = toplevel.children; + components = cacheName.split ("/"); + + for (String component : components) + { + /* Java `split' removes trailing empty matches but not + leading or intermediary ones. */ + if (component.isEmpty ()) + continue; + + /* Search for this component within the last level + of the cache. */ + + idEntry = children.get (component); + + if (idEntry == null) + /* Not cached, so return. */ + return; + + entry = toplevel.idCache.get (idEntry.documentId); + + if (entry == null) + /* Not cached, so return. */ + return; + + /* Locate the next component within this + directory. */ + children = entry.children; + } + + iter = children.values ().iterator (); + while (iter.hasNext ()) + { + idEntry = iter.next (); + + if (idEntry.documentId.equals (documentId)) + { + iter.remove (); + break; + } + } + } + }); + } + /* ``Prototypes'' for nested functions that are run within the SAF diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 5186dec974a..07e585ad37c 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1698,4 +1698,41 @@ In addition, arbitrary runtime exceptions (such as return -1; } + + /* Rename the document designated by DOCID inside the directory tree + identified by URI, which should be within the directory + designated by DIR, to NAME. If the file can't be renamed because + it doesn't support renaming, return -1, 0 otherwise. */ + + public int + renameDocument (String uri, String docId, String dir, String name) + throws FileNotFoundException + { + Uri tree, uriObject; + + tree = Uri.parse (uri); + uriObject = DocumentsContract.buildDocumentUriUsingTree (tree, docId); + + try + { + if (DocumentsContract.renameDocument (resolver, uriObject, + name) + != null) + { + /* Invalidate the cache. */ + if (storageThread != null) + storageThread.postInvalidateCacheDir (tree, docId, + name); + return 0; + } + } + catch (UnsupportedOperationException e) + { + ;; + } + + /* Handle unsupported operation exceptions specially, so + `android_rename' can return ENXDEV. */ + return -1; + } }; diff --git a/src/android.c b/src/android.c index a75193e5edd..0bdbc4e0c4b 100644 --- a/src/android.c +++ b/src/android.c @@ -1583,6 +1583,9 @@ #define FIND_METHOD(c_name, name, signature) \ FIND_METHOD (delete_document, "deleteDocument", "(Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;)I"); + FIND_METHOD (rename_document, "renameDocument", + "(Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;Ljava/lang/String;)I"); #undef FIND_METHOD } diff --git a/src/android.h b/src/android.h index fd391fa6435..591f1a1e43c 100644 --- a/src/android.h +++ b/src/android.h @@ -281,6 +281,7 @@ #define android_is_special_directory(name, dir) ((const char *) NULL) jmethodID create_document; jmethodID create_directory; jmethodID delete_document; + jmethodID rename_document; }; extern JNIEnv *android_java_env; diff --git a/src/androidvfs.c b/src/androidvfs.c index c83d3a14b50..c529e1fb30f 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -4035,6 +4035,71 @@ android_saf_delete_document (const char *tree, const char *doc_id, return 0; } +/* Declared further below. */ +static int android_document_id_from_name (const char *, char *, char **); + +/* Rename the document designated by DOC_ID inside the directory tree + identified by URI, which should be within the directory by the name + of DIR, to NAME. If the document can't be renamed, return -1 and + set errno to a value describing the error. Return 0 if the rename + is successful. + + Android permits the same document to appear in multiple + directories, but stores the display name inside the document + ``inode'' itself instead of the directory entries that refer to it. + Because of this, this operation may cause other directory entries + outside DIR to be renamed. */ + +static int +android_saf_rename_document (const char *uri, const char *doc_id, + const char *dir, const char *name) +{ + int rc; + jstring uri1, doc_id1, dir1, name1; + jmethodID method; + + /* Now build the strings for the URI, document ID, directory name + and directory ID. */ + + uri1 = (*android_java_env)->NewStringUTF (android_java_env, uri); + android_exception_check (); + doc_id1 = (*android_java_env)->NewStringUTF (android_java_env, doc_id); + android_exception_check_1 (uri1); + dir1 = (*android_java_env)->NewStringUTF (android_java_env, dir); + android_exception_check_2 (doc_id1, uri1); + name1 = (*android_java_env)->NewStringUTF (android_java_env, name); + android_exception_check_3 (dir1, doc_id1, uri1); + + method = service_class.rename_document; + rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env, + emacs_service, + service_class.class, + method, uri1, doc_id1, + dir1, name1); + + /* Check for exceptions. */ + if (android_saf_exception_check (4, uri1, doc_id1, dir1, name1)) + return -1; + + /* Delete unused local references. */ + ANDROID_DELETE_LOCAL_REF (uri1); + ANDROID_DELETE_LOCAL_REF (doc_id1); + ANDROID_DELETE_LOCAL_REF (dir1); + ANDROID_DELETE_LOCAL_REF (name1); + + /* Then check for errors handled within the Java code. */ + + if (rc == -1) + { + /* UnsupportedOperationException. Trick the caller into falling + back on delete-then-copy code. */ + errno = EXDEV; + return -1; + } + + return 0; +} + /* SAF directory vnode. A file within a SAF directory tree is @@ -4591,9 +4656,117 @@ android_saf_tree_rename (struct android_vnode *src, struct android_vnode *dst, bool keep_existing) { - /* TODO */ - errno = ENOSYS; - return -1; + char *last, *dst_last; + struct android_saf_tree_vnode *vp, *vdst; + char path[PATH_MAX], *fill; + + /* If dst isn't a tree, file or new vnode, return EXDEV. */ + + if (dst->type != ANDROID_VNODE_SAF_TREE + && dst->type != ANDROID_VNODE_SAF_FILE + && dst->type != ANDROID_VNODE_SAF_NEW) + { + errno = EXDEV; + return -1; + } + + vp = (struct android_saf_tree_vnode *) src; + vdst = (struct android_saf_tree_vnode *) dst; + + /* if vp and vdst refer to different tree URIs, return EXDEV. */ + + if (strcmp (vp->tree_uri, vdst->tree_uri)) + { + errno = EXDEV; + return -1; + } + + /* If `keep_existing' and the destination vnode designates an + existing file, return EEXIST. */ + + if (keep_existing && dst->type != ANDROID_VNODE_SAF_NEW) + { + errno = EEXIST; + return -1; + } + + /* Unix `rename' maps to two Android content provider operations. + The first case is a simple rename, where src and dst are both + located within the same directory. Compare the file names of + both up to the component before the last. */ + + last = strrchr (vp->name, '/'); + eassert (last != NULL); + + if (last[1] == '\0') + { + if (last == vp->name) + { + /* This means the caller is trying to rename the root + directory of the tree. */ + errno = EROFS; + return -1; + } + + /* The name is terminated by a trailing directory separator. + Search backwards for the preceding directory separator. */ + last = memrchr (vp->name, '/', last - vp->name); + eassert (last != NULL); + } + + /* Find the end of the second-to-last component in vdst's name. */ + + dst_last = strrchr (vdst->name, '/'); + eassert (dst_last != NULL); + + if (dst_last[1] == '\0') + { + if (dst_last == vdst->name) + { + /* Forbid overwriting the root of the tree either. */ + errno = EROFS; + return -1; + } + + dst_last = memrchr (vdst->name, '/', dst_last - vdst->name); + eassert (dst_last != NULL); + } + + if (dst_last - vdst->name != last - vp->name + || memcmp (vp->name, vdst->name, last - vp->name)) + { + /* The second case is where the file must be moved from one + directory to the other, and possibly then recreated under a + new name. */ + + errno = EXDEV; /* TODO */ + return -1; + } + + /* Otherwise, do this simple rename. The name of the parent + directory is required, as it provides the directory whose entries + will be modified. */ + + if (last - vp->name >= PATH_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + /* If the destination document exists, delete it. */ + + if (dst->type != ANDROID_VNODE_SAF_NEW + && android_saf_delete_document (vdst->tree_uri, + vdst->document_id, + vdst->name)) + return -1; + + fill = mempcpy (path, vp->name, last - vp->name); + *fill = '\0'; + return android_saf_rename_document (vp->tree_uri, + vp->document_id, + path, + dst_last + 1); } static int commit 7ce7a004f68753beafb12830e28c459c4b5b6273 Merge: 9c5e9bb9be6 4038f1b40d3 Author: Po Lu Date: Sun Jul 30 08:15:26 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 9c5e9bb9be64bedb9aac8b12b4f0c717bcaa4da5 Author: Po Lu Date: Sat Jul 29 20:53:21 2023 +0800 Correct directory permissions reported for VFS files * java/org/gnu/emacs/EmacsSafThread.java (statDocument1): * src/androidvfs.c (android_afs_stat, android_content_stat) (android_saf_root_stat): Report search permissions for files. diff --git a/java/org/gnu/emacs/EmacsSafThread.java b/java/org/gnu/emacs/EmacsSafThread.java index cb69df01bfb..b0d014ffe94 100644 --- a/java/org/gnu/emacs/EmacsSafThread.java +++ b/java/org/gnu/emacs/EmacsSafThread.java @@ -97,6 +97,7 @@ public final class EmacsSafThread extends HandlerThread /* File access mode constants. See `man 7 inode'. */ public static final int S_IRUSR = 0000400; public static final int S_IWUSR = 0000200; + public static final int S_IXUSR = 0000100; public static final int S_IFCHR = 0020000; public static final int S_IFDIR = 0040000; public static final int S_IFREG = 0100000; @@ -1010,7 +1011,8 @@ type is either NULL (in which case id should also be NULL) or Uri uriObject; String[] projection; long[] stat; - int index; + int flagsIndex, columnIndex, typeIndex; + int sizeIndex, mtimeIndex, flags; long tem; String tem1; Cursor cursor; @@ -1041,7 +1043,16 @@ type is either NULL (in which case id should also be NULL) or if (cursor == null) return null; - if (!cursor.moveToFirst ()) + /* Obtain the indices for columns wanted from this cursor. */ + flagsIndex = cursor.getColumnIndex (Document.COLUMN_FLAGS); + sizeIndex = cursor.getColumnIndex (Document.COLUMN_SIZE); + typeIndex = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + mtimeIndex = cursor.getColumnIndex (Document.COLUMN_LAST_MODIFIED); + + if (!cursor.moveToFirst () + /* COLUMN_LAST_MODIFIED is allowed to be absent in a + conforming documents provider. */ + || flagsIndex < 0 || sizeIndex < 0 || typeIndex < 0) { cursor.close (); return null; @@ -1052,34 +1063,22 @@ type is either NULL (in which case id should also be NULL) or try { - index = cursor.getColumnIndex (Document.COLUMN_FLAGS); - if (index < 0) - return null; - - tem = cursor.getInt (index); + flags = cursor.getInt (flagsIndex); stat[0] |= S_IRUSR; - if ((tem & Document.FLAG_SUPPORTS_WRITE) != 0) + if ((flags & Document.FLAG_SUPPORTS_WRITE) != 0) stat[0] |= S_IWUSR; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N - && (tem & Document.FLAG_VIRTUAL_DOCUMENT) != 0) + && (flags & Document.FLAG_VIRTUAL_DOCUMENT) != 0) stat[0] |= S_IFCHR; - index = cursor.getColumnIndex (Document.COLUMN_SIZE); - if (index < 0) - return null; - - if (cursor.isNull (index)) + if (cursor.isNull (sizeIndex)) stat[1] = -1; /* The size is unknown. */ else - stat[1] = cursor.getLong (index); + stat[1] = cursor.getLong (sizeIndex); - index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); - if (index < 0) - return null; - - tem1 = cursor.getString (index); + tem1 = cursor.getString (typeIndex); /* Check if this is a directory file. */ if (tem1.equals (Document.MIME_TYPE_DIR) @@ -1087,9 +1086,17 @@ type is either NULL (in which case id should also be NULL) or time, but Android doesn't forbid document providers from returning this information. */ && (stat[0] & S_IFCHR) == 0) - /* Since FLAG_SUPPORTS_WRITE doesn't apply to directories, - just assume they're writable. */ - stat[0] |= S_IFDIR | S_IWUSR; + { + /* Since FLAG_SUPPORTS_WRITE doesn't apply to directories, + just assume they're writable. */ + stat[0] |= S_IFDIR | S_IWUSR | S_IXUSR; + + /* Directory files cannot be modified if + FLAG_DIR_SUPPORTS_CREATE is not set. */ + + if ((flags & Document.FLAG_DIR_SUPPORTS_CREATE) == 0) + stat[0] &= ~S_IWUSR; + } /* If this file is neither a character special nor a directory, indicate that it's a regular file. */ @@ -1097,12 +1104,10 @@ type is either NULL (in which case id should also be NULL) or if ((stat[0] & (S_IFDIR | S_IFCHR)) == 0) stat[0] |= S_IFREG; - index = cursor.getColumnIndex (Document.COLUMN_LAST_MODIFIED); - - if (index >= 0 && !cursor.isNull (index)) + if (mtimeIndex >= 0 && !cursor.isNull (mtimeIndex)) { /* Content providers are allowed to not provide mtime. */ - tem = cursor.getLong (index); + tem = cursor.getLong (mtimeIndex); stat[2] = tem; } } diff --git a/src/androidvfs.c b/src/androidvfs.c index bd9112ceb67..c83d3a14b50 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -2010,6 +2010,9 @@ android_afs_stat (struct android_vnode *vnode, struct stat *statb) /* Fill in the stat buffer. */ statb->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; + /* Grant search permissions as well. */ + statb->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH; + /* Concoct a nonexistent device and an inode number. */ statb->st_dev = -1; statb->st_ino = 0; @@ -2534,7 +2537,7 @@ android_content_stat (struct android_vnode *vnode, statb->st_gid = getgid (); statb->st_ino = 0; statb->st_dev = -2; - statb->st_mode = S_IFDIR | S_IRUSR; + statb->st_mode = S_IFDIR | S_IRUSR | S_IXUSR; return 0; } @@ -3418,7 +3421,7 @@ android_saf_root_stat (struct android_vnode *vnode, statb->st_gid = getgid (); statb->st_ino = 0; statb->st_dev = -4; - statb->st_mode = S_IFDIR | S_IRUSR; + statb->st_mode = S_IFDIR | S_IRUSR | S_IXUSR; return 0; } commit 11b2399212f86a6c5f481063688837d91e876b41 Author: Po Lu Date: Sat Jul 29 18:00:09 2023 +0800 ; Update Android port * src/androidvfs.c: Improve commentary. diff --git a/src/androidvfs.c b/src/androidvfs.c index 6c34aac9e3e..bd9112ceb67 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -47,43 +47,57 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include /* This file implements support for the various special-purpose - directories found on Android systems. Such directories are not - mounted in the Unix virtual file-system, instead being accessible - through special API calls; Emacs pretends they are mounted at - specific folders within the root directory. + directories found on Android systems through a series of functions + that substitute for Unix system call wrappers. Such directories + are not mounted in the Unix virtual file-system, but instead + require the use of special system APIs to access; Emacs pretends + they are mounted at specific folders within the root directory. There are presently two directories: /assets, granting access to asset files stored within the APK, and /content, providing direct access to content URIs (in Android 4.4 and later) and content directory trees (in Android 5.0 and later.) - This file implements substitutes for the C library `open', `fstat', - `close', `fclose', `unlink', `symlink', `rmdir', `rename', `stat' - system call wrappers, which process file names through ``VFS - nodes'' representing conceptual files, that are really no more than - tables of function pointers. - - The primary function of a node is to `name' children. This takes a - relative file name and returns a second VFS node tied to a child - that exists within this node (or a child thereof, ad infinite.) - - Other functions are also defined: functions to open file - descriptors, and substitutes for each of the C library system call - wrappers replaced. Each of these functions accepts two vnodes, and - is expected to otherwise behave like the C library system calls - replaced. - - When the virtual file system needs to locate the vnode associated - with a file name, it starts searching at the root vnode. Its - `name' function then creates vnodes as appropriate for the - components of the file name, which repeats recursively until the - vnode designating the file name is found. + Substitutes for the C library `open', `fstat', `close', `fclose', + `unlink', `symlink', `rmdir', `rename', `stat' system call wrappers + are implemented, which delegate their actions to function tables + contained inside ``VFS nodes''. + + The functions of a VFS node are to provide the implementations of + the Unix file system operations that can be carried out on the file + designated by its name and to connect useful information (such as + internal file handles or identifiers) with those file names. To + those ends, there exist several different types of vnodes, each + with a different set of functions and supplementary attributes. + + The key to locating the correct vnode for any given file name is an + additional file system operation, defined by each node, which + ``names'' children. This operation takes a relative file name and + returns a second node designating a constituent sub-file. + + When a file system function is called, it invokes the `name' + operation of a special root vnode conceptually located at the top + of the Unix file system hierarchy, handing it the complete file + name given to it. This vnode's name operation examines the first + component of the relative file name it receives and creates either + an asset, content, or Unix vnode, and calls the new vnode's `name' + operation with the remainder of the file name. + + The vnode(s) created by each `name' operation may in turn create + different vnodes based on the components of the names they have + been provided that are used to repeat this process until no + components remain. The vnode created for the last component of the + file name will provide its file system operations or be passed as + an argument to other file system operations to which the file has + been passed as an argument. The substitute functions defined have two caveats, which however don't prove problematic in an Emacs context: the first is that the treatment of `..' is inconsistent with Unix, and has not really been tested, while the second is that errno values do not always - conform to what the corresponding Unix system calls may return. */ + conform to what the corresponding Unix system calls may return. + These caveats are described in more detail inside the last few + pages of this file. */ /* Structure describing an array of VFS operations. */ commit 4bf8b0a2e9db842283e9e3849e8d23573ba3b181 Author: Po Lu Date: Sat Jul 29 15:57:44 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsSafThread.java (postInvalidateCache): New argument cacheName. Remove that file from the cache. (accessDocument1): Consult the storage cache as well. * java/org/gnu/emacs/EmacsService.java (deleteDocument): New argument NAME. * src/android.c (android_init_emacs_service): Add new argument. * src/androidvfs.c (android_saf_delete_document) (android_saf_tree_rmdir, android_saf_file_unlink): Pass name of file being deleted to `deleteDocument'. diff --git a/java/org/gnu/emacs/EmacsSafThread.java b/java/org/gnu/emacs/EmacsSafThread.java index cf067adc87b..cb69df01bfb 100644 --- a/java/org/gnu/emacs/EmacsSafThread.java +++ b/java/org/gnu/emacs/EmacsSafThread.java @@ -478,14 +478,12 @@ private final class CacheEntry document tree URI. Call this after deleting a document or directory. - Caveat emptor: this does not remove the component name cache - entries linked to the name(s) of the directory being removed, the - assumption being that the next time `documentIdFromName1' is - called, it will notice that the document is missing and remove - the outdated cache entry. */ + At the same time, remove the final component within the file name + CACHENAME from the cache if it exists. */ public void - postInvalidateCache (final Uri uri, final String documentId) + postInvalidateCache (final Uri uri, final String documentId, + final String cacheName) { handler.post (new Runnable () { @Override @@ -493,9 +491,55 @@ entries linked to the name(s) of the directory being removed, the run () { CacheToplevel toplevel; + HashMap children; + String[] components; + CacheEntry entry; + DocIdEntry idEntry; toplevel = getCache (uri); toplevel.idCache.remove (documentId); + + /* If the parent of CACHENAME is cached, remove it. */ + + children = toplevel.children; + components = cacheName.split ("/"); + + for (String component : components) + { + /* Java `split' removes trailing empty matches but not + leading or intermediary ones. */ + if (component.isEmpty ()) + continue; + + if (component == components[components.length - 1]) + { + /* This is the last component, so remove it from + children. */ + children.remove (component); + return; + } + else + { + /* Search for this component within the last level + of the cache. */ + + idEntry = children.get (component); + + if (idEntry == null) + /* Not cached, so return. */ + return; + + entry = toplevel.idCache.get (idEntry.documentId); + + if (entry == null) + /* Not cached, so return. */ + return; + + /* Locate the next component within this + directory. */ + children = entry.children; + } + } } }); } @@ -1109,12 +1153,27 @@ type is either NULL (in which case id should also be NULL) or int tem, index; String tem1; Cursor cursor; + CacheToplevel toplevel; + CacheEntry entry; uriObject = Uri.parse (uri); if (documentId == null) documentId = DocumentsContract.getTreeDocumentId (uriObject); + /* If WRITABLE is false and the document ID is cached, use its + cached value instead. This speeds up + `directory-files-with-attributes' a little. */ + + if (!writable) + { + toplevel = getCache (uriObject); + entry = toplevel.idCache.get (documentId); + + if (entry != null) + return 0; + } + /* Create a document URI representing DOCUMENTID within URI's authority. */ diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index e2abd6c96ef..5186dec974a 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1674,10 +1674,13 @@ In addition, arbitrary runtime exceptions (such as /* Delete the document identified by ID from the document tree identified by URI. Return 0 upon success and -1 upon - failure. */ + failure. + + NAME should be the name of the document being deleted, and is + used to invalidate the cache. */ public int - deleteDocument (String uri, String id) + deleteDocument (String uri, String id, String name) throws FileNotFoundException { Uri uriObject, tree; @@ -1688,7 +1691,7 @@ In addition, arbitrary runtime exceptions (such as if (DocumentsContract.deleteDocument (resolver, uriObject)) { if (storageThread != null) - storageThread.postInvalidateCache (tree, id); + storageThread.postInvalidateCache (tree, id, name); return 0; } diff --git a/src/android.c b/src/android.c index 687c0b48a2a..a75193e5edd 100644 --- a/src/android.c +++ b/src/android.c @@ -1581,7 +1581,8 @@ #define FIND_METHOD(c_name, name, signature) \ "(Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;)Ljava/lang/String;"); FIND_METHOD (delete_document, "deleteDocument", - "(Ljava/lang/String;Ljava/lang/String;)I"); + "(Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;)I"); #undef FIND_METHOD } diff --git a/src/androidvfs.c b/src/androidvfs.c index b175f7746f3..6c34aac9e3e 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -3969,34 +3969,45 @@ android_saf_access (const char *uri_name, const char *id_name, /* Delete the document designated by DOC_ID within the tree identified through the URI TREE. Return 0 if the document has been deleted, - set errno and return -1 upon failure. */ + set errno and return -1 upon failure. + + DOC_NAME should be the name of the file itself, as a file name + whose constituent components lead to a document named DOC_ID. It + isn't used to search for a document ID, but is used to invalidate + the file cache. */ static int -android_saf_delete_document (const char *tree, const char *doc_id) +android_saf_delete_document (const char *tree, const char *doc_id, + const char *doc_name) { - jobject id, uri; + jobject id, uri, name; jmethodID method; jint rc; - /* Build the strings holding the ID and URI. */ + /* Build the strings holding the ID, URI and NAME. */ id = (*android_java_env)->NewStringUTF (android_java_env, doc_id); android_exception_check (); uri = (*android_java_env)->NewStringUTF (android_java_env, tree); android_exception_check_1 (id); + name = (*android_java_env)->NewStringUTF (android_java_env, + doc_name); + android_exception_check_2 (id, name); /* Now, try to delete the document. */ method = service_class.delete_document; rc = (*android_java_env)->CallIntMethod (android_java_env, emacs_service, - method, uri, id); + method, uri, id, + name); - if (android_saf_exception_check (2, id, uri)) + if (android_saf_exception_check (3, id, uri, name)) return -1; ANDROID_DELETE_LOCAL_REF (id); ANDROID_DELETE_LOCAL_REF (uri); + ANDROID_DELETE_LOCAL_REF (name); if (rc) { @@ -4553,7 +4564,9 @@ android_saf_tree_rmdir (struct android_vnode *vnode) return -1; } - return android_saf_delete_document (vp->tree_uri, vp->document_id); + return android_saf_delete_document (vp->tree_uri, + vp->document_id, + vp->name); } static int @@ -5173,7 +5186,9 @@ android_saf_file_unlink (struct android_vnode *vnode) struct android_saf_file_vnode *vp; vp = (struct android_saf_file_vnode *) vnode; - return android_saf_delete_document (vp->tree_uri, vp->document_id); + return android_saf_delete_document (vp->tree_uri, + vp->document_id, + vp->name); } static int commit 431fdda2ebbb6e93ea4eb705ec16a44b49c30c8d Author: Po Lu Date: Sat Jul 29 12:50:06 2023 +0800 ; Update Android port * src/androidvfs.c (android_saf_exception_check): Describe exceptions earlier. diff --git a/src/androidvfs.c b/src/androidvfs.c index babc7101d88..b175f7746f3 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -3739,15 +3739,17 @@ android_saf_exception_check (int n, ...) /* No exception has taken place. Return 0. */ return 0; + /* Print the exception. */ + (*env)->ExceptionDescribe (env); + exception = (*env)->ExceptionOccurred (env); if (!exception) /* JNI couldn't return a local reference to the exception. */ memory_full (0); - /* Print and clear the exception, making it safe to subsequently - call other JNI functions. */ - (*env)->ExceptionDescribe (env); + /* Clear the exception, making it safe to subsequently call other + JNI functions. */ (*env)->ExceptionClear (env); /* Delete each of the N arguments. */ commit 27fe17f0fc56ac11969dbbe54485cff8a4fdab32 Author: Po Lu Date: Sat Jul 29 12:49:36 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsSafThread.java (DocIdEntry): (getCacheEntry): (CacheEntry): (documentIdFromName1): Fix earlier change. diff --git a/java/org/gnu/emacs/EmacsSafThread.java b/java/org/gnu/emacs/EmacsSafThread.java index 9c3e3deb408..cf067adc87b 100644 --- a/java/org/gnu/emacs/EmacsSafThread.java +++ b/java/org/gnu/emacs/EmacsSafThread.java @@ -33,7 +33,9 @@ import android.os.CancellationSignal; import android.os.Handler; import android.os.HandlerThread; +import android.os.OperationCanceledException; import android.os.ParcelFileDescriptor; +import android.os.SystemClock; import android.util.Log; @@ -151,7 +153,7 @@ private final class DocIdEntry public DocIdEntry () { - time = System.uptimeMillis (); + time = SystemClock.uptimeMillis (); } /* Return a cache entry comprised of the state of the file @@ -206,6 +208,10 @@ private final class DocIdEntry toplevel.idCache.put (documentId, entry); return entry; } + catch (OperationCanceledException e) + { + throw e; + } catch (Throwable e) { return null; @@ -220,8 +226,8 @@ private final class DocIdEntry public boolean isValid () { - return ((System.uptimeMillis () - time) - < CACHE_INVALID_TIME); + return ((SystemClock.uptimeMillis () - time) + < CACHE_INVALID_TIME * 1000); } }; @@ -240,14 +246,14 @@ private final class CacheEntry CacheEntry () { children = new HashMap (); - time = System.uptimeMillis (); + time = SystemClock.uptimeMillis (); } public boolean isValid () { - return ((System.uptimeMillis () - time) - < CACHE_INVALID_TIME); + return ((SystemClock.uptimeMillis () - time) + < CACHE_INVALID_TIME * 1000); } }; @@ -740,7 +746,12 @@ public abstract Object runObject (CancellationSignal signal) obtained. Treat this as if the file does not exist. */ - children.remove (idEntry); + children.remove (component); + + if (id == null) + id = DocumentsContract.getTreeDocumentId (uri); + + id_return[0] = id; if ((type == null || type.equals (Document.MIME_TYPE_DIR)) commit 183c65e0c4471c995b1ed6d50b8ecdc8507da5d7 Merge: d3e1e188739 deb74de4f2e Author: Po Lu Date: Sat Jul 29 11:29:42 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit d3e1e188739e54079618405bcc5eb7c6914fecdf Author: Po Lu Date: Sat Jul 29 11:29:25 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsSafThread.java (DocIdEntry) (getCacheEntry, CacheEntry): Use `uptimeMillis' as the basis for cache expiration. diff --git a/java/org/gnu/emacs/EmacsSafThread.java b/java/org/gnu/emacs/EmacsSafThread.java index b3d6ab49f6d..9c3e3deb408 100644 --- a/java/org/gnu/emacs/EmacsSafThread.java +++ b/java/org/gnu/emacs/EmacsSafThread.java @@ -151,7 +151,7 @@ private final class DocIdEntry public DocIdEntry () { - time = System.currentTimeMillis (); + time = System.uptimeMillis (); } /* Return a cache entry comprised of the state of the file @@ -208,10 +208,7 @@ private final class DocIdEntry } catch (Throwable e) { - if (e instanceof FileNotFoundException) - return null; - - throw e; + return null; } finally { @@ -223,7 +220,7 @@ private final class DocIdEntry public boolean isValid () { - return ((System.currentTimeMillis () - time) + return ((System.uptimeMillis () - time) < CACHE_INVALID_TIME); } }; @@ -243,13 +240,13 @@ private final class CacheEntry CacheEntry () { children = new HashMap (); - time = System.currentTimeMillis (); + time = System.uptimeMillis (); } public boolean isValid () { - return ((System.currentTimeMillis () - time) + return ((System.uptimeMillis () - time) < CACHE_INVALID_TIME); } }; commit 47f97b5ae49af49ef3adcad4d4d9cab3668002ff Author: Po Lu Date: Sat Jul 29 11:24:07 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsSafThread.java (EmacsSafThread, getCache) (pruneCache1, pruneCache, cacheChild, cacheDirectoryFromCursor) (documentIdFromName1, openDocumentDirectory1): Implement the cache referred to by the commentary. * java/org/gnu/emacs/EmacsService.java (deleteDocument): Invalidate the cache upon document removal. * src/androidvfs.c (android_saf_exception_check) (android_document_id_from_name): Correctly preserve or set errno in error cases. diff --git a/java/org/gnu/emacs/EmacsSafThread.java b/java/org/gnu/emacs/EmacsSafThread.java index cbd4ff0d25b..b3d6ab49f6d 100644 --- a/java/org/gnu/emacs/EmacsSafThread.java +++ b/java/org/gnu/emacs/EmacsSafThread.java @@ -1,24 +1,30 @@ /* Communication module for Android terminals. -*- c-file-style: "GNU" -*- - Copyright (C) 2023 Free Software Foundation, Inc. +Copyright (C) 2023 Free Software Foundation, Inc. - This file is part of GNU Emacs. +This file is part of GNU Emacs. - GNU Emacs 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 3 of the License, or (at - your option) any later version. +GNU Emacs 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 3 of the License, or (at +your option) any later version. - GNU Emacs 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. +GNU Emacs 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 GNU Emacs. If not, see . */ +You should have received a copy of the GNU General Public License +along with GNU Emacs. If not, see . */ package org.gnu.emacs; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; + +import java.io.FileNotFoundException; + import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; @@ -29,6 +35,8 @@ import android.os.HandlerThread; import android.os.ParcelFileDescriptor; +import android.util.Log; + import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; @@ -38,7 +46,6 @@ its own handler. These operations include opening files and maintaining the path to document ID cache. -#if 0 Because Emacs paths are based on file display names, while Android document identifiers have no discernible hierarchy of their own, each file name lookup must carry out a repeated search for @@ -53,13 +60,10 @@ periodically remove entries that are older than a predetermined amount of a time. - The cache is structured much like the directory trees whose - information it records, with each entry in the cache containing a - list of entries for their children. File name lookup consults the - cache and populates it with missing information simultaneously. - - This is not yet implemented. -#endif + The cache is split into two levels: the first caches the + relationships between display names and document IDs, while the + second caches individual document IDs and their contents (children, + type, etc.) Long-running operations are also run on this thread for another reason: Android uses special cancellation objects to terminate @@ -76,9 +80,15 @@ public final class EmacsSafThread extends HandlerThread { + private static final String TAG = "EmacsSafThread"; + /* The content resolver used by this thread. */ private final ContentResolver resolver; + /* Map between tree URIs and the cache entry representing its + toplevel directory. */ + private final HashMap cacheToplevels; + /* Handler for this thread's main loop. */ private Handler handler; @@ -89,11 +99,20 @@ public final class EmacsSafThread extends HandlerThread public static final int S_IFDIR = 0040000; public static final int S_IFREG = 0100000; + /* Number of seconds in between each attempt to prune the storage + cache. */ + public static final int CACHE_PRUNE_TIME = 10; + + /* Number of seconds after which an entry in the cache is to be + considered invalid. */ + public static final int CACHE_INVALID_TIME = 10; + public EmacsSafThread (ContentResolver resolver) { super ("Document provider access thread"); this.resolver = resolver; + this.cacheToplevels = new HashMap (); } @@ -106,6 +125,376 @@ public final class EmacsSafThread extends HandlerThread /* Set up the handler after the thread starts. */ handler = new Handler (getLooper ()); + + /* And start periodically pruning the cache. */ + postPruneMessage (); + } + + + private final class CacheToplevel + { + /* Map between document names and children. */ + HashMap children; + + /* Map between document IDs and cache items. */ + HashMap idCache; + }; + + private final class DocIdEntry + { + /* The document ID. */ + String documentId; + + /* The time this entry was created. */ + long time; + + public + DocIdEntry () + { + time = System.currentTimeMillis (); + } + + /* Return a cache entry comprised of the state of the file + identified by `documentId'. TREE is the URI of the tree + containing this entry, and TOPLEVEL is the toplevel + representing it. SIGNAL is a cancellation signal. + + Value is NULL if the file cannot be found. */ + + public CacheEntry + getCacheEntry (Uri tree, CacheToplevel toplevel, + CancellationSignal signal) + { + Uri uri; + String[] projection; + String type; + Cursor cursor; + int column; + CacheEntry entry; + + /* Create a document URI representing DOCUMENTID within URI's + authority. */ + + uri = DocumentsContract.buildDocumentUriUsingTree (tree, + documentId); + projection = new String[] { + Document.COLUMN_MIME_TYPE, + }; + + cursor = null; + + try + { + cursor = resolver.query (uri, projection, null, + null, null, signal); + + if (!cursor.moveToFirst ()) + return null; + + column = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + + if (column < 0) + return null; + + type = cursor.getString (column); + + if (type == null) + return null; + + entry = new CacheEntry (); + entry.type = type; + toplevel.idCache.put (documentId, entry); + return entry; + } + catch (Throwable e) + { + if (e instanceof FileNotFoundException) + return null; + + throw e; + } + finally + { + if (cursor != null) + cursor.close (); + } + } + + public boolean + isValid () + { + return ((System.currentTimeMillis () - time) + < CACHE_INVALID_TIME); + } + }; + + private final class CacheEntry + { + /* The type of this document. */ + String type; + + /* Map between document names and children. */ + HashMap children; + + /* The time this entry was created. */ + long time; + + public + CacheEntry () + { + children = new HashMap (); + time = System.currentTimeMillis (); + } + + public boolean + isValid () + { + return ((System.currentTimeMillis () - time) + < CACHE_INVALID_TIME); + } + }; + + /* Create or return a toplevel for the given tree URI. */ + + private CacheToplevel + getCache (Uri uri) + { + CacheToplevel toplevel; + + toplevel = cacheToplevels.get (uri); + + if (toplevel != null) + return toplevel; + + toplevel = new CacheToplevel (); + toplevel.children = new HashMap (); + toplevel.idCache = new HashMap (); + cacheToplevels.put (uri, toplevel); + return toplevel; + } + + /* Remove each cache entry within COLLECTION older than + CACHE_INVALID_TIME. */ + + private void + pruneCache1 (Collection collection) + { + Iterator iter; + DocIdEntry tem; + + iter = collection.iterator (); + while (iter.hasNext ()) + { + /* Get the cache entry. */ + tem = iter.next (); + + /* If it's not valid anymore, remove it. Iterating over a + collection whose contents are being removed is undefined + unless the removal is performed using the iterator's own + `remove' function, so tem.remove cannot be used here. */ + + if (tem.isValid ()) + continue; + + iter.remove (); + } + } + + /* Remove every entry older than CACHE_INVALID_TIME from each + toplevel inside `cachedToplevels'. */ + + private void + pruneCache () + { + Iterator iter; + CacheEntry tem; + + for (CacheToplevel toplevel : cacheToplevels.values ()) + { + /* First, clean up expired cache entries. */ + iter = toplevel.idCache.values ().iterator (); + + while (iter.hasNext ()) + { + /* Get the cache entry. */ + tem = iter.next (); + + /* If it's not valid anymore, remove it. Iterating over a + collection whose contents are being removed is + undefined unless the removal is performed using the + iterator's own `remove' function, so tem.remove cannot + be used here. */ + + if (tem.isValid ()) + { + /* Otherwise, clean up expired items in its document + ID cache. */ + pruneCache1 (tem.children.values ()); + continue; + } + + iter.remove (); + } + } + + postPruneMessage (); + } + + /* Cache file information within TOPLEVEL, under the list of + children CHILDREN. + + NAME, ID, and TYPE should respectively be the display name of the + document within its parent document (the CacheEntry whose + `children' field is CHILDREN), its document ID, and its MIME + type. + + If ID_ENTRY_EXISTS, don't create a new document ID entry within + CHILDREN indexed by NAME. + + Value is the cache entry saved for the document ID. */ + + private CacheEntry + cacheChild (CacheToplevel toplevel, + HashMap children, + String name, String id, String type, + boolean id_entry_exists) + { + DocIdEntry idEntry; + CacheEntry cacheEntry; + + if (!id_entry_exists) + { + idEntry = new DocIdEntry (); + idEntry.documentId = id; + children.put (name, idEntry); + } + + cacheEntry = new CacheEntry (); + cacheEntry.type = type; + toplevel.idCache.put (id, cacheEntry); + return cacheEntry; + } + + /* Cache the type and as many of the children of the directory + designated by DOC_ID as possible into TOPLEVEL. + + CURSOR should be a cursor representing an open directory stream, + with its projection consisting of at least the display name, + document ID and MIME type columns. + + Rewind the position of CURSOR to before its first element after + completion. */ + + private void + cacheDirectoryFromCursor (CacheToplevel toplevel, String documentId, + Cursor cursor) + { + CacheEntry entry, constitutent; + int nameColumn, idColumn, typeColumn; + String id, name, type; + DocIdEntry idEntry; + + /* Find the numbers of the columns wanted. */ + + nameColumn + = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME); + idColumn + = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID); + typeColumn + = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + + if (nameColumn < 0 || idColumn < 0 || typeColumn < 0) + return; + + entry = new CacheEntry (); + + /* We know this is a directory already. */ + entry.type = Document.MIME_TYPE_DIR; + toplevel.idCache.put (documentId, entry); + + /* Now, try to cache each of its constituents. */ + + while (cursor.moveToNext ()) + { + try + { + name = cursor.getString (nameColumn); + id = cursor.getString (idColumn); + type = cursor.getString (typeColumn); + + if (name == null || id == null || type == null) + continue; + + /* First, add the name and ID to ENTRY's map of + children. */ + idEntry = new DocIdEntry (); + idEntry.documentId = id; + entry.children.put (id, idEntry); + + /* If this constituent is a directory, don't cache any + information about it. It cannot be cached without + knowing its children. */ + + if (type.equals (Document.MIME_TYPE_DIR)) + continue; + + /* Otherwise, create a new cache entry comprised of its + type. */ + constitutent = new CacheEntry (); + constitutent.type = type; + toplevel.idCache.put (documentId, entry); + } + catch (Exception e) + { + e.printStackTrace (); + continue; + } + } + + /* Rewind cursor back to the beginning. */ + cursor.moveToPosition (-1); + } + + /* Post a message to run `pruneCache' every CACHE_PRUNE_TIME + seconds. */ + + private void + postPruneMessage () + { + handler.postDelayed (new Runnable () { + @Override + public void + run () + { + pruneCache (); + } + }, CACHE_PRUNE_TIME * 1000); + } + + /* Invalidate the cache entry denoted by DOCUMENT_ID, within the + document tree URI. + Call this after deleting a document or directory. + + Caveat emptor: this does not remove the component name cache + entries linked to the name(s) of the directory being removed, the + assumption being that the next time `documentIdFromName1' is + called, it will notice that the document is missing and remove + the outdated cache entry. */ + + public void + postInvalidateCache (final Uri uri, final String documentId) + { + handler.post (new Runnable () { + @Override + public void + run () + { + CacheToplevel toplevel; + + toplevel = getCache (uri); + toplevel.idCache.remove (documentId); + } + }); } @@ -289,10 +678,14 @@ public abstract Object runObject (CancellationSignal signal) String[] id_return, CancellationSignal signal) { Uri uri, treeUri; - String id, type; + String id, type, newId, newType; String[] components, projection; Cursor cursor; - int column; + int nameColumn, idColumn, typeColumn; + CacheToplevel toplevel; + DocIdEntry idEntry; + HashMap children, next; + CacheEntry cache; projection = new String[] { Document.COLUMN_DISPLAY_NAME, @@ -310,6 +703,12 @@ public abstract Object runObject (CancellationSignal signal) type = id = null; cursor = null; + /* Obtain the top level of this cache. */ + toplevel = getCache (uri); + + /* Set the current map of children to this top level. */ + children = toplevel.children; + /* For each component... */ try @@ -321,6 +720,48 @@ public abstract Object runObject (CancellationSignal signal) if (component.isEmpty ()) continue; + /* Search for component within the currently cached list + of children. */ + + idEntry = children.get (component); + + if (idEntry != null) + { + /* The document ID is known. Now find the + corresponding document ID cache. */ + + cache = toplevel.idCache.get (idEntry.documentId); + + /* Fetch just the information for this document. */ + + if (cache == null) + cache = idEntry.getCacheEntry (uri, toplevel, signal); + + if (cache == null) + { + /* File status matching idEntry could not be + obtained. Treat this as if the file does not + exist. */ + + children.remove (idEntry); + + if ((type == null + || type.equals (Document.MIME_TYPE_DIR)) + /* ... and type and id currently represent the + penultimate component. */ + && component == components[components.length - 1]) + return -2; + + return -1; + } + + /* Otherwise, use the cached information. */ + id = idEntry.documentId; + type = cache.type; + children = cache.children; + continue; + } + /* Create the tree URI for URI from ID if it exists, or the root otherwise. */ @@ -342,6 +783,21 @@ public abstract Object runObject (CancellationSignal signal) if (cursor == null) return -1; + /* Find the column numbers for each of the columns that + are wanted. */ + + nameColumn + = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME); + idColumn + = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID); + typeColumn + = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + + if (nameColumn < 0 || idColumn < 0 || typeColumn < 0) + return -1; + + next = null; + while (true) { /* Even though the query selects for a specific @@ -350,6 +806,12 @@ public abstract Object runObject (CancellationSignal signal) if (!cursor.moveToNext ()) { + /* If a component has been found, break out of the + loop. */ + + if (next != null) + break; + /* If the last component considered is a directory... */ if ((type == null @@ -382,52 +844,38 @@ public abstract Object runObject (CancellationSignal signal) /* So move CURSOR to a row with the right display name. */ - column = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME); - - if (column < 0) - continue; - - name = cursor.getString (column); - - /* Break out of the loop only once a matching - component is found. */ - - if (name.equals (component)) - break; - } - - /* Look for a column by the name of - COLUMN_DOCUMENT_ID. */ - - column = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID); - - if (column < 0) - return -1; + name = cursor.getString (nameColumn); + newId = cursor.getString (idColumn); + newType = cursor.getString (typeColumn); - /* Now replace ID with the document ID. */ + /* Any of the three variables above may be NULL if the + column data is of the wrong type depending on how + the Cursor returned is implemented. */ - id = cursor.getString (column); - - /* If this is the last component, be sure to initialize - the document type. */ - - if (component == components[components.length - 1]) - { - column - = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); - - if (column < 0) + if (name == null || newId == null || newType == null) return -1; - type = cursor.getString (column); + /* Cache this name, even if it isn't the document + that's being searched for. */ - /* Type may be NULL depending on how the Cursor - returned is implemented. */ + cache = cacheChild (toplevel, children, name, + newId, newType, + idEntry != null); - if (type == null) - return -1; + /* Record the desired component once it is located, + but continue reading and caching items from the + cursor. */ + + if (name.equals (component)) + { + id = newId; + next = cache.children; + type = newType; + } } + children = next; + /* Now close the cursor. */ cursor.close (); cursor = null; @@ -771,11 +1219,12 @@ In addition, arbitrary runtime exceptions (such as openDocumentDirectory1 (String uri, String documentId, CancellationSignal signal) { - Uri uriObject; + Uri uriObject, tree; Cursor cursor; String projection[]; + CacheToplevel toplevel; - uriObject = Uri.parse (uri); + tree = uriObject = Uri.parse (uri); /* If documentId is not set, use the document ID of the tree URI itself. */ @@ -792,11 +1241,22 @@ In addition, arbitrary runtime exceptions (such as projection = new String [] { Document.COLUMN_DISPLAY_NAME, + Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, }; cursor = resolver.query (uriObject, projection, null, null, null, signal); + + /* Create a new cache entry tied to this document ID. */ + + if (cursor != null) + { + toplevel = getCache (tree); + cacheDirectoryFromCursor (toplevel, documentId, + cursor); + } + /* Return the cursor. */ return cursor; } diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index e410754071b..e2abd6c96ef 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1680,14 +1680,18 @@ In addition, arbitrary runtime exceptions (such as deleteDocument (String uri, String id) throws FileNotFoundException { - Uri uriObject; + Uri uriObject, tree; - uriObject = Uri.parse (uri); - uriObject = DocumentsContract.buildDocumentUriUsingTree (uriObject, - id); + tree = Uri.parse (uri); + uriObject = DocumentsContract.buildDocumentUriUsingTree (tree, id); if (DocumentsContract.deleteDocument (resolver, uriObject)) - return 0; + { + if (storageThread != null) + storageThread.postInvalidateCache (tree, id); + + return 0; + } return -1; } diff --git a/src/androidvfs.c b/src/androidvfs.c index 7f61a4b4fa3..babc7101d88 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -3728,6 +3728,7 @@ android_saf_exception_check (int n, ...) jthrowable exception; JNIEnv *env; va_list ap; + int new_errno; env = android_java_env; va_start (ap, n); @@ -3744,8 +3745,9 @@ android_saf_exception_check (int n, ...) /* JNI couldn't return a local reference to the exception. */ memory_full (0); - /* Clear the exception, making it safe to subsequently call other - JNI functions. */ + /* Print and clear the exception, making it safe to subsequently + call other JNI functions. */ + (*env)->ExceptionDescribe (env); (*env)->ExceptionClear (env); /* Delete each of the N arguments. */ @@ -3760,16 +3762,16 @@ android_saf_exception_check (int n, ...) if ((*env)->IsInstanceOf (env, (jobject) exception, file_not_found_exception)) - errno = ENOENT; + new_errno = ENOENT; else if ((*env)->IsInstanceOf (env, (jobject) exception, security_exception)) - errno = EACCES; + new_errno = EACCES; else if ((*env)->IsInstanceOf (env, (jobject) exception, operation_canceled_exception)) - errno = EINTR; + new_errno = EINTR; else if ((*env)->IsInstanceOf (env, (jobject) exception, unsupported_operation_exception)) - errno = ENOSYS; + new_errno = ENOSYS; else if ((*env)->IsInstanceOf (env, (jobject) exception, out_of_memory_error)) { @@ -3777,10 +3779,11 @@ android_saf_exception_check (int n, ...) memory_full (0); } else - errno = EIO; + new_errno = EIO; /* expression is still a local reference! */ ANDROID_DELETE_LOCAL_REF ((jobject) exception); + errno = new_errno; return 1; } @@ -4238,10 +4241,7 @@ android_document_id_from_name (const char *tree_uri, char *name, inside_saf_critical_section = false; if (android_saf_exception_check (3, result, uri, java_name)) - { - rc = -1; - goto finish; - } + return -1; ANDROID_DELETE_LOCAL_REF (uri); ANDROID_DELETE_LOCAL_REF (java_name); @@ -4251,7 +4251,8 @@ android_document_id_from_name (const char *tree_uri, char *name, if (rc == -1) { ANDROID_DELETE_LOCAL_REF (result); - goto finish; + errno = ENOENT; + return -1; } eassert (rc == -2 || rc >= 0); @@ -4274,8 +4275,6 @@ android_document_id_from_name (const char *tree_uri, char *name, (*android_java_env)->ReleaseStringUTFChars (android_java_env, (jstring) uri, doc_id); ANDROID_DELETE_LOCAL_REF (uri); - - finish: return rc; } commit 25ef0c3a89a92e8f5ea12afe43224c84632e89c6 Author: Po Lu Date: Fri Jul 28 16:43:40 2023 +0800 Fix SAF query * java/org/gnu/emacs/EmacsSafThread.java (documentIdFromName1): Fix query argument placeholder string. diff --git a/java/org/gnu/emacs/EmacsSafThread.java b/java/org/gnu/emacs/EmacsSafThread.java index fd06603fab3..cbd4ff0d25b 100644 --- a/java/org/gnu/emacs/EmacsSafThread.java +++ b/java/org/gnu/emacs/EmacsSafThread.java @@ -335,7 +335,7 @@ public abstract Object runObject (CancellationSignal signal) cursor = resolver.query (treeUri, projection, (Document.COLUMN_DISPLAY_NAME - + " = ?s"), + + " = ?"), new String[] { component, }, null, signal); commit a78d4256687373de109e1b88d51bfa0a278ab319 Author: Po Lu Date: Fri Jul 28 15:45:35 2023 +0800 Update Android port * src/androidvfs.c (android_document_id_from_name): Don't return 0 if an SAF exception occurs. diff --git a/src/androidvfs.c b/src/androidvfs.c index 4f485622ff4..7f61a4b4fa3 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -4179,7 +4179,9 @@ android_verify_jni_string (const char *name) document ID of that directory within *ID. If the designated file can't be located, return -1 and set errno - accordingly. */ + accordingly. The reasons for which a file can't be located are not + all immediately obvious: quitting, for example, can cause document + ID lookup to be canceled. */ static int android_document_id_from_name (const char *tree_uri, char *name, @@ -4236,7 +4238,10 @@ android_document_id_from_name (const char *tree_uri, char *name, inside_saf_critical_section = false; if (android_saf_exception_check (3, result, uri, java_name)) - goto finish; + { + rc = -1; + goto finish; + } ANDROID_DELETE_LOCAL_REF (uri); ANDROID_DELETE_LOCAL_REF (java_name); commit 3f4653400f1ad0ac07d9a4ac905777772494f41d Author: Po Lu Date: Fri Jul 28 15:24:47 2023 +0800 Avoid crashes when the primary clip is empty * src/androidselect.c (Fandroid_get_clipboard): Don't return data if clipboard is empty. Reported by Johan Widén . diff --git a/src/androidselect.c b/src/androidselect.c index f5371280457..9910e7921de 100644 --- a/src/androidselect.c +++ b/src/androidselect.c @@ -194,6 +194,9 @@ DEFUN ("android-get-clipboard", Fandroid_get_clipboard, method); android_exception_check (); + if (!bytes) + return Qnil; + length = (*android_java_env)->GetArrayLength (android_java_env, bytes); data = (*android_java_env)->GetByteArrayElements (android_java_env, commit 0709e03f88cdef8f785338cab9315b527db0854e Author: Po Lu Date: Fri Jul 28 15:19:37 2023 +0800 Allow quitting from Android content provider operations * doc/emacs/android.texi (Android Document Providers): Say that quitting is now possible. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New functions `safSyncAndReadInput', `safync' and `safPostRequest'. * java/org/gnu/emacs/EmacsSafThread.java: New file. Move cancel-able SAF operations here. * java/org/gnu/emacs/EmacsService.java (EmacsService): Allow quitting from most SAF operations. * src/androidvfs.c (android_saf_exception_check): Return EINTR if OperationCanceledException is received. (android_saf_stat, android_saf_access) (android_document_id_from_name, android_saf_tree_opendir_1) (android_saf_file_open): Don't allow reentrant calls from async input handlers. (NATIVE_NAME): Implement new synchronization primitives for JNI. (android_vfs_init): Initialize new class. * src/dired.c (open_directory): Handle EINTR from opendir. * src/sysdep.c: Describe which operations may return EINTR on Android. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index b86c71cea49..0330e9b5890 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -305,14 +305,10 @@ Android Document Providers create files inside these directories, it cannot create symlinks or hard links. -@c TODO: fix this! Since document providers are allowed to perform expensive network operations to obtain file contents, a file access operation within one -of these directories will possibly take a significant amount of time. -Emacs presently does not support quitting out of such file system -operations, and the timeouts applied are fully subject to the -discretion of the system and the document provider that is responding -to these operations. +of these directories has the potential to take a significant amount of +time. @node Android Environment @section Running Emacs under Android diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index d4d502ede5a..ea200037218 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -257,6 +257,23 @@ public static native void blitRect (Bitmap src, Bitmap dest, int x1, public static native void notifyPixelsChanged (Bitmap bitmap); + + /* Functions used to synchronize document provider access with the + main thread. */ + + /* Wait for a call to `safPostRequest' while also reading async + input. + + If asynchronous input arrives and sets Vquit_flag, return 1. */ + public static native int safSyncAndReadInput (); + + /* Wait for a call to `safPostRequest'. */ + public static native void safSync (); + + /* Post the semaphore used to await the completion of SAF + operations. */ + public static native void safPostRequest (); + static { /* Older versions of Android cannot link correctly with shared diff --git a/java/org/gnu/emacs/EmacsSafThread.java b/java/org/gnu/emacs/EmacsSafThread.java new file mode 100644 index 00000000000..fd06603fab3 --- /dev/null +++ b/java/org/gnu/emacs/EmacsSafThread.java @@ -0,0 +1,922 @@ +/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- + + Copyright (C) 2023 Free Software Foundation, Inc. + + This file is part of GNU Emacs. + + GNU Emacs 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 3 of the License, or (at + your option) any later version. + + GNU Emacs 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 GNU Emacs. If not, see . */ + +package org.gnu.emacs; + +import android.content.ContentResolver; +import android.database.Cursor; +import android.net.Uri; + +import android.os.Build; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.ParcelFileDescriptor; + +import android.provider.DocumentsContract; +import android.provider.DocumentsContract.Document; + + + +/* Emacs runs long-running SAF operations on a second thread running + its own handler. These operations include opening files and + maintaining the path to document ID cache. + +#if 0 + Because Emacs paths are based on file display names, while Android + document identifiers have no discernible hierarchy of their own, + each file name lookup must carry out a repeated search for + directory documents with the names of all of the file name's + constituent components, where each iteration searches within the + directory document identified by the previous iteration. + + A time limited cache tying components to document IDs is maintained + in order to speed up consecutive searches for file names sharing + the same components. Since listening for changes to each document + in the cache is prohibitively expensive, Emacs instead elects to + periodically remove entries that are older than a predetermined + amount of a time. + + The cache is structured much like the directory trees whose + information it records, with each entry in the cache containing a + list of entries for their children. File name lookup consults the + cache and populates it with missing information simultaneously. + + This is not yet implemented. +#endif + + Long-running operations are also run on this thread for another + reason: Android uses special cancellation objects to terminate + ongoing IPC operations. However, the functions that perform these + operations block instead of providing mechanisms for the caller to + wait for their completion while also reading async input, as a + consequence of which the calling thread is unable to signal the + cancellation objects that it provides. Performing the blocking + operations in this auxiliary thread enables the main thread to wait + for completion itself, signaling the cancellation objects when it + deems necessary. */ + + + +public final class EmacsSafThread extends HandlerThread +{ + /* The content resolver used by this thread. */ + private final ContentResolver resolver; + + /* Handler for this thread's main loop. */ + private Handler handler; + + /* File access mode constants. See `man 7 inode'. */ + public static final int S_IRUSR = 0000400; + public static final int S_IWUSR = 0000200; + public static final int S_IFCHR = 0020000; + public static final int S_IFDIR = 0040000; + public static final int S_IFREG = 0100000; + + public + EmacsSafThread (ContentResolver resolver) + { + super ("Document provider access thread"); + this.resolver = resolver; + } + + + + @Override + public void + start () + { + super.start (); + + /* Set up the handler after the thread starts. */ + handler = new Handler (getLooper ()); + } + + + + /* ``Prototypes'' for nested functions that are run within the SAF + thread and accepts a cancellation signal. They differ in their + return types. */ + + private abstract class SafIntFunction + { + /* The ``throws Throwable'' here is a Java idiosyncracy that tells + the compiler to allow arbitrary error objects to be signaled + from within this function. + + Later, runIntFunction will try to re-throw any error object + generated by this function in the Emacs thread, using a trick + to avoid the compiler requirement to expressly declare that an + error (and which types of errors) will be signaled. */ + + public abstract int runInt (CancellationSignal signal) + throws Throwable; + }; + + private abstract class SafObjectFunction + { + /* The ``throws Throwable'' here is a Java idiosyncracy that tells + the compiler to allow arbitrary error objects to be signaled + from within this function. + + Later, runObjectFunction will try to re-throw any error object + generated by this function in the Emacs thread, using a trick + to avoid the compiler requirement to expressly declare that an + error (and which types of errors) will be signaled. */ + + public abstract Object runObject (CancellationSignal signal) + throws Throwable; + }; + + + + /* Functions that run cancel-able queries. These functions are + internally run within the SAF thread. */ + + /* Throw the specified EXCEPTION. The type template T is erased by + the compiler before the object is compiled, so the compiled code + simply throws EXCEPTION without the cast being verified. + + T should be RuntimeException to obtain the desired effect of + throwing an exception without a compiler check. */ + + @SuppressWarnings("unchecked") + private static void + throwException (Throwable exception) + throws T + { + throw (T) exception; + } + + /* Run the given function (or rather, its `runInt' field) within the + SAF thread, waiting for it to complete. + + If async input arrives in the meantime and sets Vquit_flag, + signal the cancellation signal supplied to that function. + + Rethrow any exception thrown from that function, and return its + value otherwise. */ + + private int + runIntFunction (final SafIntFunction function) + { + final EmacsHolder result; + final CancellationSignal signal; + Throwable throwable; + + result = new EmacsHolder (); + signal = new CancellationSignal (); + + handler.post (new Runnable () { + @Override + public void + run () + { + try + { + result.thing + = Integer.valueOf (function.runInt (signal)); + } + catch (Throwable throwable) + { + result.thing = throwable; + } + + EmacsNative.safPostRequest (); + } + }); + + if (EmacsNative.safSyncAndReadInput () != 0) + { + signal.cancel (); + + /* Now wait for the function to finish. Either the signal has + arrived after the query took place, in which case it will + finish normally, or an OperationCanceledException will be + thrown. */ + + EmacsNative.safSync (); + } + + if (result.thing instanceof Throwable) + { + throwable = (Throwable) result.thing; + EmacsSafThread.throwException (throwable); + } + + return (Integer) result.thing; + } + + /* Run the given function (or rather, its `runObject' field) within + the SAF thread, waiting for it to complete. + + If async input arrives in the meantime and sets Vquit_flag, + signal the cancellation signal supplied to that function. + + Rethrow any exception thrown from that function, and return its + value otherwise. */ + + private Object + runObjectFunction (final SafObjectFunction function) + { + final EmacsHolder result; + final CancellationSignal signal; + Throwable throwable; + + result = new EmacsHolder (); + signal = new CancellationSignal (); + + handler.post (new Runnable () { + @Override + public void + run () + { + try + { + result.thing = function.runObject (signal); + } + catch (Throwable throwable) + { + result.thing = throwable; + } + + EmacsNative.safPostRequest (); + } + }); + + if (EmacsNative.safSyncAndReadInput () != 0) + { + signal.cancel (); + + /* Now wait for the function to finish. Either the signal has + arrived after the query took place, in which case it will + finish normally, or an OperationCanceledException will be + thrown. */ + + EmacsNative.safSync (); + } + + if (result.thing instanceof Throwable) + { + throwable = (Throwable) result.thing; + EmacsSafThread.throwException (throwable); + } + + return result.thing; + } + + /* The crux of `documentIdFromName1', run within the SAF thread. + SIGNAL should be a cancellation signal run upon quitting. */ + + private int + documentIdFromName1 (String tree_uri, String name, + String[] id_return, CancellationSignal signal) + { + Uri uri, treeUri; + String id, type; + String[] components, projection; + Cursor cursor; + int column; + + projection = new String[] { + Document.COLUMN_DISPLAY_NAME, + Document.COLUMN_DOCUMENT_ID, + Document.COLUMN_MIME_TYPE, + }; + + /* Parse the URI identifying the tree first. */ + uri = Uri.parse (tree_uri); + + /* Now, split NAME into its individual components. */ + components = name.split ("/"); + + /* Set id and type to the value at the root of the tree. */ + type = id = null; + cursor = null; + + /* For each component... */ + + try + { + for (String component : components) + { + /* Java split doesn't behave very much like strtok when it + comes to trailing and leading delimiters... */ + if (component.isEmpty ()) + continue; + + /* Create the tree URI for URI from ID if it exists, or + the root otherwise. */ + + if (id == null) + id = DocumentsContract.getTreeDocumentId (uri); + + treeUri + = DocumentsContract.buildChildDocumentsUriUsingTree (uri, id); + + /* Look for a file in this directory by the name of + component. */ + + cursor = resolver.query (treeUri, projection, + (Document.COLUMN_DISPLAY_NAME + + " = ?s"), + new String[] { component, }, + null, signal); + + if (cursor == null) + return -1; + + while (true) + { + /* Even though the query selects for a specific + display name, some content providers nevertheless + return every file within the directory. */ + + if (!cursor.moveToNext ()) + { + /* If the last component considered is a + directory... */ + if ((type == null + || type.equals (Document.MIME_TYPE_DIR)) + /* ... and type and id currently represent the + penultimate component. */ + && component == components[components.length - 1]) + { + /* The cursor is empty. In this case, return + -2 and the current document ID (belonging + to the previous component) in + ID_RETURN. */ + + id_return[0] = id; + + /* But return -1 on the off chance that id is + null. */ + + if (id == null) + return -1; + + return -2; + } + + /* The last component found is not a directory, so + return -1. */ + return -1; + } + + /* So move CURSOR to a row with the right display + name. */ + + column = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME); + + if (column < 0) + continue; + + name = cursor.getString (column); + + /* Break out of the loop only once a matching + component is found. */ + + if (name.equals (component)) + break; + } + + /* Look for a column by the name of + COLUMN_DOCUMENT_ID. */ + + column = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID); + + if (column < 0) + return -1; + + /* Now replace ID with the document ID. */ + + id = cursor.getString (column); + + /* If this is the last component, be sure to initialize + the document type. */ + + if (component == components[components.length - 1]) + { + column + = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + + if (column < 0) + return -1; + + type = cursor.getString (column); + + /* Type may be NULL depending on how the Cursor + returned is implemented. */ + + if (type == null) + return -1; + } + + /* Now close the cursor. */ + cursor.close (); + cursor = null; + + /* ID may have become NULL if the data is in an invalid + format. */ + if (id == null) + return -1; + } + } + finally + { + /* If an error is thrown within the block above, let + android_saf_exception_check handle it, but make sure the + cursor is closed. */ + + if (cursor != null) + cursor.close (); + } + + /* Here, id is either NULL (meaning the same as TREE_URI), and + type is either NULL (in which case id should also be NULL) or + the MIME type of the file. */ + + /* First return the ID. */ + + if (id == null) + id_return[0] = DocumentsContract.getTreeDocumentId (uri); + else + id_return[0] = id; + + /* Next, return whether or not this is a directory. */ + if (type == null || type.equals (Document.MIME_TYPE_DIR)) + return 1; + + return 0; + } + + /* Find the document ID of the file within TREE_URI designated by + NAME. + + NAME is a ``file name'' comprised of the display names of + individual files. Each constituent component prior to the last + must name a directory file within TREE_URI. + + Upon success, return 0 or 1 (contingent upon whether or not the + last component within NAME is a directory) and place the document + ID of the named file in ID_RETURN[0]. + + If the designated file can't be located, but each component of + NAME up to the last component can and is a directory, return -2 + and the ID of the last component located in ID_RETURN[0]. + + If the designated file can't be located, return -1, or signal one + of OperationCanceledException, SecurityException, + FileNotFoundException, or UnsupportedOperationException. */ + + public int + documentIdFromName (final String tree_uri, final String name, + final String[] id_return) + { + return runIntFunction (new SafIntFunction () { + @Override + public int + runInt (CancellationSignal signal) + { + return documentIdFromName1 (tree_uri, name, id_return, + signal); + } + }); + } + + /* The bulk of `statDocument'. SIGNAL should be a cancelation + signal. */ + + private long[] + statDocument1 (String uri, String documentId, + CancellationSignal signal) + { + Uri uriObject; + String[] projection; + long[] stat; + int index; + long tem; + String tem1; + Cursor cursor; + + uriObject = Uri.parse (uri); + + if (documentId == null) + documentId = DocumentsContract.getTreeDocumentId (uriObject); + + /* Create a document URI representing DOCUMENTID within URI's + authority. */ + + uriObject + = DocumentsContract.buildDocumentUriUsingTree (uriObject, documentId); + + /* Now stat this document. */ + + projection = new String[] { + Document.COLUMN_FLAGS, + Document.COLUMN_LAST_MODIFIED, + Document.COLUMN_MIME_TYPE, + Document.COLUMN_SIZE, + }; + + cursor = resolver.query (uriObject, projection, null, + null, null, signal); + + if (cursor == null) + return null; + + if (!cursor.moveToFirst ()) + { + cursor.close (); + return null; + } + + /* Create the array of file status. */ + stat = new long[3]; + + try + { + index = cursor.getColumnIndex (Document.COLUMN_FLAGS); + if (index < 0) + return null; + + tem = cursor.getInt (index); + + stat[0] |= S_IRUSR; + if ((tem & Document.FLAG_SUPPORTS_WRITE) != 0) + stat[0] |= S_IWUSR; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N + && (tem & Document.FLAG_VIRTUAL_DOCUMENT) != 0) + stat[0] |= S_IFCHR; + + index = cursor.getColumnIndex (Document.COLUMN_SIZE); + if (index < 0) + return null; + + if (cursor.isNull (index)) + stat[1] = -1; /* The size is unknown. */ + else + stat[1] = cursor.getLong (index); + + index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + if (index < 0) + return null; + + tem1 = cursor.getString (index); + + /* Check if this is a directory file. */ + if (tem1.equals (Document.MIME_TYPE_DIR) + /* Files shouldn't be specials and directories at the same + time, but Android doesn't forbid document providers + from returning this information. */ + && (stat[0] & S_IFCHR) == 0) + /* Since FLAG_SUPPORTS_WRITE doesn't apply to directories, + just assume they're writable. */ + stat[0] |= S_IFDIR | S_IWUSR; + + /* If this file is neither a character special nor a + directory, indicate that it's a regular file. */ + + if ((stat[0] & (S_IFDIR | S_IFCHR)) == 0) + stat[0] |= S_IFREG; + + index = cursor.getColumnIndex (Document.COLUMN_LAST_MODIFIED); + + if (index >= 0 && !cursor.isNull (index)) + { + /* Content providers are allowed to not provide mtime. */ + tem = cursor.getLong (index); + stat[2] = tem; + } + } + finally + { + cursor.close (); + } + + return stat; + } + + /* Return file status for the document designated by the given + DOCUMENTID and tree URI. If DOCUMENTID is NULL, use the document + ID in URI itself. + + Value is null upon failure, or an array of longs [MODE, SIZE, + MTIM] upon success, where MODE contains the file type and access + modes of the file as in `struct stat', SIZE is the size of the + file in BYTES or -1 if not known, and MTIM is the time of the + last modification to this file in milliseconds since 00:00, + January 1st, 1970. + + OperationCanceledException and other typical exceptions may be + signaled upon receiving async input or other errors. */ + + public long[] + statDocument (final String uri, final String documentId) + { + return (long[]) runObjectFunction (new SafObjectFunction () { + @Override + public Object + runObject (CancellationSignal signal) + { + return statDocument1 (uri, documentId, signal); + } + }); + } + + /* The bulk of `accessDocument'. SIGNAL should be a cancellation + signal. */ + + private int + accessDocument1 (String uri, String documentId, boolean writable, + CancellationSignal signal) + { + Uri uriObject; + String[] projection; + int tem, index; + String tem1; + Cursor cursor; + + uriObject = Uri.parse (uri); + + if (documentId == null) + documentId = DocumentsContract.getTreeDocumentId (uriObject); + + /* Create a document URI representing DOCUMENTID within URI's + authority. */ + + uriObject + = DocumentsContract.buildDocumentUriUsingTree (uriObject, documentId); + + /* Now stat this document. */ + + projection = new String[] { + Document.COLUMN_FLAGS, + Document.COLUMN_MIME_TYPE, + }; + + cursor = resolver.query (uriObject, projection, null, + null, null, signal); + + if (cursor == null) + return -1; + + try + { + if (!cursor.moveToFirst ()) + return -1; + + if (!writable) + return 0; + + index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + if (index < 0) + return -3; + + /* Get the type of this file to check if it's a directory. */ + tem1 = cursor.getString (index); + + /* Check if this is a directory file. */ + if (tem1.equals (Document.MIME_TYPE_DIR)) + { + /* If so, don't check for FLAG_SUPPORTS_WRITE. + Check for FLAG_DIR_SUPPORTS_CREATE instead. */ + + if (!writable) + return 0; + + index = cursor.getColumnIndex (Document.COLUMN_FLAGS); + if (index < 0) + return -3; + + tem = cursor.getInt (index); + if ((tem & Document.FLAG_DIR_SUPPORTS_CREATE) == 0) + return -3; + + return 0; + } + + index = cursor.getColumnIndex (Document.COLUMN_FLAGS); + if (index < 0) + return -3; + + tem = cursor.getInt (index); + if (writable && (tem & Document.FLAG_SUPPORTS_WRITE) == 0) + return -3; + } + finally + { + /* Close the cursor if an exception occurs. */ + cursor.close (); + } + + return 0; + } + + /* Find out whether Emacs has access to the document designated by + the specified DOCUMENTID within the tree URI. If DOCUMENTID is + NULL, use the document ID in URI itself. + + If WRITABLE, also check that the file is writable, which is true + if it is either a directory or its flags contains + FLAG_SUPPORTS_WRITE. + + Value is 0 if the file is accessible, and one of the following if + not: + + -1, if the file does not exist. + -2, if WRITABLE and the file is not writable. + -3, upon any other error. + + In addition, arbitrary runtime exceptions (such as + SecurityException or UnsupportedOperationException) may be + thrown. */ + + public int + accessDocument (final String uri, final String documentId, + final boolean writable) + { + return runIntFunction (new SafIntFunction () { + @Override + public int + runInt (CancellationSignal signal) + { + return accessDocument1 (uri, documentId, writable, + signal); + } + }); + } + + /* The crux of openDocumentDirectory. SIGNAL must be a cancellation + signal. */ + + private Cursor + openDocumentDirectory1 (String uri, String documentId, + CancellationSignal signal) + { + Uri uriObject; + Cursor cursor; + String projection[]; + + uriObject = Uri.parse (uri); + + /* If documentId is not set, use the document ID of the tree URI + itself. */ + + if (documentId == null) + documentId = DocumentsContract.getTreeDocumentId (uriObject); + + /* Build a URI representing each directory entry within + DOCUMENTID. */ + + uriObject + = DocumentsContract.buildChildDocumentsUriUsingTree (uriObject, + documentId); + + projection = new String [] { + Document.COLUMN_DISPLAY_NAME, + Document.COLUMN_MIME_TYPE, + }; + + cursor = resolver.query (uriObject, projection, null, null, + null, signal); + /* Return the cursor. */ + return cursor; + } + + /* Open a cursor representing each entry within the directory + designated by the specified DOCUMENTID within the tree URI. + + If DOCUMENTID is NULL, use the document ID within URI itself. + Value is NULL upon failure. + + In addition, arbitrary runtime exceptions (such as + SecurityException or UnsupportedOperationException) may be + thrown. */ + + public Cursor + openDocumentDirectory (final String uri, final String documentId) + { + return (Cursor) runObjectFunction (new SafObjectFunction () { + @Override + public Object + runObject (CancellationSignal signal) + { + return openDocumentDirectory1 (uri, documentId, signal); + } + }); + } + + /* The crux of `openDocument'. SIGNAL must be a cancellation + signal. */ + + public ParcelFileDescriptor + openDocument1 (String uri, String documentId, boolean write, + boolean truncate, CancellationSignal signal) + throws Throwable + { + Uri treeUri, documentUri; + String mode; + ParcelFileDescriptor fileDescriptor; + + treeUri = Uri.parse (uri); + + /* documentId must be set for this request, since it doesn't make + sense to ``open'' the root of the directory tree. */ + + documentUri + = DocumentsContract.buildDocumentUriUsingTree (treeUri, documentId); + + if (write || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) + { + /* Select the mode used to open the file. `rw' means open + a stat-able file, while `rwt' means that and to + truncate the file as well. */ + + if (truncate) + mode = "rwt"; + else + mode = "rw"; + + fileDescriptor + = resolver.openFileDescriptor (documentUri, mode, + signal); + } + else + { + /* Select the mode used to open the file. `openFile' + below means always open a stat-able file. */ + + if (truncate) + /* Invalid mode! */ + return null; + else + mode = "r"; + + fileDescriptor = resolver.openFile (documentUri, mode, + signal); + } + + return fileDescriptor; + } + + /* Open a file descriptor for a file document designated by + DOCUMENTID within the document tree identified by URI. If + TRUNCATE and the document already exists, truncate its contents + before returning. + + On Android 9.0 and earlier, always open the document in + ``read-write'' mode; this instructs the document provider to + return a seekable file that is stored on disk and returns correct + file status. + + Under newer versions of Android, open the document in a + non-writable mode if WRITE is false. This is possible because + these versions allow Emacs to explicitly request a seekable + on-disk file. + + Value is NULL upon failure or a parcel file descriptor upon + success. Call `ParcelFileDescriptor.close' on this file + descriptor instead of using the `close' system call. + + FileNotFoundException and/or SecurityException and/or + UnsupportedOperationException and/or OperationCanceledException + may be thrown upon failure. */ + + public ParcelFileDescriptor + openDocument (final String uri, final String documentId, + final boolean write, final boolean truncate) + { + Object tem; + + tem = runObjectFunction (new SafObjectFunction () { + @Override + public Object + runObject (CancellationSignal signal) + throws Throwable + { + return openDocument1 (uri, documentId, write, truncate, + signal); + } + }); + + return (ParcelFileDescriptor) tem; + } +}; diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index aa672994f12..e410754071b 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -109,13 +109,6 @@ public final class EmacsService extends Service public static final int IC_MODE_ACTION = 1; public static final int IC_MODE_TEXT = 2; - /* File access mode constants. See `man 7 inode'. */ - public static final int S_IRUSR = 0000400; - public static final int S_IWUSR = 0000200; - public static final int S_IFCHR = 0020000; - public static final int S_IFDIR = 0040000; - public static final int S_IFREG = 0100000; - /* Display metrics used by font backends. */ public DisplayMetrics metrics; @@ -134,6 +127,10 @@ public final class EmacsService extends Service being called, and 2 if icBeginSynchronous was called. */ public static final AtomicInteger servicingQuery; + /* Thread used to query document providers, or null if it hasn't + been created yet. */ + private EmacsSafThread storageThread; + static { servicingQuery = new AtomicInteger (); @@ -1160,10 +1157,7 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) /* Document tree management functions. These functions shouldn't be - called before Android 5.0. - - TODO: a timeout, let alone quitting, has yet to be implemented - for any of these functions. */ + called before Android 5.0. */ /* Return an array of each document authority providing at least one tree URI that Emacs holds the rights to persistently access. */ @@ -1319,223 +1313,26 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) If the designated file can't be located, but each component of NAME up to the last component can and is a directory, return -2 - and the ID of the last component located in ID_RETURN[0]; + and the ID of the last component located in ID_RETURN[0]. - If the designated file can't be located, return -1. */ + If the designated file can't be located, return -1, or signal one + of OperationCanceledException, SecurityException, + FileNotFoundException, or UnsupportedOperationException. */ private int documentIdFromName (String tree_uri, String name, String[] id_return) { - Uri uri, treeUri; - String id, type; - String[] components, projection; - Cursor cursor; - int column; - - projection = new String[] { - Document.COLUMN_DISPLAY_NAME, - Document.COLUMN_DOCUMENT_ID, - Document.COLUMN_MIME_TYPE, - }; - - /* Parse the URI identifying the tree first. */ - uri = Uri.parse (tree_uri); - - /* Now, split NAME into its individual components. */ - components = name.split ("/"); + /* Start the thread used to run SAF requests if it isn't already + running. */ - /* Set id and type to the value at the root of the tree. */ - type = id = null; - - /* For each component... */ - - for (String component : components) + if (storageThread == null) { - /* Java split doesn't behave very much like strtok when it - comes to trailing and leading delimiters... */ - if (component.isEmpty ()) - continue; - - /* Create the tree URI for URI from ID if it exists, or the - root otherwise. */ - - if (id == null) - id = DocumentsContract.getTreeDocumentId (uri); - - treeUri - = DocumentsContract.buildChildDocumentsUriUsingTree (uri, id); - - /* Look for a file in this directory by the name of - component. */ - - try - { - cursor = resolver.query (treeUri, projection, - (Document.COLUMN_DISPLAY_NAME - + " = ?s"), - new String[] { component, }, null); - } - catch (SecurityException exception) - { - /* A SecurityException can be thrown if Emacs doesn't have - access to treeUri. */ - return -1; - } - catch (Exception exception) - { - exception.printStackTrace (); - - /* Why is this? */ - return -1; - } - - if (cursor == null) - return -1; - - while (true) - { - /* Even though the query selects for a specific display - name, some content providers nevertheless return every - file within the directory. */ - - if (!cursor.moveToNext ()) - { - cursor.close (); - - /* If the last component considered is a - directory... */ - if ((type == null - || type.equals (Document.MIME_TYPE_DIR)) - /* ... and type and id currently represent the - penultimate component. */ - && component == components[components.length - 1]) - { - /* The cursor is empty. In this case, return -2 - and the current document ID (belonging to the - previous component) in ID_RETURN. */ - - id_return[0] = id; - - /* But return -1 on the off chance that id is - null. */ - - if (id == null) - return -1; - - return -2; - } - - /* The last component found is not a directory, so - return -1. */ - return -1; - } - - /* So move CURSOR to a row with the right display - name. */ - - column = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME); - - if (column < 0) - continue; - - try - { - name = cursor.getString (column); - } - catch (Exception exception) - { - cursor.close (); - return -1; - } - - /* Break out of the loop only once a matching component is - found. */ - - if (name.equals (component)) - break; - } - - /* Look for a column by the name of COLUMN_DOCUMENT_ID. */ - - column = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID); - - if (column < 0) - { - cursor.close (); - return -1; - } - - /* Now replace ID with the document ID. */ - - try - { - id = cursor.getString (column); - } - catch (Exception exception) - { - cursor.close (); - return -1; - } - - /* If this is the last component, be sure to initialize the - document type. */ - - if (component == components[components.length - 1]) - { - column - = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); - - if (column < 0) - { - cursor.close (); - return -1; - } - - try - { - type = cursor.getString (column); - } - catch (Exception exception) - { - cursor.close (); - return -1; - } - - /* Type may be NULL depending on how the Cursor returned - is implemented. */ - - if (type == null) - { - cursor.close (); - return -1; - } - } - - /* Now close the cursor. */ - cursor.close (); - - /* ID may have become NULL if the data is in an invalid - format. */ - if (id == null) - return -1; + storageThread = new EmacsSafThread (resolver); + storageThread.start (); } - /* Here, id is either NULL (meaning the same as TREE_URI), and - type is either NULL (in which case id should also be NULL) or - the MIME type of the file. */ - - /* First return the ID. */ - - if (id == null) - id_return[0] = DocumentsContract.getTreeDocumentId (uri); - else - id_return[0] = id; - - /* Next, return whether or not this is a directory. */ - if (type == null || type.equals (Document.MIME_TYPE_DIR)) - return 1; - - return 0; + return storageThread.documentIdFromName (tree_uri, name, + id_return); } /* Return an encoded document URI representing a tree with the @@ -1585,130 +1382,24 @@ type is either NULL (in which case id should also be NULL) or modes of the file as in `struct stat', SIZE is the size of the file in BYTES or -1 if not known, and MTIM is the time of the last modification to this file in milliseconds since 00:00, - January 1st, 1970. */ + January 1st, 1970. + + OperationCanceledException and other typical exceptions may be + signaled upon receiving async input or other errors. */ public long[] statDocument (String uri, String documentId) { - Uri uriObject; - String[] projection; - long[] stat; - int index; - long tem; - String tem1; - Cursor cursor; - - uriObject = Uri.parse (uri); - - if (documentId == null) - documentId = DocumentsContract.getTreeDocumentId (uriObject); - - /* Create a document URI representing DOCUMENTID within URI's - authority. */ - - uriObject - = DocumentsContract.buildDocumentUriUsingTree (uriObject, documentId); - - /* Now stat this document. */ - - projection = new String[] { - Document.COLUMN_FLAGS, - Document.COLUMN_LAST_MODIFIED, - Document.COLUMN_MIME_TYPE, - Document.COLUMN_SIZE, - }; - - try - { - cursor = resolver.query (uriObject, projection, null, - null, null); - } - catch (SecurityException exception) - { - /* A SecurityException can be thrown if Emacs doesn't have - access to uriObject. */ - return null; - } - catch (UnsupportedOperationException exception) - { - exception.printStackTrace (); - - /* Why is this? */ - return null; - } - - if (cursor == null || !cursor.moveToFirst ()) - return null; - - /* Create the array of file status. */ - stat = new long[3]; + /* Start the thread used to run SAF requests if it isn't already + running. */ - try + if (storageThread == null) { - index = cursor.getColumnIndex (Document.COLUMN_FLAGS); - if (index < 0) - return null; - - tem = cursor.getInt (index); - - stat[0] |= S_IRUSR; - if ((tem & Document.FLAG_SUPPORTS_WRITE) != 0) - stat[0] |= S_IWUSR; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N - && (tem & Document.FLAG_VIRTUAL_DOCUMENT) != 0) - stat[0] |= S_IFCHR; - - index = cursor.getColumnIndex (Document.COLUMN_SIZE); - if (index < 0) - return null; - - if (cursor.isNull (index)) - stat[1] = -1; /* The size is unknown. */ - else - stat[1] = cursor.getLong (index); - - index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); - if (index < 0) - return null; - - tem1 = cursor.getString (index); - - /* Check if this is a directory file. */ - if (tem1.equals (Document.MIME_TYPE_DIR) - /* Files shouldn't be specials and directories at the same - time, but Android doesn't forbid document providers - from returning this information. */ - && (stat[0] & S_IFCHR) == 0) - /* Since FLAG_SUPPORTS_WRITE doesn't apply to directories, - just assume they're writable. */ - stat[0] |= S_IFDIR | S_IWUSR; - - /* If this file is neither a character special nor a - directory, indicate that it's a regular file. */ - - if ((stat[0] & (S_IFDIR | S_IFCHR)) == 0) - stat[0] |= S_IFREG; - - index = cursor.getColumnIndex (Document.COLUMN_LAST_MODIFIED); - - if (index >= 0 && !cursor.isNull (index)) - { - /* Content providers are allowed to not provide mtime. */ - tem = cursor.getLong (index); - stat[2] = tem; - } - } - catch (Exception exception) - { - /* Whether or not type errors cause exceptions to be signaled - is defined ``by the implementation of Cursor'', whatever - that means. */ - exception.printStackTrace (); - return null; + storageThread = new EmacsSafThread (resolver); + storageThread.start (); } - return stat; + return storageThread.statDocument (uri, documentId); } /* Find out whether Emacs has access to the document designated by @@ -1733,83 +1424,16 @@ In addition, arbitrary runtime exceptions (such as public int accessDocument (String uri, String documentId, boolean writable) { - Uri uriObject; - String[] projection; - int tem, index; - String tem1; - Cursor cursor; - - uriObject = Uri.parse (uri); - - if (documentId == null) - documentId = DocumentsContract.getTreeDocumentId (uriObject); - - /* Create a document URI representing DOCUMENTID within URI's - authority. */ - - uriObject - = DocumentsContract.buildDocumentUriUsingTree (uriObject, documentId); - - /* Now stat this document. */ + /* Start the thread used to run SAF requests if it isn't already + running. */ - projection = new String[] { - Document.COLUMN_FLAGS, - Document.COLUMN_MIME_TYPE, - }; - - cursor = resolver.query (uriObject, projection, null, - null, null); - - if (cursor == null || !cursor.moveToFirst ()) - return -1; - - if (!writable) - return 0; - - try + if (storageThread == null) { - index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); - if (index < 0) - return -3; - - /* Get the type of this file to check if it's a directory. */ - tem1 = cursor.getString (index); - - /* Check if this is a directory file. */ - if (tem1.equals (Document.MIME_TYPE_DIR)) - { - /* If so, don't check for FLAG_SUPPORTS_WRITE. - Check for FLAG_DIR_SUPPORTS_CREATE instead. */ - - if (!writable) - return 0; - - index = cursor.getColumnIndex (Document.COLUMN_FLAGS); - if (index < 0) - return -3; - - tem = cursor.getInt (index); - if ((tem & Document.FLAG_DIR_SUPPORTS_CREATE) == 0) - return -3; - - return 0; - } - - index = cursor.getColumnIndex (Document.COLUMN_FLAGS); - if (index < 0) - return -3; - - tem = cursor.getInt (index); - if (writable && (tem & Document.FLAG_SUPPORTS_WRITE) == 0) - return -3; - } - finally - { - /* Close the cursor if an exception occurs. */ - cursor.close (); + storageThread = new EmacsSafThread (resolver); + storageThread.start (); } - return 0; + return storageThread.accessDocument (uri, documentId, writable); } /* Open a cursor representing each entry within the directory @@ -1825,34 +1449,16 @@ In addition, arbitrary runtime exceptions (such as public Cursor openDocumentDirectory (String uri, String documentId) { - Uri uriObject; - Cursor cursor; - String projection[]; - - uriObject = Uri.parse (uri); - - /* If documentId is not set, use the document ID of the tree URI - itself. */ - - if (documentId == null) - documentId = DocumentsContract.getTreeDocumentId (uriObject); - - /* Build a URI representing each directory entry within - DOCUMENTID. */ - - uriObject - = DocumentsContract.buildChildDocumentsUriUsingTree (uriObject, - documentId); + /* Start the thread used to run SAF requests if it isn't already + running. */ - projection = new String [] { - Document.COLUMN_DISPLAY_NAME, - Document.COLUMN_MIME_TYPE, - }; + if (storageThread == null) + { + storageThread = new EmacsSafThread (resolver); + storageThread.start (); + } - cursor = resolver.query (uriObject, projection, null, null, - null); - /* Return the cursor. */ - return cursor; + return storageThread.openDocumentDirectory (uri, documentId); } /* Read a single directory entry from the specified CURSOR. Return @@ -1945,50 +1551,18 @@ In addition, arbitrary runtime exceptions (such as public ParcelFileDescriptor openDocument (String uri, String documentId, boolean write, boolean truncate) - throws FileNotFoundException { - Uri treeUri, documentUri; - String mode; - ParcelFileDescriptor fileDescriptor; - - treeUri = Uri.parse (uri); - - /* documentId must be set for this request, since it doesn't make - sense to ``open'' the root of the directory tree. */ + /* Start the thread used to run SAF requests if it isn't already + running. */ - documentUri - = DocumentsContract.buildDocumentUriUsingTree (treeUri, documentId); - - if (write || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) + if (storageThread == null) { - /* Select the mode used to open the file. `rw' means open - a stat-able file, while `rwt' means that and to - truncate the file as well. */ - - if (truncate) - mode = "rwt"; - else - mode = "rw"; - - fileDescriptor - = resolver.openFileDescriptor (documentUri, mode, - null); - } - else - { - /* Select the mode used to open the file. `openFile' - below means always open a stat-able file. */ - - if (truncate) - /* Invalid mode! */ - return null; - else - mode = "r"; - - fileDescriptor = resolver.openFile (documentUri, mode, null); + storageThread = new EmacsSafThread (resolver); + storageThread.start (); } - return fileDescriptor; + return storageThread.openDocument (uri, documentId, write, + truncate); } /* Create a new document with the given display NAME within the diff --git a/src/androidvfs.c b/src/androidvfs.c index 2cd50963e97..4f485622ff4 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -26,6 +26,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include #include +#include #include #include @@ -34,6 +35,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include "android.h" #include "systime.h" +#include "blockinput.h" #if __ANDROID_API__ >= 9 #include @@ -278,6 +280,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. /* Global references to several exception classes. */ static jclass file_not_found_exception, security_exception; +static jclass operation_canceled_exception; static jclass unsupported_operation_exception, out_of_memory_error; /* Initialize `cursor_class' using the given JNI environment ENV. @@ -3692,6 +3695,10 @@ android_saf_root_get_directory (int dirfd) /* Functions common to both SAF directory and file nodes. */ +/* Whether or not Emacs is within an operation running from the SAF + thread. */ +static bool inside_saf_critical_section; + /* Check for JNI exceptions, clear them, and set errno accordingly. Also, free each of the N local references given as arguments if an exception takes place. @@ -3704,6 +3711,9 @@ android_saf_root_get_directory (int dirfd) If the exception thrown derives from SecurityException, set errno to EACCES. + If the exception thrown derives from OperationCanceledException, + set errno to EINTR. + If the exception thrown derives from UnsupportedOperationException, set errno to ENOSYS. @@ -3754,6 +3764,9 @@ android_saf_exception_check (int n, ...) else if ((*env)->IsInstanceOf (env, (jobject) exception, security_exception)) errno = EACCES; + else if ((*env)->IsInstanceOf (env, (jobject) exception, + operation_canceled_exception)) + errno = EINTR; else if ((*env)->IsInstanceOf (env, (jobject) exception, unsupported_operation_exception)) errno = ENOSYS; @@ -3786,6 +3799,15 @@ android_saf_stat (const char *uri_name, const char *id_name, jobject status; jlong mode, size, mtim, *longs; + /* Now guarantee that it is safe to call functions which + synchronize with the SAF thread. */ + + if (inside_saf_critical_section) + { + errno = EIO; + return -1; + } + /* Build strings for both URI and ID. */ uri = (*android_java_env)->NewStringUTF (android_java_env, uri_name); android_exception_check (); @@ -3801,10 +3823,12 @@ android_saf_stat (const char *uri_name, const char *id_name, /* Try to retrieve the file status. */ method = service_class.stat_document; + inside_saf_critical_section = true; status = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, emacs_service, service_class.class, method, uri, id); + inside_saf_critical_section = false; /* Check for exceptions and release unneeded local references. */ @@ -3870,6 +3894,15 @@ android_saf_access (const char *uri_name, const char *id_name, jstring uri, id; jint rc; + /* Now guarantee that it is safe to call functions which + synchronize with the SAF thread. */ + + if (inside_saf_critical_section) + { + errno = EIO; + return -1; + } + /* Build strings for both URI and ID. */ uri = (*android_java_env)->NewStringUTF (android_java_env, uri_name); android_exception_check (); @@ -3885,11 +3918,13 @@ android_saf_access (const char *uri_name, const char *id_name, /* Try to retrieve the file status. */ method = service_class.access_document; + inside_saf_critical_section = true; rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env, emacs_service, service_class.class, method, uri, id, (jboolean) writable); + inside_saf_critical_section = false; /* Check for exceptions and release unneeded local references. */ @@ -4161,7 +4196,19 @@ android_document_id_from_name (const char *tree_uri, char *name, contain characters that can't be encoded in Java. */ if (android_verify_jni_string (name)) - return -1; + { + errno = ENOENT; + return -1; + } + + /* Now guarantee that it is safe to call + `document_id_from_name'. */ + + if (inside_saf_critical_section) + { + errno = EIO; + return -1; + } /* First, create the array that will hold the result. */ result = (*android_java_env)->NewObjectArray (android_java_env, 1, @@ -4176,14 +4223,17 @@ android_document_id_from_name (const char *tree_uri, char *name, uri = (*android_java_env)->NewStringUTF (android_java_env, tree_uri); android_exception_check_2 (result, java_name); - /* Now, call documentIdFromName. */ + /* Now, call documentIdFromName. This will synchronize with the SAF + thread, so make sure reentrant calls don't happen. */ method = service_class.document_id_from_name; + inside_saf_critical_section = true; rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env, emacs_service, service_class.class, method, uri, java_name, result); + inside_saf_critical_section = false; if (android_saf_exception_check (3, result, uri, java_name)) goto finish; @@ -4562,6 +4612,12 @@ android_saf_tree_opendir_1 (struct android_saf_tree_vnode *vp) jobject uri, id, cursor; jmethodID method; + if (inside_saf_critical_section) + { + errno = EIO; + return NULL; + } + /* Build strings for both URI and ID. */ uri = (*android_java_env)->NewStringUTF (android_java_env, vp->tree_uri); @@ -4578,11 +4634,13 @@ android_saf_tree_opendir_1 (struct android_saf_tree_vnode *vp) /* Try to open the cursor. */ method = service_class.open_document_directory; + inside_saf_critical_section = true; cursor = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, emacs_service, service_class.class, method, uri, id); + inside_saf_critical_section = false; if (id) { @@ -5001,6 +5059,12 @@ android_saf_file_open (struct android_vnode *vnode, int flags, struct android_parcel_fd *info; struct stat statb; + if (inside_saf_critical_section) + { + errno = EIO; + return -1; + } + /* Build strings for both the URI and ID. */ vp = (struct android_saf_file_vnode *) vnode; @@ -5016,12 +5080,14 @@ android_saf_file_open (struct android_vnode *vnode, int flags, method = service_class.open_document; trunc = flags & O_TRUNC; write = ((flags & O_RDWR) == O_RDWR || (flags & O_WRONLY)); + inside_saf_critical_section = true; descriptor = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, emacs_service, service_class.class, method, uri, id, write, trunc); + inside_saf_critical_section = false; if (android_saf_exception_check (2, uri, id)) return -1; @@ -5468,6 +5534,48 @@ android_saf_new_opendir (struct android_vnode *vnode) +/* Synchronization between SAF and Emacs. Consult EmacsSafThread.java + for more details. */ + +/* Semaphore posted upon the completion of an SAF operation. */ +static sem_t saf_completion_sem; + +JNIEXPORT jint JNICALL +NATIVE_NAME (safSyncAndReadInput) (JNIEnv *env, jobject object) +{ + while (sem_wait (&saf_completion_sem) < 0) + { + if (input_blocked_p ()) + continue; + + process_pending_signals (); + + if (!NILP (Vquit_flag)) + { + __android_log_print (ANDROID_LOG_VERBOSE, __func__, + "quitting from IO operation"); + return 1; + } + } + + return 0; +} + +JNIEXPORT void JNICALL +NATIVE_NAME (safSync) (JNIEnv *env, jobject object) +{ + while (sem_wait (&saf_completion_sem) < 0) + process_pending_signals (); +} + +JNIEXPORT void JNICALL +NATIVE_NAME (safPostRequest) (JNIEnv *env, jobject object) +{ + sem_post (&saf_completion_sem); +} + + + /* Root vnode. This vnode represents the root inode, and is a regular Unix vnode with modifications to `name' that make it return asset vnodes. */ @@ -5692,6 +5800,11 @@ android_vfs_init (JNIEnv *env, jobject manager) (*env)->DeleteLocalRef (env, old); eassert (security_exception); + old = (*env)->FindClass (env, "android/os/OperationCanceledException"); + operation_canceled_exception = (*env)->NewGlobalRef (env, old); + (*env)->DeleteLocalRef (env, old); + eassert (operation_canceled_exception); + old = (*env)->FindClass (env, "java/lang/UnsupportedOperationException"); unsupported_operation_exception = (*env)->NewGlobalRef (env, old); (*env)->DeleteLocalRef (env, old); @@ -5701,6 +5814,12 @@ android_vfs_init (JNIEnv *env, jobject manager) out_of_memory_error = (*env)->NewGlobalRef (env, old); (*env)->DeleteLocalRef (env, old); eassert (out_of_memory_error); + + /* Initialize the semaphore used to wait for SAF operations to + complete. */ + + if (sem_init (&saf_completion_sem, 0, 0) < 0) + emacs_abort (); } /* The replacement functions that follow have several major @@ -5754,6 +5873,12 @@ android_vfs_init (JNIEnv *env, jobject manager) The sixth is that flags and other argument checking is nowhere near exhaustive on vnode types other than Unix vnodes. + The seventh is that certain vnode types may read async input and + return EINTR not upon the arrival of a signal itself, but instead + if subsequently read input causes Vquit_flag to be set. These + vnodes may not be reentrant, but operating on them from within an + async input handler will at worst cause an error to be returned. + And the final drawback is that directories cannot be directly opened. Instead, `dirfd' must be called on a directory stream used by `openat'. @@ -6409,7 +6534,8 @@ android_asset_fstat (struct android_fd_or_asset asset, /* Directory listing emulation. */ /* Open a directory stream from the VFS node designated by NAME. - Value is NULL upon failure with errno set accordingly. + Value is NULL upon failure with errno set accordingly. `errno' may + be set to EINTR. The directory stream returned holds local references to JNI objects and shouldn't be used after the current local reference frame is diff --git a/src/dired.c b/src/dired.c index f2a123dc168..c10531cdb16 100644 --- a/src/dired.c +++ b/src/dired.c @@ -115,10 +115,19 @@ open_directory (Lisp_Object dirname, Lisp_Object encoded_dirname, int *fdp) #ifndef HAVE_ANDROID d = opendir (name); #else + /* `android_opendir' can return EINTR if DIRNAME designates a file + within a slow-to-respond document provider. */ + + again: d = android_opendir (name); if (d) fd = android_dirfd (d); + else if (errno == EINTR) + { + maybe_quit (); + goto again; + } #endif opendir_errno = errno; #else diff --git a/src/sysdep.c b/src/sysdep.c index 88938d15b91..0a1905c9196 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -2656,7 +2656,7 @@ emacs_fclose (FILE *stream) /* Wrappers around unlink, symlink, rename, renameat_noreplace, and rmdir. These operations handle asset and content directories on - Android. */ + Android, and may return EINTR. */ int emacs_unlink (const char *name) commit 03cf3bbb5c38aa55abd6f7d4860025f7482fcfc3 Author: Po Lu Date: Fri Jul 28 12:21:47 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsDirectoryEntry.java (EmacsDirectoryEntry): Make class final. * java/org/gnu/emacs/EmacsService.java (accessDocument) (openDocumentDirectory, openDocument, createDocument): Throw access and IO error exceptions instead of catching them. (createDirectory, deleteDocument): New functions. * src/android.c (android_init_emacs_service): Add new functions. * src/android.h (struct android_emacs_service): Likewise. * src/androidvfs.c (android_saf_exception_check): New function. Translate between Java exceptions and errno values. (android_saf_stat, android_saf_access, android_saf_delete_document) (struct android_saf_tree_vnode, android_document_id_from_name) (android_saf_tree_name, android_saf_tree_rmdir) (android_saf_tree_opendir_1, android_saf_tree_opendir) (android_saf_file_open, android_saf_file_unlink) (android_saf_new_open, android_saf_new_mkdir): Implement missing VFS operations and derive errno values from the type of any exceptions thrown. (android_vfs_init): Initialize exception classes. (android_mkdir, android_fstat): Remove trailing whitespace. diff --git a/java/org/gnu/emacs/EmacsDirectoryEntry.java b/java/org/gnu/emacs/EmacsDirectoryEntry.java index 9c10f2e8771..75c52e48002 100644 --- a/java/org/gnu/emacs/EmacsDirectoryEntry.java +++ b/java/org/gnu/emacs/EmacsDirectoryEntry.java @@ -22,7 +22,7 @@ /* Structure holding a single ``directory entry'' from a document provider. */ -public class EmacsDirectoryEntry +public final class EmacsDirectoryEntry { /* The type of this directory entry. 0 means a regular file and 1 means a directory. */ diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index bc62e050345..aa672994f12 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1723,9 +1723,12 @@ type is either NULL (in which case id should also be NULL) or not: -1, if the file does not exist. - -2, upon a security exception or if WRITABLE the file - is not writable. - -3, upon any other error. */ + -2, if WRITABLE and the file is not writable. + -3, upon any other error. + + In addition, arbitrary runtime exceptions (such as + SecurityException or UnsupportedOperationException) may be + thrown. */ public int accessDocument (String uri, String documentId, boolean writable) @@ -1754,24 +1757,8 @@ type is either NULL (in which case id should also be NULL) or Document.COLUMN_MIME_TYPE, }; - try - { - cursor = resolver.query (uriObject, projection, null, - null, null); - } - catch (SecurityException exception) - { - /* A SecurityException can be thrown if Emacs doesn't have - access to uriObject. */ - return -2; - } - catch (UnsupportedOperationException exception) - { - exception.printStackTrace (); - - /* Why is this? */ - return -3; - } + cursor = resolver.query (uriObject, projection, null, + null, null); if (cursor == null || !cursor.moveToFirst ()) return -1; @@ -1816,13 +1803,10 @@ type is either NULL (in which case id should also be NULL) or if (writable && (tem & Document.FLAG_SUPPORTS_WRITE) == 0) return -3; } - catch (Exception exception) + finally { - /* Whether or not type errors cause exceptions to be signaled - is defined ``by the implementation of Cursor'', whatever - that means. */ - exception.printStackTrace (); - return -3; + /* Close the cursor if an exception occurs. */ + cursor.close (); } return 0; @@ -1832,7 +1816,11 @@ type is either NULL (in which case id should also be NULL) or designated by the specified DOCUMENTID within the tree URI. If DOCUMENTID is NULL, use the document ID within URI itself. - Value is NULL upon failure. */ + Value is NULL upon failure. + + In addition, arbitrary runtime exceptions (such as + SecurityException or UnsupportedOperationException) may be + thrown. */ public Cursor openDocumentDirectory (String uri, String documentId) @@ -1861,25 +1849,8 @@ type is either NULL (in which case id should also be NULL) or Document.COLUMN_MIME_TYPE, }; - try - { - cursor = resolver.query (uriObject, projection, null, null, - null); - } - catch (SecurityException exception) - { - /* A SecurityException can be thrown if Emacs doesn't have - access to uriObject. */ - return null; - } - catch (UnsupportedOperationException exception) - { - exception.printStackTrace (); - - /* Why is this? */ - return null; - } - + cursor = resolver.query (uriObject, projection, null, null, + null); /* Return the cursor. */ return cursor; } @@ -1966,11 +1937,15 @@ type is either NULL (in which case id should also be NULL) or Value is NULL upon failure or a parcel file descriptor upon success. Call `ParcelFileDescriptor.close' on this file - descriptor instead of using the `close' system call. */ + descriptor instead of using the `close' system call. + + FileNotFoundException and/or SecurityException and + UnsupportedOperationException may be thrown upon failure. */ public ParcelFileDescriptor openDocument (String uri, String documentId, boolean write, boolean truncate) + throws FileNotFoundException { Uri treeUri, documentUri; String mode; @@ -1984,40 +1959,33 @@ type is either NULL (in which case id should also be NULL) or documentUri = DocumentsContract.buildDocumentUriUsingTree (treeUri, documentId); - try + if (write || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - if (write || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) - { - /* Select the mode used to open the file. `rw' means open - a stat-able file, while `rwt' means that and to - truncate the file as well. */ - - if (truncate) - mode = "rwt"; - else - mode = "rw"; - - fileDescriptor - = resolver.openFileDescriptor (documentUri, mode, - null); - } - else - { - /* Select the mode used to open the file. `openFile' - below means always open a stat-able file. */ + /* Select the mode used to open the file. `rw' means open + a stat-able file, while `rwt' means that and to + truncate the file as well. */ - if (truncate) - /* Invalid mode! */ - return null; - else - mode = "r"; + if (truncate) + mode = "rwt"; + else + mode = "rw"; - fileDescriptor = resolver.openFile (documentUri, mode, null); - } + fileDescriptor + = resolver.openFileDescriptor (documentUri, mode, + null); } - catch (Exception exception) + else { - return null; + /* Select the mode used to open the file. `openFile' + below means always open a stat-able file. */ + + if (truncate) + /* Invalid mode! */ + return null; + else + mode = "r"; + + fileDescriptor = resolver.openFile (documentUri, mode, null); } return fileDescriptor; @@ -2030,11 +1998,15 @@ type is either NULL (in which case id should also be NULL) or If DOCUMENTID is NULL, create the document inside the root of that tree. + Either FileNotFoundException, SecurityException or + UnsupportedOperationException may be thrown upon failure. + Return the document ID of the new file upon success, NULL otherwise. */ public String createDocument (String uri, String documentId, String name) + throws FileNotFoundException { String mimeType, separator, mime, extension; int index; @@ -2072,23 +2044,77 @@ type is either NULL (in which case id should also be NULL) or = DocumentsContract.buildChildDocumentsUriUsingTree (directoryUri, documentId); - try - { - docUri = DocumentsContract.createDocument (resolver, - directoryUri, - mimeType, name); + docUri = DocumentsContract.createDocument (resolver, + directoryUri, + mimeType, name); - if (docUri == null) - return null; + if (docUri == null) + return null; - /* Return the ID of the new document. */ - return DocumentsContract.getDocumentId (docUri); - } - catch (Exception exception) - { - exception.printStackTrace (); - } + /* Return the ID of the new document. */ + return DocumentsContract.getDocumentId (docUri); + } - return null; + /* Like `createDocument', but create a directory instead of an + ordinary document. */ + + public String + createDirectory (String uri, String documentId, String name) + throws FileNotFoundException + { + int index; + Uri directoryUri, docUri; + + /* Now parse URI. */ + directoryUri = Uri.parse (uri); + + if (documentId == null) + documentId = DocumentsContract.getTreeDocumentId (directoryUri); + + /* And build a file URI referring to the directory. */ + + directoryUri + = DocumentsContract.buildChildDocumentsUriUsingTree (directoryUri, + documentId); + + /* If name ends with a directory separator character, delete + it. */ + + if (name.endsWith ("/")) + name = name.substring (0, name.length () - 1); + + /* From Android's perspective, directories are just ordinary + documents with the `MIME_TYPE_DIR' type. */ + + docUri = DocumentsContract.createDocument (resolver, + directoryUri, + Document.MIME_TYPE_DIR, + name); + + if (docUri == null) + return null; + + /* Return the ID of the new document. */ + return DocumentsContract.getDocumentId (docUri); + } + + /* Delete the document identified by ID from the document tree + identified by URI. Return 0 upon success and -1 upon + failure. */ + + public int + deleteDocument (String uri, String id) + throws FileNotFoundException + { + Uri uriObject; + + uriObject = Uri.parse (uri); + uriObject = DocumentsContract.buildDocumentUriUsingTree (uriObject, + id); + + if (DocumentsContract.deleteDocument (resolver, uriObject)) + return 0; + + return -1; } }; diff --git a/src/android.c b/src/android.c index 098fa6c383d..687c0b48a2a 100644 --- a/src/android.c +++ b/src/android.c @@ -1577,6 +1577,11 @@ #define FIND_METHOD(c_name, name, signature) \ FIND_METHOD (create_document, "createDocument", "(Ljava/lang/String;Ljava/lang/String;" "Ljava/lang/String;)Ljava/lang/String;"); + FIND_METHOD (create_directory, "createDirectory", + "(Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;)Ljava/lang/String;"); + FIND_METHOD (delete_document, "deleteDocument", + "(Ljava/lang/String;Ljava/lang/String;)I"); #undef FIND_METHOD } diff --git a/src/android.h b/src/android.h index 94a3ad46e74..fd391fa6435 100644 --- a/src/android.h +++ b/src/android.h @@ -279,6 +279,8 @@ #define android_is_special_directory(name, dir) ((const char *) NULL) jmethodID read_directory_entry; jmethodID open_document; jmethodID create_document; + jmethodID create_directory; + jmethodID delete_document; }; extern JNIEnv *android_java_env; diff --git a/src/androidvfs.c b/src/androidvfs.c index c174c35f02b..2cd50963e97 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -276,6 +276,10 @@ Copyright (C) 2023 Free Software Foundation, Inc. class. */ static struct android_parcel_file_descriptor_class fd_class; +/* Global references to several exception classes. */ +static jclass file_not_found_exception, security_exception; +static jclass unsupported_operation_exception, out_of_memory_error; + /* Initialize `cursor_class' using the given JNI environment ENV. Calling this function is not necessary on Android 4.4 and earlier. */ @@ -3688,6 +3692,85 @@ android_saf_root_get_directory (int dirfd) /* Functions common to both SAF directory and file nodes. */ +/* Check for JNI exceptions, clear them, and set errno accordingly. + Also, free each of the N local references given as arguments if an + exception takes place. + + Value is 1 if an exception has taken place, 0 otherwise. + + If the exception thrown derives from FileNotFoundException, set + errno to ENOENT. + + If the exception thrown derives from SecurityException, set errno + to EACCES. + + If the exception thrown derives from UnsupportedOperationException, + set errno to ENOSYS. + + If the exception thrown derives from OutOfMemoryException, call + `memory_full'. + + If the exception thrown is anything else, set errno to EIO. */ + +static int +android_saf_exception_check (int n, ...) +{ + jthrowable exception; + JNIEnv *env; + va_list ap; + + env = android_java_env; + va_start (ap, n); + + /* First, check for an exception. */ + + if (!(*env)->ExceptionCheck (env)) + /* No exception has taken place. Return 0. */ + return 0; + + exception = (*env)->ExceptionOccurred (env); + + if (!exception) + /* JNI couldn't return a local reference to the exception. */ + memory_full (0); + + /* Clear the exception, making it safe to subsequently call other + JNI functions. */ + (*env)->ExceptionClear (env); + + /* Delete each of the N arguments. */ + + while (n > 0) + { + ANDROID_DELETE_LOCAL_REF (va_arg (ap, jobject)); + n--; + } + + /* Now set errno or signal memory_full as required. */ + + if ((*env)->IsInstanceOf (env, (jobject) exception, + file_not_found_exception)) + errno = ENOENT; + else if ((*env)->IsInstanceOf (env, (jobject) exception, + security_exception)) + errno = EACCES; + else if ((*env)->IsInstanceOf (env, (jobject) exception, + unsupported_operation_exception)) + errno = ENOSYS; + else if ((*env)->IsInstanceOf (env, (jobject) exception, + out_of_memory_error)) + { + ANDROID_DELETE_LOCAL_REF ((jobject) exception); + memory_full (0); + } + else + errno = EIO; + + /* expression is still a local reference! */ + ANDROID_DELETE_LOCAL_REF ((jobject) exception); + return 1; +} + /* Return file status for the document designated by ID_NAME within the document tree identified by URI_NAME. @@ -3727,11 +3810,13 @@ android_saf_stat (const char *uri_name, const char *id_name, if (id) { - android_exception_check_2 (uri, id); + if (android_saf_exception_check (2, uri, id)) + return -1; + ANDROID_DELETE_LOCAL_REF (id); } - else - android_exception_check_1 (uri); + else if (android_saf_exception_check (1, uri)) + return -1; ANDROID_DELETE_LOCAL_REF (uri); @@ -3810,11 +3895,13 @@ android_saf_access (const char *uri_name, const char *id_name, if (id) { - android_exception_check_2 (uri, id); + if (android_saf_exception_check (2, uri, id)) + return -1; + ANDROID_DELETE_LOCAL_REF (id); } - else - android_exception_check_1 (uri); + else if (android_saf_exception_check (1, uri)) + return -1; ANDROID_DELETE_LOCAL_REF (uri); @@ -3840,6 +3927,46 @@ android_saf_access (const char *uri_name, const char *id_name, return 0; } +/* Delete the document designated by DOC_ID within the tree identified + through the URI TREE. Return 0 if the document has been deleted, + set errno and return -1 upon failure. */ + +static int +android_saf_delete_document (const char *tree, const char *doc_id) +{ + jobject id, uri; + jmethodID method; + jint rc; + + /* Build the strings holding the ID and URI. */ + id = (*android_java_env)->NewStringUTF (android_java_env, + doc_id); + android_exception_check (); + uri = (*android_java_env)->NewStringUTF (android_java_env, + tree); + android_exception_check_1 (id); + + /* Now, try to delete the document. */ + method = service_class.delete_document; + rc = (*android_java_env)->CallIntMethod (android_java_env, + emacs_service, + method, uri, id); + + if (android_saf_exception_check (2, id, uri)) + return -1; + + ANDROID_DELETE_LOCAL_REF (id); + ANDROID_DELETE_LOCAL_REF (uri); + + if (rc) + { + errno = EACCES; + return -1; + } + + return 0; +} + /* SAF directory vnode. A file within a SAF directory tree is @@ -3863,7 +3990,9 @@ android_saf_access (const char *uri_name, const char *id_name, char *tree_id; /* The document ID of the directory represented, or NULL if this is - the root directory of the tree. */ + the root directory of the tree. Since file and new vnodes don't + represent the root directory, this field is always set in + them. */ char *document_id; /* The file name of this tree vnode. This is a ``path'' to the @@ -4004,7 +4133,7 @@ android_verify_jni_string (const char *name) must name a directory file within TREE_URI. If NAME is not correct for the Java ``modified UTF-8'' coding - system, return -1. + system, return -1 and set errno to ENOENT. Upon success, return 0 or 1 (contingent upon whether or not the last component within NAME is a directory) and place the document @@ -4014,7 +4143,8 @@ android_verify_jni_string (const char *name) within NAME does and is also a directory, return -2 and place the document ID of that directory within *ID. - If the designated file can't be located, return -1. */ + If the designated file can't be located, return -1 and set errno + accordingly. */ static int android_document_id_from_name (const char *tree_uri, char *name, @@ -4023,7 +4153,6 @@ android_document_id_from_name (const char *tree_uri, char *name, jobjectArray result; jstring uri; jbyteArray java_name; - size_t length; jint rc; jmethodID method; const char *doc_id; @@ -4055,7 +4184,10 @@ android_document_id_from_name (const char *tree_uri, char *name, method, uri, java_name, result); - android_exception_check_3 (result, uri, java_name); + + if (android_saf_exception_check (3, result, uri, java_name)) + goto finish; + ANDROID_DELETE_LOCAL_REF (uri); ANDROID_DELETE_LOCAL_REF (java_name); @@ -4178,7 +4310,6 @@ android_saf_tree_name (struct android_vnode *vnode, char *name, /* The document ID can't be found. */ xfree (filename); - errno = ENOENT; return NULL; } @@ -4354,9 +4485,19 @@ android_saf_tree_symlink (const char *target, struct android_vnode *vnode) static int android_saf_tree_rmdir (struct android_vnode *vnode) { - /* TODO */ - errno = ENOSYS; - return -1; + struct android_saf_tree_vnode *vp; + + vp = (struct android_saf_tree_vnode *) vnode; + + /* Don't allow deleting the root directory. */ + + if (!vp->document_id) + { + errno = EROFS; + return -1; + } + + return android_saf_delete_document (vp->tree_uri, vp->document_id); } static int @@ -4412,8 +4553,8 @@ android_saf_tree_mkdir (struct android_vnode *vnode, mode_t mode) /* Open a database Cursor containing each directory entry within the supplied SAF tree vnode VP. - Value is NULL upon failure, a local reference to the Cursor object - otherwise. */ + Value is NULL upon failure with errno set to a suitable value, a + local reference to the Cursor object otherwise. */ static jobject android_saf_tree_opendir_1 (struct android_saf_tree_vnode *vp) @@ -4445,11 +4586,13 @@ android_saf_tree_opendir_1 (struct android_saf_tree_vnode *vp) if (id) { - android_exception_check_2 (id, uri); + if (android_saf_exception_check (2, id, uri)) + return NULL; + ANDROID_DELETE_LOCAL_REF (id); } - else - android_exception_check_1 (uri); + else if (android_saf_exception_check (1, uri)) + return NULL; ANDROID_DELETE_LOCAL_REF (uri); @@ -4657,7 +4800,6 @@ android_saf_tree_opendir (struct android_vnode *vnode) { xfree (dir); xfree (dir->name); - errno = ENOENT; return NULL; } @@ -4880,7 +5022,10 @@ android_saf_file_open (struct android_vnode *vnode, int flags, service_class.class, method, uri, id, write, trunc); - android_exception_check_2 (uri, id); + + if (android_saf_exception_check (2, uri, id)) + return -1; + ANDROID_DELETE_LOCAL_REF (uri); ANDROID_DELETE_LOCAL_REF (id); @@ -4953,9 +5098,10 @@ android_saf_file_open (struct android_vnode *vnode, int flags, static int android_saf_file_unlink (struct android_vnode *vnode) { - /* TODO */ - errno = ENOSYS; - return -1; + struct android_saf_file_vnode *vp; + + vp = (struct android_saf_file_vnode *) vnode; + return android_saf_delete_document (vp->tree_uri, vp->document_id); } static int @@ -5114,7 +5260,7 @@ android_saf_new_open (struct android_vnode *vnode, int flags, { errno = ENOENT; return -1; - } + } /* Otherwise, try to create a new document. First, build strings for the name, ID and document URI. */ @@ -5137,7 +5283,9 @@ android_saf_new_open (struct android_vnode *vnode, int flags, service_class.class, method, uri, id, name); - android_exception_check_3 (name, id, uri); + + if (android_saf_exception_check (3, name, id, uri)) + return -1; /* Delete unused local references. */ ANDROID_DELETE_LOCAL_REF (name); @@ -5225,9 +5373,90 @@ android_saf_new_access (struct android_vnode *vnode, int mode) static int android_saf_new_mkdir (struct android_vnode *vnode, mode_t mode) { - /* TODO */ - errno = ENOSYS; - return -1; + struct android_saf_new_vnode *vp; + jstring name, id, uri, new_id; + jmethodID method; + const char *new_doc_id; + char *end; + + vp = (struct android_saf_tree_vnode *) vnode; + + /* Find the last component of vp->name. */ + end = strrchr (vp->name, '/'); + + /* VP->name must contain at least one directory separator. */ + eassert (end); + + if (end[1] == '\0') + { + /* There's a trailing directory separator. Search + backwards. */ + + end--; + while (end != vp->name && *end != '/') + end--; + + /* vp->name[0] is always a directory separator. */ + eassert (*end == '/'); + } + + /* Otherwise, try to create a new document. First, build strings + for the name, ID and document URI. */ + + name = (*android_java_env)->NewStringUTF (android_java_env, + end + 1); + android_exception_check (); + id = (*android_java_env)->NewStringUTF (android_java_env, + vp->document_id); + android_exception_check_1 (name); + uri = (*android_java_env)->NewStringUTF (android_java_env, + vp->tree_uri); + android_exception_check_2 (name, id); + + /* Next, try to create a new document and retrieve its ID. */ + + method = service_class.create_directory; + new_id = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, + emacs_service, + service_class.class, + method, uri, id, + name); + + if (android_saf_exception_check (3, name, id, uri)) + return -1; + + /* Delete unused local references. */ + ANDROID_DELETE_LOCAL_REF (name); + ANDROID_DELETE_LOCAL_REF (id); + ANDROID_DELETE_LOCAL_REF (uri); + + if (!new_id) + { + /* The file couldn't be created for some reason. */ + errno = EIO; + return -1; + } + + /* Now, free VP->document_id and replace it with the service + document ID. */ + + new_doc_id = (*android_java_env)->GetStringUTFChars (android_java_env, + new_id, NULL); + + if (android_saf_exception_check (3, name, id, uri)) + return -1; + + xfree (vp->document_id); + vp->document_id = xstrdup (new_doc_id); + + (*android_java_env)->ReleaseStringUTFChars (android_java_env, + new_id, new_doc_id); + ANDROID_DELETE_LOCAL_REF (new_id); + + /* Finally, transform this vnode into a directory vnode. */ + vp->vnode.type = ANDROID_VNODE_SAF_TREE; + vp->vnode.ops = &saf_tree_vfs_ops; + return 0; } static struct android_vdir * @@ -5449,6 +5678,29 @@ android_vfs_init (JNIEnv *env, jobject manager) android_init_cursor_class (env); android_init_entry_class (env); android_init_fd_class (env); + + /* Initialize each of the exception classes used by + `android_saf_exception_check'. */ + + old = (*env)->FindClass (env, "java/io/FileNotFoundException"); + file_not_found_exception = (*env)->NewGlobalRef (env, old); + (*env)->DeleteLocalRef (env, old); + eassert (file_not_found_exception); + + old = (*env)->FindClass (env, "java/lang/SecurityException"); + security_exception = (*env)->NewGlobalRef (env, old); + (*env)->DeleteLocalRef (env, old); + eassert (security_exception); + + old = (*env)->FindClass (env, "java/lang/UnsupportedOperationException"); + unsupported_operation_exception = (*env)->NewGlobalRef (env, old); + (*env)->DeleteLocalRef (env, old); + eassert (unsupported_operation_exception); + + old = (*env)->FindClass (env, "java/lang/OutOfMemoryError"); + out_of_memory_error = (*env)->NewGlobalRef (env, old); + (*env)->DeleteLocalRef (env, old); + eassert (out_of_memory_error); } /* The replacement functions that follow have several major @@ -5606,7 +5858,7 @@ android_mkdir (const char *name, mode_t mode) rc = (*vp->ops->mkdir) (vp, mode); (*vp->ops->close) (vp); - return rc; + return rc; } /* Rename the vnode designated by SRC to the vnode designated by DST. @@ -5724,7 +5976,7 @@ android_fstat (int fd, struct stat *statb) for (tem = afs_file_descriptors; tem; tem = tem->next) { if (tem->fd == fd) - { + { memcpy (statb, &tem->statb, sizeof *statb); return 0; } commit 7c0899586471d3649dfb468d2b8f7d6d9685fea1 Merge: de0e0939f01 3443574a66d Author: Po Lu Date: Fri Jul 28 08:22:01 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit de0e0939f01a747b8201e06bda5cd50dfa95187f Author: Po Lu Date: Thu Jul 27 21:59:58 2023 +0800 Update Android port * doc/emacs/android.texi (Android Document Providers): Improve wording of paragraph clarifying limits on subprocesses. * java/org/gnu/emacs/EmacsService.java (getDocumentTrees): Use Java standard US-ASCII coding standard instead of the undocumented ``ASCII'' alias. (decodeFileName): Remove unused function. (documentIdFromName): * src/android.c (android_init_emacs_service): Take a String for NAME instead of a byte array. * src/androidvfs.c (android_verify_jni_string): New function. (android_document_id_from_name): Verify that STRING is a valid Modified UTF-8 string. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 1f32fdfc1d2..b86c71cea49 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -298,11 +298,12 @@ Android Document Providers a unique identifier assigned to the directory by the document provider. - Because these directories do not exist within the Unix file-system, -sub-processes cannot be created within them, just as with the -@file{/assets} directory (@pxref{Android File System}.) In addition, -although Emacs can normally write and create files inside these -directories, it cannot create symlinks or hard links. + The same limitations applied to the @file{/assets} directory +(@pxref{Android File System}) are applied when creating sub-processes +within those directories, because they do not exist within the Unix +file-system. In addition, although Emacs can normally write and +create files inside these directories, it cannot create symlinks or +hard links. @c TODO: fix this! Since document providers are allowed to perform expensive network diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 6059439551f..bc62e050345 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1282,7 +1282,7 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) try { - providerName = new String (provider, "ASCII"); + providerName = new String (provider, "US-ASCII"); } catch (UnsupportedEncodingException exception) { @@ -1306,24 +1306,6 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) return treeList.toArray (new String[0]); } - /* Decode the specified STRING into a String object using the UTF-8 - format. If an exception is thrown, return null. */ - - private String - decodeFileName (byte[] string) - { - try - { - return new String (string, "UTF-8"); - } - catch (Exception e) /* UnsupportedEncodingException, etc. */ - { - ;; - } - - return null; - } - /* Find the document ID of the file within TREE_URI designated by NAME. @@ -1342,11 +1324,10 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) If the designated file can't be located, return -1. */ private int - documentIdFromName (String tree_uri, byte name[], - String[] id_return) + documentIdFromName (String tree_uri, String name, String[] id_return) { Uri uri, treeUri; - String nameString, id, type; + String id, type; String[] components, projection; Cursor cursor; int column; @@ -1360,11 +1341,8 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) /* Parse the URI identifying the tree first. */ uri = Uri.parse (tree_uri); - /* Next, decode NAME. */ - nameString = decodeFileName (name); - /* Now, split NAME into its individual components. */ - components = nameString.split ("/"); + components = name.split ("/"); /* Set id and type to the value at the root of the tree. */ type = id = null; @@ -1462,7 +1440,7 @@ and the current document ID (belonging to the try { - nameString = cursor.getString (column); + name = cursor.getString (column); } catch (Exception exception) { @@ -1473,7 +1451,7 @@ and the current document ID (belonging to the /* Break out of the loop only once a matching component is found. */ - if (nameString.equals (component)) + if (name.equals (component)) break; } diff --git a/src/android.c b/src/android.c index d8b264a8491..098fa6c383d 100644 --- a/src/android.c +++ b/src/android.c @@ -1556,7 +1556,8 @@ #define FIND_METHOD(c_name, name, signature) \ FIND_METHOD (get_document_trees, "getDocumentTrees", "([B)[Ljava/lang/String;"); FIND_METHOD (document_id_from_name, "documentIdFromName", - "(Ljava/lang/String;[B[Ljava/lang/String;)I"); + "(Ljava/lang/String;Ljava/lang/String;" + "[Ljava/lang/String;)I"); FIND_METHOD (get_tree_uri, "getTreeUri", "(Ljava/lang/String;Ljava/lang/String;)" "Ljava/lang/String;"); diff --git a/src/androidvfs.c b/src/androidvfs.c index bab3977ed5a..c174c35f02b 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -3936,6 +3936,66 @@ android_saf_access (const char *uri_name, const char *id_name, /* Chain of all open SAF directory streams. */ static struct android_saf_tree_vdir *all_saf_tree_vdirs; +/* Verify that the specified NULL-terminated STRING is a valid JNI + ``UTF-8'' string. Return 0 if so, 1 otherwise. + + The native coding system used by the JVM to store strings derives + from UTF-8, but deviates from it in two aspects in an attempt to + better represent the UCS-16 based Java String format, and to let + strings contain NULL characters while remaining valid C strings: + NULL bytes are encoded as two-byte sequences, and Unicode surrogate + pairs encoded as two-byte sequences are prefered to four-byte + sequences when encoding characters above the BMP. */ + +static int +android_verify_jni_string (const char *name) +{ + const unsigned char *chars; + + chars = (unsigned char *) name; + while (*chars) + { + /* Switch on the high 4 bits. */ + + switch (*chars++ >> 4) + { + case 0 ... 7: + /* The 8th bit is clean, so this is a regular C + character. */ + break; + + case 8 ... 0xb: + /* Invalid starting byte! */ + return 1; + + case 0xf: + /* The start of a four byte sequence. These aren't allowed + in Java. */ + return 1; + + case 0xe: + /* The start of a three byte sequence. Verify that its + continued. */ + + if ((*chars++ & 0xc0) != 0x80) + return 1; + + FALLTHROUGH; + + case 0xc ... 0xd: + /* The start of a two byte sequence. Verify that the + next byte exists and has its high bit set. */ + + if ((*chars++ & 0xc0) != 0x80) + return 1; + + break; + } + } + + return 0; +} + /* Find the document ID of the file within TREE_URI designated by NAME. @@ -3943,6 +4003,9 @@ android_saf_access (const char *uri_name, const char *id_name, individual files. Each constituent component prior to the last must name a directory file within TREE_URI. + If NAME is not correct for the Java ``modified UTF-8'' coding + system, return -1. + Upon success, return 0 or 1 (contingent upon whether or not the last component within NAME is a directory) and place the document ID of the named file in ID. @@ -3965,6 +4028,12 @@ android_document_id_from_name (const char *tree_uri, char *name, jmethodID method; const char *doc_id; + /* Verify the format of NAME. Don't allow creating files that + contain characters that can't be encoded in Java. */ + + if (android_verify_jni_string (name)) + return -1; + /* First, create the array that will hold the result. */ result = (*android_java_env)->NewObjectArray (android_java_env, 1, java_string_class, @@ -3972,11 +4041,9 @@ android_document_id_from_name (const char *tree_uri, char *name, android_exception_check (); /* Next, create the string for the tree URI and name. */ - length = strlen (name); - java_name = (*android_java_env)->NewByteArray (android_java_env, length); + java_name = (*android_java_env)->NewStringUTF (android_java_env, + name); android_exception_check_1 (result); - (*android_java_env)->SetByteArrayRegion (android_java_env, java_name, - 0, length, (jbyte *) name); uri = (*android_java_env)->NewStringUTF (android_java_env, tree_uri); android_exception_check_2 (result, java_name); commit 4e754817b56c80b1a8c9cf4a9ae811d8217347a4 Author: Po Lu Date: Thu Jul 27 21:12:29 2023 +0800 Update Android port * src/androidvfs.c (android_afs_initial): (android_content_get_directory_name): (android_saf_tree_name): (android_saf_tree_from_name): (android_vfs_init): Silence compiler warnings. diff --git a/src/androidvfs.c b/src/androidvfs.c index 42b1ff8770f..bab3977ed5a 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -1720,7 +1720,7 @@ android_afs_initial (char *name, size_t length) temp.vnode.type = ANDROID_VNODE_AFS; temp.vnode.flags = 0; temp.name_length = 1; - temp.name = "/"; + temp.name = (char *) "/"; /* Try to name this vnode. If NAME is empty, it will be duplicated instead. */ @@ -2667,7 +2667,7 @@ android_content_get_directory_name (int dirfd) for (dir = all_content_vdirs; dir; dir = dir->next) { if (dir->fd == dirfd && dirfd != -1) - return "/content"; + return (char *) "/content"; } return NULL; @@ -4221,7 +4221,7 @@ android_saf_tree_name (struct android_vnode *vnode, char *name, tree.vnode.type = ANDROID_VNODE_SAF_TREE; tree.vnode.flags = 0; tree.document_id = NULL; - tree.name = "/"; + tree.name = (char *) "/"; tree.tree_uri = vp->tree_uri; tree.tree_id = vp->tree_id; @@ -4668,7 +4668,7 @@ android_saf_tree_from_name (char *name, const char *tree, root.tree_uri = uri; root.tree_id = (char *) tree; root.document_id = NULL; - root.name = "/"; + root.name = (char *) "/"; vp = (*root.vnode.ops->name) (&root.vnode, name, strlen (name)); (*android_java_env)->ReleaseStringUTFChars (android_java_env, @@ -5362,7 +5362,7 @@ android_vfs_init (JNIEnv *env, jobject manager) root_vnode.vnode.type = ANDROID_VNODE_UNIX; root_vnode.vnode.flags = 0; root_vnode.name_length = 1; - root_vnode.name = "/"; + root_vnode.name = (char *) "/"; /* Initialize some required classes. */ java_string_class = (*env)->FindClass (env, "java/lang/String"); commit 46c603be5a81f175b8003d3b5b6c4e73695f1002 Author: Po Lu Date: Thu Jul 27 20:56:43 2023 +0800 Update Android port * src/android.c (android_run_in_emacs_thread): Behave more robustly if SIGIO arrives too late Emacs for Emacs to check for signals, but too early to preempt a long running syscall. diff --git a/src/android.c b/src/android.c index b1d7b75c129..d8b264a8491 100644 --- a/src/android.c +++ b/src/android.c @@ -6385,6 +6385,8 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) __atomic_store_n (&android_urgent_query, true, __ATOMIC_RELEASE); + kill_again: + /* And raise SIGIO. Now that the query is considered urgent, the main thread will reply while reading async input. @@ -6396,9 +6398,27 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) /* Wait for the query to complete. `android_urgent_query' is only cleared by either `android_select' or `android_check_query', so there's no need to worry about the - flag being cleared before the query is processed. */ - while (sem_wait (&android_query_sem) < 0) - ;; + flag being cleared before the query is processed. + + Send SIGIO again periodically until the query is answered, on + the off chance that SIGIO arrived too late to preempt a + system call, but too early for it to return EINTR. */ + + timeout.tv_sec = 4; + timeout.tv_nsec = 0; + timeout = timespec_add (current_timespec (), timeout); + + while (sem_timedwait (&android_query_sem, &timeout) < 0) + { + /* If waiting timed out, send SIGIO to the main thread + again. */ + + if (errno == ETIMEDOUT) + goto kill_again; + + /* Otherwise, continue waiting. */ + eassert (errno == EINTR); + } } /* At this point, `android_servicing_query' should either be zero if commit 24af8af62c06cef59d2c82799f83da95643ef960 Author: Po Lu Date: Thu Jul 27 20:32:16 2023 +0800 Avoid crashes in some edge cases * java/org/gnu/emacs/EmacsActivity.java (onActivityResult): Avoid crashes in some edge cases. diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 86fed5396d7..4ddf51fbb20 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -462,8 +462,17 @@ public class EmacsActivity extends Activity flags = (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - if (uri != null) - resolver.takePersistableUriPermission (uri, flags); + try + { + if (uri != null) + resolver.takePersistableUriPermission (uri, flags); + } + catch (Exception exception) + { + /* Permission to access URI might've been revoked in + between selecting the file and this callback being + invoked. Don't crash in such cases. */ + } } break; commit c85222a59380aa9c12622951646874a61b82aa96 Author: Po Lu Date: Thu Jul 27 18:17:12 2023 +0800 Avoid dereference of a freed vnode's operations table * src/androidvfs.c (android_renameat_noreplace): (android_rename): Free vdst using vdst->ops, not vp->ops. diff --git a/src/androidvfs.c b/src/androidvfs.c index a32471d250e..42b1ff8770f 100644 --- a/src/androidvfs.c +++ b/src/androidvfs.c @@ -5581,7 +5581,7 @@ android_renameat_noreplace (int srcfd, const char *src, /* Now try to rename vp to vdst. */ rc = (*vp->ops->rename) (vp, vdst, true); (*vp->ops->close) (vp); - (*vp->ops->close) (vdst); + (*vdst->ops->close) (vdst); return rc; error1: @@ -5613,7 +5613,7 @@ android_rename (const char *src, const char *dst) /* Now try to rename vp to vdst. */ rc = (*vp->ops->rename) (vp, vdst, false); (*vp->ops->close) (vp); - (*vp->ops->close) (vdst); + (*vdst->ops->close) (vdst); return rc; error1: commit 24711be0502bba6c95589aa1457a755bbe12d6ad Merge: 65b58251b1b 2dc5f17c3ec Author: Po Lu Date: Thu Jul 27 17:14:58 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 65b58251b1b07b50672367c675efdfdc97354c4a Author: Po Lu Date: Thu Jul 27 17:13:39 2023 +0800 Update Android port * configure.ac (ANDROID_STUBIFY): Add androidvfs.o when building libemacs.so. * doc/emacs/android.texi (Android): Add `Android Document Providers'. (Android Startup): Update the location of the content identifier directory. (Android File System): Describe access to document provider directories. (Android Document Providers): New node. * doc/emacs/emacs.texi (Top): Update the menu for the Android appendix. * java/Makefile.in (filename, install_temp/assets/build_info): Make directory-tree depend on build_info. * java/org/gnu/emacs/EmacsActivity.java (onActivityResult): New function. When a document tree is accepted, persist access to it. * java/org/gnu/emacs/EmacsDirectoryEntry.java (EmacsDirectoryEntry): New struct. * java/org/gnu/emacs/EmacsOpenActivity.java (checkReadableOrCopy): Use EmacsService.buildContentName. * java/org/gnu/emacs/EmacsService.java (getEmacsView, openContentUri) (checkContentUri): Remove excessive debug logging. (buildContentName, getDocumentAuthorities, requestDirectoryAccess) (getDocumentTrees, decodeFileName, documentIdFromName, getTreeUri) (statDocument, accessDocument, openDocumentDirectory, readDirectoryEntry) (openDocument, createDocument): New functions. * lib-src/asset-directory-tool.c: Improve commentary by illustrating the difference between directory and ordinary files. * src/android.c (ANDROID_THROW, enum android_fd_table_entry_flags) (struct android_emacs_service, android_extract_long) (android_scan_directory_tree, android_is_directory) (android_get_asset_name, android_url_encode, android_content_name_p) (android_get_content_name, android_check_content_access, android_fstat) (android_fstatat, android_file_access_p, android_hack_asset_fd_fallback) (android_detect_ashmem, android_hack_asset_fd, android_close_on_exec) (android_open, android_close, android_fclose, android_create_lib_link) (android_faccessat, struct android_dir, android_opendir, android_dirfd) (android_readdir, android_closedir, android_lookup_asset_directory_fd) (android_exception_check_3, android_get_current_api_level) (android_open_asset, android_close_asset, android_asset_read_quit) (android_asset_read, android_asset_lseek, android_asset_fstat): Move content and asset related functions to androidvfs.c. (android_init_emacs_service): Obtain handles for new JNI functions. (initEmacsParams): Initialize the VFS layer. (android_request_directory_access): New function. (android_display_toast): Remove unused function. * src/android.h (android_get_current_api_level): Assume that this function never returns less than __ANDROID_API__. (struct android_emacs_service): Move `struct android_emacs_service' here. * src/androidfns.c (Fandroid_request_directory_access): New interactive function. (syms_of_androidfns): Register new subr. * src/androidvfs.c (struct android_vdir, struct android_vops) (struct android_vnode, struct android_special_vnode) (enum android_vnode_type, struct android_cursor_class) (struct emacs_directory_entry_class) (struct android_parcel_file_descriptor_class) (android_init_cursor_class, android_init_entry_class) (android_init_fd_class, android_vfs_canonicalize_name) (struct android_unix_vnode, struct android_unix_vdir, unix_vfs_ops) (android_unix_name, android_unix_vnode, android_unix_open) (android_unix_close, android_unix_unlink, android_unix_symlink) (android_unix_rmdir, android_unix_rename, android_unix_stat) (android_unix_access, android_unix_mkdir, android_unix_readdir) (android_unix_closedir, android_unix_dirfd, android_unix_opendir) (android_extract_long, android_scan_directory_tree) (android_is_directory, android_init_assets) (android_hack_asset_fd_fallback, android_detect_ashmem) (android_hack_asset_fd, struct android_afs_vnode) (struct android_afs_vdir, struct android_afs_open_fd, afs_vfs_ops) (android_afs_name, android_afs_initial, android_close_on_exec) (android_afs_open, android_afs_close, android_afs_unlink) (android_afs_symlink, android_afs_rmdir, android_afs_rename) (android_afs_stat, android_afs_access, android_afs_mkdir) (android_afs_readdir, android_afs_closedir, android_afs_dirfd) (android_afs_opendir, android_afs_get_directory_name) (struct android_content_vdir, content_vfs_ops) (content_directory_contents, android_content_name) (android_content_open, android_content_close) (android_content_unlink, android_content_symlink) (android_content_rmdir, android_content_rename) (android_content_stat, android_content_access) (android_content_mkdir, android_content_readdir) (android_content_closedir, android_content_dirfd) (android_content_opendir, android_content_get_directory_name) (android_content_initial, android_get_content_name) (android_check_content_access, struct android_authority_vnode) (authority_vfs_ops, android_authority_name, android_authority_open) (android_authority_close, android_authority_unlink) (android_authority_symlink, android_authority_rmdir) (android_authority_rename, android_authority_stat) (android_authority_access, android_authority_mkdir) (android_authority_opendir, android_authority_initial) (struct android_saf_root_vnode, struct android_saf_root_vdir) (saf_root_vfs_ops, android_saf_root_name, android_saf_root_open) (android_saf_root_close, android_saf_root_unlink) (android_saf_root_symlink, android_saf_root_rmdir) (android_saf_root_rename, android_saf_root_stat) (android_saf_root_access, android_saf_root_mkdir) (android_saf_root_readdir, android_saf_root_closedir) (android_saf_root_dirfd, android_saf_root_opendir) (android_saf_root_initial, android_saf_root_get_directory) (android_saf_stat, android_saf_access) (struct android_saf_tree_vnode, struct android_saf_tree_vdir) (saf_tree_vfs_ops, android_document_id_from_name) (android_saf_tree_name, android_saf_tree_open) (android_saf_tree_close, android_saf_tree_unlink) (android_saf_tree_symlink, android_saf_tree_rmdir) (android_saf_tree_rename, android_saf_tree_stat) (android_saf_tree_access, android_saf_tree_mkdir) (android_saf_tree_opendir_1, android_saf_tree_readdir) (android_saf_tree_closedir, android_saf_tree_dirfd) (android_saf_tree_opendir, android_saf_tree_from_name) (android_saf_tree_get_directory, android_saf_file_vnode) (saf_file_vfs_ops, android_saf_file_name, android_saf_file_open) (android_saf_file_unlink, android_saf_file_rmdir) (android_saf_file_opendir, android_close_parcel_fd) (android_saf_new_vnode, android_saf_new_name, android_saf_new_open) (android_saf_new_unlink, android_saf_new_symlink) (android_saf_new_rmdir, android_saf_new_rename) (android_saf_new_stat, android_saf_new_access) (android_saf_new_mkdir, android_saf_new_opendir, root_vfs_ops) (special_vnodes, android_root_name, android_name_file) (android_vfs_init, android_open, android_unlink, android_symlink) (android_rmdir, android_mkdir, android_renameat_noreplace) (android_rename, android_fstat, android_fstatat_1, android_fstatat) (android_faccessat, android_fdopen, android_close, android_fclose) (android_open_asset, android_close_asset, android_asset_read_quit) (android_asset_read, android_asset_lseek, android_asset_fstat) (android_opendir, android_dirfd, android_readdir) (android_closedir): Move file system emulation routines here. Introduce a new ``VFS'' layer for translating between Emacs-specific file names and the various disparate interfaces for accessing files on Android. * src/callproc.c (delete_temp_file): * src/charset.c (load_charset_map_from_file): * src/dired.c: * src/emacs.c (Fkill_emacs): * src/fileio.c (check_mutable_filename, Fcopy_file) (Fmake_directory_internal, Fdelete_directory_internal) (Fdelete_file, Frename_file, Fadd_name_to_file) (Fmake_symbolic_link, file_accessible_directory_p, Fset_file_modes) (Fset_file_times, write_region): * src/filelock.c (get_boot_time, rename_lock_file) (create_lock_file, current_lock_owner, unlock_file): * src/image.c (slurp_file, png_load_body, jpeg_load_body): * src/keyboard.c (Fopen_dribble_file): * src/lisp.h: * src/lread.c (Fload): * src/process.c (handle_child_signal): * src/sysdep.c (init_standard_fds, emacs_fopen, emacs_fdopen) (emacs_unlink, emacs_symlink, emacs_rmdir, emacs_mkdir) (emacs_renameat_noreplace, emacs_rename): * src/term.c (Fresume_tty, init_tty): Use and add new wrappers for fopen, fdopen, unlink, symlink, rmdir, mkdir, renameat_norepalce and rename. diff --git a/configure.ac b/configure.ac index 8d54ea0de16..ff9f604221f 100644 --- a/configure.ac +++ b/configure.ac @@ -2689,8 +2689,8 @@ AC_DEFUN # sfntfont-android.o. ANDROID_OBJ="$ANDROID_OBJ sfnt.o sfntfont.o sfntfont-android.o" - # Build androidselect.o. - ANDROID_OBJ="$ANDROID_OBJ androidselect.o" + # Build androidselect.o and androidvfs.o. + ANDROID_OBJ="$ANDROID_OBJ androidselect.o androidvfs.o" # Check for some functions not always present in the NDK. AC_CHECK_DECLS([android_get_device_api_level]) diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 86af851efd2..1f32fdfc1d2 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -17,6 +17,7 @@ Android * What is Android?:: Preamble. * Android Startup:: Starting up Emacs on Android. * Android File System:: The Android file system. +* Android Document Providers:: Accessing files from other programs. * Android Environment:: Running Emacs under Android. * Android Windowing:: The Android window system. * Android Fonts:: Font selection under Android. @@ -142,12 +143,12 @@ Android Startup that if that Emacs in turn does not start the Emacs server, subsequent attempts to open the file with the wrapper will fail. -@cindex /content directory, android +@cindex /content/by-authority directory, android Some files are given to Emacs as ``content identifiers'' that the system provides access to outside the normal filesystem APIs. Emacs -uses a pseudo-directory named @file{/content} to access those files. -Do not make any assumptions about the contents of this directory, or -try to open files in it yourself. +uses a pseudo-directory named @file{/content/by-authority} to access +those files. Do not make any assumptions about the contents of this +directory, or try to open files in it yourself. This feature is not provided on Android 4.3 and earlier, in which case such files are copied to a temporary directory before being @@ -180,12 +181,13 @@ Android File System @item Subprocesses (such as @command{ls}) can not run from the @file{/assets} directory; if you try to run a subprocess with -@code{current-directory} set to @file{/assets} or a subdirectory -thereof, it will run from the home directory instead. +@code{current-directory} set to @file{/assets}, +@file{/content/storage} or a subdirectory thereof, it will run from +the home directory instead. @item There are no @file{.} and @file{..} directories inside the -@file{/assets} directory. +@file{/assets} or @file{/content} directory. @item Files in the @file{/assets} directory are always read only, and may be @@ -193,7 +195,7 @@ Android File System @end itemize Aside from the @file{/assets} directory, Android programs normally -have access to three other directories. They are: +have access to four other directories. They are: @itemize @bullet @item @@ -209,6 +211,12 @@ Android File System The @dfn{external storage} directory. This is accessible to Emacs when the user grants the ``Files and Media'' permission to Emacs via system settings. + +@item +Directories provided by @dfn{document providers} on Android 5.0 and +later. These directories exist outside the normal Unix filesystem, +containing files provided by external programs (@pxref{Android +Document Providers}.) @end itemize The external storage directory is found at @file{/sdcard}. The @@ -265,6 +273,46 @@ Android File System These settings are not present on many proprietary versions of Android. +@node Android Document Providers +@section Accessing files from other programs under Android +@cindex document providers, Android +@cindex /content/storage directory, Android + + Android 5.0 introduces a new sort of program, the ``document +provider'': these programs are small programs that provide access to +their own files outside both the asset manager and the Unix +filesystem. Emacs supports accessing files and directories they +provide, placing their files within the directory +@file{/content/storage}. + +@findex android-request-directory-access + Before Emacs is granted access to any of these directories, it must +first request the right to access it. This is done by running the +command (@pxref{M-x}) @code{android-request-directory-access}, which +displays a file selection dialog. + + If a directory is selected within this dialog, its contents are +subsequently made available within a new directory named +@file{/content/storage/@var{authority}/@var{id}}, where +@var{authority} is the name of the document provider, and @var{id} is +a unique identifier assigned to the directory by the document +provider. + + Because these directories do not exist within the Unix file-system, +sub-processes cannot be created within them, just as with the +@file{/assets} directory (@pxref{Android File System}.) In addition, +although Emacs can normally write and create files inside these +directories, it cannot create symlinks or hard links. + +@c TODO: fix this! + Since document providers are allowed to perform expensive network +operations to obtain file contents, a file access operation within one +of these directories will possibly take a significant amount of time. +Emacs presently does not support quitting out of such file system +operations, and the timeouts applied are fully subject to the +discretion of the system and the document provider that is responding +to these operations. + @node Android Environment @section Running Emacs under Android diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi index b255e679d5f..7a21eb49e24 100644 --- a/doc/emacs/emacs.texi +++ b/doc/emacs/emacs.texi @@ -1267,6 +1267,7 @@ Top * What is Android?:: Preamble. * Android Startup:: Starting up Emacs on Android. * Android File System:: The Android file system. +* Android Document Providers:: Accessing files from other programs. * Android Environment:: Running Emacs under Android. * Android Windowing:: The Android window system. * Android Fonts:: Font selection under Android. diff --git a/java/Makefile.in b/java/Makefile.in index d11278e6110..804d4669c7a 100644 --- a/java/Makefile.in +++ b/java/Makefile.in @@ -233,7 +233,8 @@ install_temp: endif install_temp/assets/directory-tree: $(libsrc)/asset-directory-tool \ - install_temp install_temp/assets/version + install_temp install_temp/assets/version \ + install_temp/assets/build_info $(AM_V_GEN) $(libsrc)/asset-directory-tool install_temp/assets \ install_temp/assets/directory-tree @@ -246,9 +247,9 @@ install_temp/assets/version: install_temp/assets/build_info: install_temp $(AM_V_GEN) { hostname; date +%s; } > $@ -emacs.apk-in: install_temp install_temp/assets/directory-tree \ - install_temp/assets/version install_temp/assets/build_info \ - AndroidManifest.xml +emacs.apk-in: install_temp install_temp/assets/directory-tree \ + AndroidManifest.xml install_temp/assets/version \ + install_temp/assets/build_info # Package everything. Specifying the assets on this command line is # necessary for AAssetManager_getNextFileName to work on old versions # of Android. Make sure not to generate R.java, as it's already been diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index d7b51388929..86fed5396d7 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -25,6 +25,7 @@ import android.app.Activity; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -33,6 +34,8 @@ import android.util.Log; +import android.net.Uri; + import android.view.Menu; import android.view.View; import android.view.ViewTreeObserver; @@ -48,6 +51,9 @@ public class EmacsActivity extends Activity { public static final String TAG = "EmacsActivity"; + /* ID for URIs from a granted document tree. */ + public static final int ACCEPT_DOCUMENT_TREE = 1; + /* The currently attached EmacsWindow, or null if none. */ private EmacsWindow window; @@ -431,4 +437,36 @@ public class EmacsActivity extends Activity /* Update the window insets. */ syncFullscreenWith (window); } + + + + @Override + public final void + onActivityResult (int requestCode, int resultCode, Intent data) + { + ContentResolver resolver; + Uri uri; + int flags; + + switch (requestCode) + { + case ACCEPT_DOCUMENT_TREE: + + /* A document granted through + EmacsService.requestDirectoryAccess. */ + + if (resultCode == RESULT_OK) + { + resolver = getContentResolver (); + uri = data.getData (); + flags = (Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + + if (uri != null) + resolver.takePersistableUriPermission (uri, flags); + } + + break; + } + } }; diff --git a/java/org/gnu/emacs/EmacsDirectoryEntry.java b/java/org/gnu/emacs/EmacsDirectoryEntry.java new file mode 100644 index 00000000000..9c10f2e8771 --- /dev/null +++ b/java/org/gnu/emacs/EmacsDirectoryEntry.java @@ -0,0 +1,33 @@ +/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + +package org.gnu.emacs; + +/* Structure holding a single ``directory entry'' from a document + provider. */ + +public class EmacsDirectoryEntry +{ + /* The type of this directory entry. 0 means a regular file and 1 + means a directory. */ + public int d_type; + + /* The display name of the file represented. */ + public String d_name; +}; diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index 9411f85d434..3832cd2faab 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -243,18 +243,8 @@ private class EmacsClientThread extends Thread if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - content = "/content/" + uri.getEncodedAuthority (); - - for (String segment : uri.getPathSegments ()) - content += "/" + Uri.encode (segment); - - /* Append the URI query. */ - - if (uri.getEncodedQuery () != null) - content += "?" + uri.getEncodedQuery (); - + content = EmacsService.buildContentName (uri); Log.d (TAG, "checkReadableOrCopy: " + content); - return content; } diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 6240b0e475a..6059439551f 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -23,13 +23,19 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; +import android.database.Cursor; + import android.graphics.Matrix; import android.graphics.Point; +import android.webkit.MimeTypeMap; + import android.view.InputDevice; import android.view.KeyEvent; import android.view.inputmethod.CursorAnchorInfo; @@ -45,9 +51,12 @@ import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; +import android.content.UriPermission; + import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager; + import android.content.res.AssetManager; import android.hardware.input.InputManager; @@ -65,6 +74,7 @@ import android.os.VibrationEffect; import android.provider.DocumentsContract; +import android.provider.DocumentsContract.Document; import android.util.Log; import android.util.DisplayMetrics; @@ -77,14 +87,21 @@ public final class EmacsService extends Service { public static final String TAG = "EmacsService"; - public static volatile EmacsService SERVICE; + + /* The started Emacs service object. */ + public static EmacsService SERVICE; /* If non-NULL, an extra argument to pass to `android_emacs_init'. */ public static String extraStartupArgument; + /* The thread running Emacs C code. */ private EmacsThread thread; + + /* Handler used to run tasks on the main thread. */ private Handler handler; + + /* Content resolver used to access URIs. */ private ContentResolver resolver; /* Keep this in synch with androidgui.h. */ @@ -92,6 +109,13 @@ public final class EmacsService extends Service public static final int IC_MODE_ACTION = 1; public static final int IC_MODE_TEXT = 2; + /* File access mode constants. See `man 7 inode'. */ + public static final int S_IRUSR = 0000400; + public static final int S_IWUSR = 0000200; + public static final int S_IFCHR = 0020000; + public static final int S_IFDIR = 0040000; + public static final int S_IFREG = 0100000; + /* Display metrics used by font backends. */ public DisplayMetrics metrics; @@ -305,6 +329,7 @@ invocation of app_process (through android-emacs) can view = new EmacsHolder (); runnable = new Runnable () { + @Override public void run () { @@ -557,7 +582,7 @@ invocation of app_process (through android-emacs) can return String.valueOf (keysym); } - + /* Start the Emacs service if necessary. On Android 26 and up, start Emacs as a foreground service with a notification, to avoid @@ -872,6 +897,10 @@ invocation of app_process (through android-emacs) can icEndSynchronous (); } + + + /* Content provider functions. */ + /* Open a content URI described by the bytes BYTES, a non-terminated string; make it writable if WRITABLE, and readable if READABLE. Truncate the file if TRUNCATE. @@ -905,9 +934,8 @@ invocation of app_process (through android-emacs) can { /* The usual file name encoding question rears its ugly head again. */ - name = new String (bytes, "UTF-8"); - Log.d (TAG, "openContentUri: " + Uri.parse (name)); + name = new String (bytes, "UTF-8"); fd = resolver.openFileDescriptor (Uri.parse (name), mode); /* Use detachFd on newer versions of Android or plain old @@ -947,7 +975,6 @@ invocation of app_process (through android-emacs) can /* The usual file name encoding question rears its ugly head again. */ name = new String (string, "UTF-8"); - Log.d (TAG, "checkContentUri: " + Uri.parse (name)); } catch (UnsupportedEncodingException exception) { @@ -960,25 +987,58 @@ invocation of app_process (through android-emacs) can if (writable) mode += "w"; - Log.d (TAG, "checkContentUri: checking against mode " + mode); - try { fd = resolver.openFileDescriptor (Uri.parse (name), mode); fd.close (); - Log.d (TAG, "checkContentUri: YES"); - return true; } catch (Exception exception) { - Log.d (TAG, "checkContentUri: NO"); - Log.d (TAG, exception.toString ()); - return false; + /* Fall through. */ } + + return false; } + /* Build a content file name for URI. + + Return a file name within the /contents/by-authority + pseudo-directory that `android_get_content_name' can then + transform back into an encoded URI. + + A content name consists of any number of unencoded path segments + separated by `/' characters, possibly followed by a question mark + and an encoded query string. */ + + public static String + buildContentName (Uri uri) + { + StringBuilder builder; + + builder = new StringBuilder ("/content/by-authority/"); + builder.append (uri.getAuthority ()); + + /* First, append each path segment. */ + + for (String segment : uri.getPathSegments ()) + { + /* FIXME: what if segment contains a slash character? */ + builder.append ('/'); + builder.append (uri.encode (segment)); + } + + /* Now, append the query string if necessary. */ + + if (uri.getEncodedQuery () != null) + builder.append ('?').append (uri.getEncodedQuery ()); + + return builder.toString (); + } + + + private long[] queryBattery19 () { @@ -1096,4 +1156,961 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) window.view.imManager.updateExtractedText (window.view, token, text); } + + + + /* Document tree management functions. These functions shouldn't be + called before Android 5.0. + + TODO: a timeout, let alone quitting, has yet to be implemented + for any of these functions. */ + + /* Return an array of each document authority providing at least one + tree URI that Emacs holds the rights to persistently access. */ + + public String[] + getDocumentAuthorities () + { + List permissions; + HashSet allProviders; + Uri uri; + + permissions = resolver.getPersistedUriPermissions (); + allProviders = new HashSet (); + + for (UriPermission permission : permissions) + { + uri = permission.getUri (); + + if (DocumentsContract.isTreeUri (uri) + && permission.isReadPermission ()) + allProviders.add (uri.getAuthority ()); + } + + return allProviders.toArray (new String[0]); + } + + /* Start a file chooser activity to request access to a directory + tree. + + Value is 1 if the activity couldn't be started for some reason, + and 0 in any other case. */ + + public int + requestDirectoryAccess () + { + Runnable runnable; + final EmacsHolder rc; + + /* Return 1 if Android is too old to support this feature. */ + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + return 1; + + rc = new EmacsHolder (); + rc.thing = Integer.valueOf (1); + + runnable = new Runnable () { + @Override + public void + run () + { + EmacsActivity activity; + Intent intent; + int id; + + synchronized (this) + { + /* Try to obtain an activity that will receive the + response from the file chooser dialog. */ + + if (EmacsActivity.focusedActivities.isEmpty ()) + { + /* If focusedActivities is empty then this dialog + may have been displayed immediately after another + popup dialog was dismissed. Try the + EmacsActivity to be focused. */ + + activity = EmacsActivity.lastFocusedActivity; + + if (activity == null) + { + /* Still no luck. Return failure. */ + notify (); + return; + } + } + else + activity = EmacsActivity.focusedActivities.get (0); + + /* Now create the intent. */ + intent = new Intent (Intent.ACTION_OPEN_DOCUMENT_TREE); + + try + { + id = EmacsActivity.ACCEPT_DOCUMENT_TREE; + activity.startActivityForResult (intent, id, null); + rc.thing = Integer.valueOf (0); + } + catch (Exception e) + { + e.printStackTrace (); + } + + notify (); + } + } + }; + + syncRunnable (runnable); + return rc.thing; + } + + /* Return an array of each tree provided by the document PROVIDER + that Emacs has permission to access. + + Value is an array if the provider really does exist, NULL + otherwise. */ + + public String[] + getDocumentTrees (byte provider[]) + { + String providerName; + List treeList; + List permissions; + Uri uri; + + try + { + providerName = new String (provider, "ASCII"); + } + catch (UnsupportedEncodingException exception) + { + return null; + } + + permissions = resolver.getPersistedUriPermissions (); + treeList = new ArrayList (); + + for (UriPermission permission : permissions) + { + uri = permission.getUri (); + + if (DocumentsContract.isTreeUri (uri) + && uri.getAuthority ().equals (providerName) + && permission.isReadPermission ()) + /* Make sure the tree document ID is encoded. */ + treeList.add (Uri.encode (DocumentsContract.getTreeDocumentId (uri))); + } + + return treeList.toArray (new String[0]); + } + + /* Decode the specified STRING into a String object using the UTF-8 + format. If an exception is thrown, return null. */ + + private String + decodeFileName (byte[] string) + { + try + { + return new String (string, "UTF-8"); + } + catch (Exception e) /* UnsupportedEncodingException, etc. */ + { + ;; + } + + return null; + } + + /* Find the document ID of the file within TREE_URI designated by + NAME. + + NAME is a ``file name'' comprised of the display names of + individual files. Each constituent component prior to the last + must name a directory file within TREE_URI. + + Upon success, return 0 or 1 (contingent upon whether or not the + last component within NAME is a directory) and place the document + ID of the named file in ID_RETURN[0]. + + If the designated file can't be located, but each component of + NAME up to the last component can and is a directory, return -2 + and the ID of the last component located in ID_RETURN[0]; + + If the designated file can't be located, return -1. */ + + private int + documentIdFromName (String tree_uri, byte name[], + String[] id_return) + { + Uri uri, treeUri; + String nameString, id, type; + String[] components, projection; + Cursor cursor; + int column; + + projection = new String[] { + Document.COLUMN_DISPLAY_NAME, + Document.COLUMN_DOCUMENT_ID, + Document.COLUMN_MIME_TYPE, + }; + + /* Parse the URI identifying the tree first. */ + uri = Uri.parse (tree_uri); + + /* Next, decode NAME. */ + nameString = decodeFileName (name); + + /* Now, split NAME into its individual components. */ + components = nameString.split ("/"); + + /* Set id and type to the value at the root of the tree. */ + type = id = null; + + /* For each component... */ + + for (String component : components) + { + /* Java split doesn't behave very much like strtok when it + comes to trailing and leading delimiters... */ + if (component.isEmpty ()) + continue; + + /* Create the tree URI for URI from ID if it exists, or the + root otherwise. */ + + if (id == null) + id = DocumentsContract.getTreeDocumentId (uri); + + treeUri + = DocumentsContract.buildChildDocumentsUriUsingTree (uri, id); + + /* Look for a file in this directory by the name of + component. */ + + try + { + cursor = resolver.query (treeUri, projection, + (Document.COLUMN_DISPLAY_NAME + + " = ?s"), + new String[] { component, }, null); + } + catch (SecurityException exception) + { + /* A SecurityException can be thrown if Emacs doesn't have + access to treeUri. */ + return -1; + } + catch (Exception exception) + { + exception.printStackTrace (); + + /* Why is this? */ + return -1; + } + + if (cursor == null) + return -1; + + while (true) + { + /* Even though the query selects for a specific display + name, some content providers nevertheless return every + file within the directory. */ + + if (!cursor.moveToNext ()) + { + cursor.close (); + + /* If the last component considered is a + directory... */ + if ((type == null + || type.equals (Document.MIME_TYPE_DIR)) + /* ... and type and id currently represent the + penultimate component. */ + && component == components[components.length - 1]) + { + /* The cursor is empty. In this case, return -2 + and the current document ID (belonging to the + previous component) in ID_RETURN. */ + + id_return[0] = id; + + /* But return -1 on the off chance that id is + null. */ + + if (id == null) + return -1; + + return -2; + } + + /* The last component found is not a directory, so + return -1. */ + return -1; + } + + /* So move CURSOR to a row with the right display + name. */ + + column = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME); + + if (column < 0) + continue; + + try + { + nameString = cursor.getString (column); + } + catch (Exception exception) + { + cursor.close (); + return -1; + } + + /* Break out of the loop only once a matching component is + found. */ + + if (nameString.equals (component)) + break; + } + + /* Look for a column by the name of COLUMN_DOCUMENT_ID. */ + + column = cursor.getColumnIndex (Document.COLUMN_DOCUMENT_ID); + + if (column < 0) + { + cursor.close (); + return -1; + } + + /* Now replace ID with the document ID. */ + + try + { + id = cursor.getString (column); + } + catch (Exception exception) + { + cursor.close (); + return -1; + } + + /* If this is the last component, be sure to initialize the + document type. */ + + if (component == components[components.length - 1]) + { + column + = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + + if (column < 0) + { + cursor.close (); + return -1; + } + + try + { + type = cursor.getString (column); + } + catch (Exception exception) + { + cursor.close (); + return -1; + } + + /* Type may be NULL depending on how the Cursor returned + is implemented. */ + + if (type == null) + { + cursor.close (); + return -1; + } + } + + /* Now close the cursor. */ + cursor.close (); + + /* ID may have become NULL if the data is in an invalid + format. */ + if (id == null) + return -1; + } + + /* Here, id is either NULL (meaning the same as TREE_URI), and + type is either NULL (in which case id should also be NULL) or + the MIME type of the file. */ + + /* First return the ID. */ + + if (id == null) + id_return[0] = DocumentsContract.getTreeDocumentId (uri); + else + id_return[0] = id; + + /* Next, return whether or not this is a directory. */ + if (type == null || type.equals (Document.MIME_TYPE_DIR)) + return 1; + + return 0; + } + + /* Return an encoded document URI representing a tree with the + specified IDENTIFIER supplied by the authority AUTHORITY. + + Return null instead if Emacs does not have permanent access + to the specified document tree recorded on disk. */ + + public String + getTreeUri (String tree, String authority) + { + Uri uri, grantedUri; + List permissions; + + /* First, build the URI. */ + tree = Uri.decode (tree); + uri = DocumentsContract.buildTreeDocumentUri (authority, tree); + + /* Now, search for it within the list of persisted URI + permissions. */ + permissions = resolver.getPersistedUriPermissions (); + + for (UriPermission permission : permissions) + { + /* If the permission doesn't entitle Emacs to read access, + skip it. */ + + if (!permission.isReadPermission ()) + continue; + + grantedUri = permission.getUri (); + + if (grantedUri.equals (uri)) + return uri.toString (); + } + + /* Emacs doesn't have permission to access this tree URI. */ + return null; + } + + /* Return file status for the document designated by the given + DOCUMENTID and tree URI. If DOCUMENTID is NULL, use the document + ID in URI itself. + + Value is null upon failure, or an array of longs [MODE, SIZE, + MTIM] upon success, where MODE contains the file type and access + modes of the file as in `struct stat', SIZE is the size of the + file in BYTES or -1 if not known, and MTIM is the time of the + last modification to this file in milliseconds since 00:00, + January 1st, 1970. */ + + public long[] + statDocument (String uri, String documentId) + { + Uri uriObject; + String[] projection; + long[] stat; + int index; + long tem; + String tem1; + Cursor cursor; + + uriObject = Uri.parse (uri); + + if (documentId == null) + documentId = DocumentsContract.getTreeDocumentId (uriObject); + + /* Create a document URI representing DOCUMENTID within URI's + authority. */ + + uriObject + = DocumentsContract.buildDocumentUriUsingTree (uriObject, documentId); + + /* Now stat this document. */ + + projection = new String[] { + Document.COLUMN_FLAGS, + Document.COLUMN_LAST_MODIFIED, + Document.COLUMN_MIME_TYPE, + Document.COLUMN_SIZE, + }; + + try + { + cursor = resolver.query (uriObject, projection, null, + null, null); + } + catch (SecurityException exception) + { + /* A SecurityException can be thrown if Emacs doesn't have + access to uriObject. */ + return null; + } + catch (UnsupportedOperationException exception) + { + exception.printStackTrace (); + + /* Why is this? */ + return null; + } + + if (cursor == null || !cursor.moveToFirst ()) + return null; + + /* Create the array of file status. */ + stat = new long[3]; + + try + { + index = cursor.getColumnIndex (Document.COLUMN_FLAGS); + if (index < 0) + return null; + + tem = cursor.getInt (index); + + stat[0] |= S_IRUSR; + if ((tem & Document.FLAG_SUPPORTS_WRITE) != 0) + stat[0] |= S_IWUSR; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N + && (tem & Document.FLAG_VIRTUAL_DOCUMENT) != 0) + stat[0] |= S_IFCHR; + + index = cursor.getColumnIndex (Document.COLUMN_SIZE); + if (index < 0) + return null; + + if (cursor.isNull (index)) + stat[1] = -1; /* The size is unknown. */ + else + stat[1] = cursor.getLong (index); + + index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + if (index < 0) + return null; + + tem1 = cursor.getString (index); + + /* Check if this is a directory file. */ + if (tem1.equals (Document.MIME_TYPE_DIR) + /* Files shouldn't be specials and directories at the same + time, but Android doesn't forbid document providers + from returning this information. */ + && (stat[0] & S_IFCHR) == 0) + /* Since FLAG_SUPPORTS_WRITE doesn't apply to directories, + just assume they're writable. */ + stat[0] |= S_IFDIR | S_IWUSR; + + /* If this file is neither a character special nor a + directory, indicate that it's a regular file. */ + + if ((stat[0] & (S_IFDIR | S_IFCHR)) == 0) + stat[0] |= S_IFREG; + + index = cursor.getColumnIndex (Document.COLUMN_LAST_MODIFIED); + + if (index >= 0 && !cursor.isNull (index)) + { + /* Content providers are allowed to not provide mtime. */ + tem = cursor.getLong (index); + stat[2] = tem; + } + } + catch (Exception exception) + { + /* Whether or not type errors cause exceptions to be signaled + is defined ``by the implementation of Cursor'', whatever + that means. */ + exception.printStackTrace (); + return null; + } + + return stat; + } + + /* Find out whether Emacs has access to the document designated by + the specified DOCUMENTID within the tree URI. If DOCUMENTID is + NULL, use the document ID in URI itself. + + If WRITABLE, also check that the file is writable, which is true + if it is either a directory or its flags contains + FLAG_SUPPORTS_WRITE. + + Value is 0 if the file is accessible, and one of the following if + not: + + -1, if the file does not exist. + -2, upon a security exception or if WRITABLE the file + is not writable. + -3, upon any other error. */ + + public int + accessDocument (String uri, String documentId, boolean writable) + { + Uri uriObject; + String[] projection; + int tem, index; + String tem1; + Cursor cursor; + + uriObject = Uri.parse (uri); + + if (documentId == null) + documentId = DocumentsContract.getTreeDocumentId (uriObject); + + /* Create a document URI representing DOCUMENTID within URI's + authority. */ + + uriObject + = DocumentsContract.buildDocumentUriUsingTree (uriObject, documentId); + + /* Now stat this document. */ + + projection = new String[] { + Document.COLUMN_FLAGS, + Document.COLUMN_MIME_TYPE, + }; + + try + { + cursor = resolver.query (uriObject, projection, null, + null, null); + } + catch (SecurityException exception) + { + /* A SecurityException can be thrown if Emacs doesn't have + access to uriObject. */ + return -2; + } + catch (UnsupportedOperationException exception) + { + exception.printStackTrace (); + + /* Why is this? */ + return -3; + } + + if (cursor == null || !cursor.moveToFirst ()) + return -1; + + if (!writable) + return 0; + + try + { + index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + if (index < 0) + return -3; + + /* Get the type of this file to check if it's a directory. */ + tem1 = cursor.getString (index); + + /* Check if this is a directory file. */ + if (tem1.equals (Document.MIME_TYPE_DIR)) + { + /* If so, don't check for FLAG_SUPPORTS_WRITE. + Check for FLAG_DIR_SUPPORTS_CREATE instead. */ + + if (!writable) + return 0; + + index = cursor.getColumnIndex (Document.COLUMN_FLAGS); + if (index < 0) + return -3; + + tem = cursor.getInt (index); + if ((tem & Document.FLAG_DIR_SUPPORTS_CREATE) == 0) + return -3; + + return 0; + } + + index = cursor.getColumnIndex (Document.COLUMN_FLAGS); + if (index < 0) + return -3; + + tem = cursor.getInt (index); + if (writable && (tem & Document.FLAG_SUPPORTS_WRITE) == 0) + return -3; + } + catch (Exception exception) + { + /* Whether or not type errors cause exceptions to be signaled + is defined ``by the implementation of Cursor'', whatever + that means. */ + exception.printStackTrace (); + return -3; + } + + return 0; + } + + /* Open a cursor representing each entry within the directory + designated by the specified DOCUMENTID within the tree URI. + + If DOCUMENTID is NULL, use the document ID within URI itself. + Value is NULL upon failure. */ + + public Cursor + openDocumentDirectory (String uri, String documentId) + { + Uri uriObject; + Cursor cursor; + String projection[]; + + uriObject = Uri.parse (uri); + + /* If documentId is not set, use the document ID of the tree URI + itself. */ + + if (documentId == null) + documentId = DocumentsContract.getTreeDocumentId (uriObject); + + /* Build a URI representing each directory entry within + DOCUMENTID. */ + + uriObject + = DocumentsContract.buildChildDocumentsUriUsingTree (uriObject, + documentId); + + projection = new String [] { + Document.COLUMN_DISPLAY_NAME, + Document.COLUMN_MIME_TYPE, + }; + + try + { + cursor = resolver.query (uriObject, projection, null, null, + null); + } + catch (SecurityException exception) + { + /* A SecurityException can be thrown if Emacs doesn't have + access to uriObject. */ + return null; + } + catch (UnsupportedOperationException exception) + { + exception.printStackTrace (); + + /* Why is this? */ + return null; + } + + /* Return the cursor. */ + return cursor; + } + + /* Read a single directory entry from the specified CURSOR. Return + NULL if at the end of the directory stream, and a directory entry + with `d_name' set to NULL if an error occurs. */ + + public EmacsDirectoryEntry + readDirectoryEntry (Cursor cursor) + { + EmacsDirectoryEntry entry; + int index; + String name, type; + + entry = new EmacsDirectoryEntry (); + + while (true) + { + if (!cursor.moveToNext ()) + return null; + + /* First, retrieve the display name. */ + index = cursor.getColumnIndex (Document.COLUMN_DISPLAY_NAME); + + if (index < 0) + /* Return an invalid directory entry upon failure. */ + return entry; + + try + { + name = cursor.getString (index); + } + catch (Exception exception) + { + return entry; + } + + /* Skip this entry if its name cannot be represented. */ + + if (name.equals ("..") || name.equals (".") || name.contains ("/")) + continue; + + /* Now, look for its type. */ + + index = cursor.getColumnIndex (Document.COLUMN_MIME_TYPE); + + if (index < 0) + /* Return an invalid directory entry upon failure. */ + return entry; + + try + { + type = cursor.getString (index); + } + catch (Exception exception) + { + return entry; + } + + if (type.equals (Document.MIME_TYPE_DIR)) + entry.d_type = 1; + entry.d_name = name; + return entry; + } + + /* Not reached. */ + } + + /* Open a file descriptor for a file document designated by + DOCUMENTID within the document tree identified by URI. If + TRUNCATE and the document already exists, truncate its contents + before returning. + + On Android 9.0 and earlier, always open the document in + ``read-write'' mode; this instructs the document provider to + return a seekable file that is stored on disk and returns correct + file status. + + Under newer versions of Android, open the document in a + non-writable mode if WRITE is false. This is possible because + these versions allow Emacs to explicitly request a seekable + on-disk file. + + Value is NULL upon failure or a parcel file descriptor upon + success. Call `ParcelFileDescriptor.close' on this file + descriptor instead of using the `close' system call. */ + + public ParcelFileDescriptor + openDocument (String uri, String documentId, boolean write, + boolean truncate) + { + Uri treeUri, documentUri; + String mode; + ParcelFileDescriptor fileDescriptor; + + treeUri = Uri.parse (uri); + + /* documentId must be set for this request, since it doesn't make + sense to ``open'' the root of the directory tree. */ + + documentUri + = DocumentsContract.buildDocumentUriUsingTree (treeUri, documentId); + + try + { + if (write || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) + { + /* Select the mode used to open the file. `rw' means open + a stat-able file, while `rwt' means that and to + truncate the file as well. */ + + if (truncate) + mode = "rwt"; + else + mode = "rw"; + + fileDescriptor + = resolver.openFileDescriptor (documentUri, mode, + null); + } + else + { + /* Select the mode used to open the file. `openFile' + below means always open a stat-able file. */ + + if (truncate) + /* Invalid mode! */ + return null; + else + mode = "r"; + + fileDescriptor = resolver.openFile (documentUri, mode, null); + } + } + catch (Exception exception) + { + return null; + } + + return fileDescriptor; + } + + /* Create a new document with the given display NAME within the + directory identified by DOCUMENTID inside the document tree + designated by URI. + + If DOCUMENTID is NULL, create the document inside the root of + that tree. + + Return the document ID of the new file upon success, NULL + otherwise. */ + + public String + createDocument (String uri, String documentId, String name) + { + String mimeType, separator, mime, extension; + int index; + MimeTypeMap singleton; + Uri directoryUri, docUri; + + /* Try to get the MIME type for this document. + Default to ``application/octet-stream''. */ + + mimeType = "application/octet-stream"; + + /* Abuse WebView stuff to get the file's MIME type. */ + + index = name.lastIndexOf ('.'); + + if (index > 0) + { + singleton = MimeTypeMap.getSingleton (); + extension = name.substring (index + 1); + mime = singleton.getMimeTypeFromExtension (extension); + + if (mime != null) + mimeType = mime; + } + + /* Now parse URI. */ + directoryUri = Uri.parse (uri); + + if (documentId == null) + documentId = DocumentsContract.getTreeDocumentId (directoryUri); + + /* And build a file URI referring to the directory. */ + + directoryUri + = DocumentsContract.buildChildDocumentsUriUsingTree (directoryUri, + documentId); + + try + { + docUri = DocumentsContract.createDocument (resolver, + directoryUri, + mimeType, name); + + if (docUri == null) + return null; + + /* Return the ID of the new document. */ + return DocumentsContract.getDocumentId (docUri); + } + catch (Exception exception) + { + exception.printStackTrace (); + } + + return null; + } }; diff --git a/lib-src/asset-directory-tool.c b/lib-src/asset-directory-tool.c index 239ab083b66..99c954a3922 100644 --- a/lib-src/asset-directory-tool.c +++ b/lib-src/asset-directory-tool.c @@ -45,7 +45,12 @@ Copyright (C) 2023 Free Software Foundation, Inc. the first file or directory, a NULL byte and an unsigned int indicating the offset from the start of the file to the start of the next sibling. Following that is a list of subdirectories or - files in the same format. The long is stored LSB first. */ + files in the same format. The long is stored LSB first. + + Directories can be distinguished from ordinary files through the + last bytes of their file names (immediately previous to their + terminating NULL bytes), which are set to the directory separator + character `/'. */ diff --git a/src/android.c b/src/android.c index 6fcaa40b2a9..b1d7b75c129 100644 --- a/src/android.c +++ b/src/android.c @@ -29,6 +29,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include #include +#include #include #include @@ -56,17 +57,9 @@ Copyright (C) 2023 Free Software Foundation, Inc. #ifndef ANDROID_STUBIFY -#if __ANDROID_API__ >= 9 -#include -#include -#else -#include "android-asset.h" -#endif - #include #include -#include #include #include @@ -78,50 +71,6 @@ Copyright (C) 2023 Free Software Foundation, Inc. #define ANDROID_THROW(env, class, msg) \ ((*(env))->ThrowNew ((env), (*(env))->FindClass ((env), class), msg)) -#define ANDROID_MAX_ASSET_FD 65535 - -struct android_fd_table_entry -{ - /* Various flags associated with this table. */ - short flags; - - /* The stat buffer associated with this entry. */ - struct stat statb; -}; - -enum android_fd_table_entry_flags - { - ANDROID_FD_TABLE_ENTRY_IS_VALID = 1, - }; - -struct android_emacs_service -{ - jclass class; - jmethodID fill_rectangle; - jmethodID fill_polygon; - jmethodID draw_rectangle; - jmethodID draw_line; - jmethodID draw_point; - jmethodID clear_window; - jmethodID clear_area; - jmethodID ring_bell; - jmethodID query_tree; - jmethodID get_screen_width; - jmethodID get_screen_height; - jmethodID detect_mouse; - jmethodID name_keysym; - jmethodID browse_url; - jmethodID restart_emacs; - jmethodID update_ic; - jmethodID reset_ic; - jmethodID open_content_uri; - jmethodID check_content_uri; - jmethodID query_battery; - jmethodID display_toast; - jmethodID update_extracted_text; - jmethodID update_cursor_anchor_info; -}; - struct android_emacs_pixmap { jclass class; @@ -174,12 +123,6 @@ #define ANDROID_MAX_ASSET_FD 65535 /* The API level of the current device. */ static int android_api_level; -/* The asset manager being used. */ -static AAssetManager *asset_manager; - -/* Whether or not Emacs has been initialized. */ -static int emacs_initialized; - /* The directory used to store site-lisp. */ char *android_site_load_path; @@ -206,11 +149,6 @@ #define ANDROID_MAX_ASSET_FD 65535 /* The Android application data directory. */ static char *android_files_dir; -/* Array of structures used to hold asset information corresponding to - a file descriptor. This assumes Emacs does not do funny things - with dup. It currently does not. */ -static struct android_fd_table_entry android_table[ANDROID_MAX_ASSET_FD]; - /* The Java environment being used for the main thread. */ JNIEnv *android_java_env; @@ -235,10 +173,10 @@ #define ANDROID_MAX_ASSET_FD 65535 static jmethodID android_rect_constructor; /* The EmacsService object. */ -static jobject emacs_service; +jobject emacs_service; /* Various methods associated with the EmacsService. */ -static struct android_emacs_service service_class; +struct android_emacs_service service_class; /* Various methods associated with the EmacsPixmap class. */ static struct android_emacs_pixmap pixmap_class; @@ -881,224 +819,6 @@ android_run_debug_thread (void *data) -/* Asset directory handling functions. ``directory-tree'' is a file in - the root of the assets directory describing its contents. - - See lib-src/asset-directory-tool for more details. */ - -/* The Android directory tree. */ -static const char *directory_tree; - -/* The size of the directory tree. */ -static size_t directory_tree_size; - -/* Read an unaligned (32-bit) long from the address POINTER. */ - -static unsigned int -android_extract_long (char *pointer) -{ - unsigned int number; - - memcpy (&number, pointer, sizeof number); - return number; -} - -/* Scan to the file FILE in the asset directory tree. Return a - pointer to the end of that file (immediately before any children) - in the directory tree, or NULL if that file does not exist. - - If returning non-NULL, also return the offset to the end of the - last subdirectory or file in *LIMIT_RETURN. LIMIT_RETURN may be - NULL. - - FILE must have less than 11 levels of nesting. If it ends with a - trailing slash, then NULL will be returned if it is not actually a - directory. */ - -static const char * -android_scan_directory_tree (char *file, size_t *limit_return) -{ - char *token, *saveptr, *copy, *copy1, *start, *max, *limit; - size_t token_length, ntokens, i; - char *tokens[10]; - - USE_SAFE_ALLOCA; - - /* Skip past the 5 byte header. */ - start = (char *) directory_tree + 5; - - /* Figure out the current limit. */ - limit = (char *) directory_tree + directory_tree_size; - - /* Now, split `file' into tokens, with the delimiter being the file - name separator. Look for the file and seek past it. */ - - ntokens = 0; - saveptr = NULL; - copy = copy1 = xstrdup (file); - memset (tokens, 0, sizeof tokens); - - while ((token = strtok_r (copy, "/", &saveptr))) - { - copy = NULL; - - /* Make sure ntokens is within bounds. */ - if (ntokens == ARRAYELTS (tokens)) - { - xfree (copy1); - goto fail; - } - - tokens[ntokens] = SAFE_ALLOCA (strlen (token) + 1); - memcpy (tokens[ntokens], token, strlen (token) + 1); - ntokens++; - } - - /* Free the copy created for strtok_r. */ - xfree (copy1); - - /* If there are no tokens, just return the start of the directory - tree. */ - - if (!ntokens) - { - SAFE_FREE (); - - /* Return the size of the directory tree as the limit. - Do not subtract the initial header bytes, as the limit - is an offset from the start of the file. */ - - if (limit_return) - *limit_return = directory_tree_size; - - return start; - } - - /* Loop through tokens, indexing the directory tree each time. */ - - for (i = 0; i < ntokens; ++i) - { - token = tokens[i]; - - /* Figure out how many bytes to compare. */ - token_length = strlen (token); - - again: - - /* If this would be past the directory, return NULL. */ - if (start + token_length > limit) - goto fail; - - /* Now compare the file name. */ - if (!memcmp (start, token, token_length)) - { - /* They probably match. Find the NULL byte. It must be - either one byte past start + token_length, with the last - byte a trailing slash (indicating that it is a - directory), or just start + token_length. Return 4 bytes - past the next NULL byte. */ - - max = memchr (start, 0, limit - start); - - if (max != start + token_length - && !(max == start + token_length + 1 - && *(max - 1) == '/')) - goto false_positive; - - /* Return it if it exists and is in range, and this is the - last token. Otherwise, set it as start and the limit as - start + the offset and continue the loop. */ - - if (max && max + 5 <= limit) - { - if (i < ntokens - 1) - { - start = max + 5; - limit = ((char *) directory_tree - + android_extract_long (max + 1)); - - /* Make sure limit is still in range. */ - if (limit > directory_tree + directory_tree_size - || start > directory_tree + directory_tree_size) - goto fail; - - continue; - } - - /* Now see if max is not a directory and file is. If - file is a directory, then return NULL. */ - if (*(max - 1) != '/' && file[strlen (file) - 1] == '/') - max = NULL; - else - { - /* Figure out the limit. */ - if (limit_return) - *limit_return = android_extract_long (max + 1); - - /* Go to the end of this file. */ - max += 5; - } - - SAFE_FREE (); - return max; - } - - /* Return NULL otherwise. */ - __android_log_print (ANDROID_LOG_WARN, __func__, - "could not scan to end of directory tree" - ": %s", file); - goto fail; - } - - false_positive: - - /* No match was found. Set start to the next sibling and try - again. */ - - start = memchr (start, 0, limit - start); - - if (!start || start + 5 > limit) - goto fail; - - start = ((char *) directory_tree - + android_extract_long (start + 1)); - - /* Make sure start is still in bounds. */ - - if (start > limit) - goto fail; - - /* Continue the loop. */ - goto again; - } - - fail: - SAFE_FREE (); - return NULL; -} - -/* Return whether or not the directory tree entry DIR is a - directory. - - DIR should be a value returned by - `android_scan_directory_tree'. */ - -static bool -android_is_directory (const char *dir) -{ - /* If the directory is the directory tree, then it is a - directory. */ - if (dir == directory_tree + 5) - return true; - - /* Otherwise, look 5 bytes behind. If it is `/', then it is a - directory. */ - return (dir - 6 >= directory_tree - && *(dir - 6) == '/'); -} - - - /* Intercept USER_FULL_NAME and return something that makes sense if pw->pw_gecos is NULL. */ @@ -1158,46 +878,106 @@ android_is_special_directory (const char *name, const char *dir) } } -/* Given a real file name, return the part that describes its asset - path, or NULL if it is not an asset. +#if 0 - If FILENAME contains only `/assets', return `/' to indicate the - root of the assets hierarchy. */ +/* URL-encode N bytes of the specified STRING into at most N bytes of + BUFFER; STRING is assumed to be encoded in a `utf-8-emacs' + compatible coding system. Value is the number of bytes encoded + (excluding the trailing null byte placed at the end of the encoded + text) or -1 upon failure. */ -static const char * -android_get_asset_name (const char *filename) +static ssize_t +android_url_encode (const char *restrict string, size_t length, + char *restrict buffer, size_t n) { - const char *name; + int len, character; + size_t num_encoded; + char *end; + char format[1 + 25]; - name = android_is_special_directory (filename, "/assets"); + /* For each multibyte character... */ - if (!name) - return NULL; + end = string + length; + num_encoded = 0; + + while (string < end) + { + /* XXX: Android documentation claims that URIs is encoded + according to the ``Unicode'' scheme, but what this means in + reality is that the URI is encoded in UTF-8, and then + each of its bytes are encoded. */ + /* Find the length of the multibyte character at STRING. */ + len = /* multibyte_length (string, end, true, true) */ 1; + + /* 0 means that STRING is not a valid multibyte string. */ + if (!len || string + len > end) + goto failure; + + /* Now fetch the character and increment string. */ + /* character = /\* STRING_CHAR ((unsigned char *) string) *\/; */ + character = *(unsigned char *) string; + string += len; + + /* If CHARACTER is not a letter or an unreserved character, + escape it. */ + + if (!((character >= 'A' + && character <= 'Z') + || (character >= 'a' + && character <= 'z') + || (character >= '0' + && character <= '9') + || character == '_' + || character == '-' + || character == '!' + || character == '.' + || character == '~' + || character == '\'' + || character == '(' + || character == ')' + || character == '*')) + { + len = sprintf (format, "%%%X", (unsigned int) character); + if (len < 0) + goto failure; - /* If NAME is empty, return /. Otherwise, return the name relative - to /assets/. */ + /* See if there is enough space left to hold the encoded + string. */ - if (*name) - return name; + if (n < len) + goto failure; - return "/"; -} + n -= len; + num_encoded += len; -/* Return whether or not the specified FILENAME actually resolves to a - content resolver URI. */ + /* Copy the encoded string to STRING. */ + memcpy (buffer, format, n); + buffer += len; + } + else + { + /* No more space within BUFFER. */ + if (!n) + goto failure; -static bool -android_content_name_p (const char *filename) -{ - /* Content URIs aren't supported before Android 4.4, so return - false. */ + /* Don't encode this ASCII character; just store it. */ + n--, num_encoded++; + *(buffer++) = character; + } + } + + /* If there's no space for a trailing null byte or more bytes have + been encoded than representable in ssize_t, fail. */ - if (android_api_level < 19) - return false; + if (!n || num_encoded > SSIZE_MAX) + goto failure; + + /* Store the terminating NULL byte. */ + *buffer = '\0'; + return num_encoded; - return (android_is_special_directory (filename, - "/content") - != NULL); + failure: + return -1; } /* Return the content URI corresponding to a `/content' file name, @@ -1209,10 +989,9 @@ android_content_name_p (const char *filename) android_get_content_name (const char *filename) { static char buffer[PATH_MAX + 1]; - char *head, *token, *saveptr, *copy; - size_t n; - - n = PATH_MAX; + char *head, *token, *next, *saveptr, *copy, *mark, *mark1; + ssize_t rc; + size_t n, length; /* Find the file name described if it starts with `/content'. If just the directory is described, return content://. */ @@ -1229,743 +1008,148 @@ android_get_content_name (const char *filename) URI. */ copy = xstrdup (filename); - token = saveptr = NULL; + mark = saveptr = NULL; head = stpcpy (buffer, "content:/"); /* Split FILENAME by slashes. */ - while ((token = strtok_r (!token ? copy : NULL, - "/", &saveptr))) - { - head = stpncpy (head, "/", n--); - head = stpncpy (head, token, n); - - /* Check that head has not overflown the buffer. */ - eassert ((head - buffer) <= PATH_MAX); - - n = PATH_MAX - (head - buffer); - } - - /* Make sure the given buffer ends up NULL terminated. */ - buffer[PATH_MAX] = '\0'; - xfree (copy); - - return buffer; -} - -/* Return whether or not the specified FILENAME is an accessible - content URI. MODE specifies what to check. */ - -static bool -android_check_content_access (const char *filename, int mode) -{ - const char *name; - jobject string; - size_t length; - jboolean rc; - - name = android_get_content_name (filename); - length = strlen (name); - - string = (*android_java_env)->NewByteArray (android_java_env, - length); - android_exception_check (); - - (*android_java_env)->SetByteArrayRegion (android_java_env, - string, 0, length, - (jbyte *) name); - rc = (*android_java_env)->CallBooleanMethod (android_java_env, - emacs_service, - service_class.check_content_uri, - string, - (jboolean) ((mode & R_OK) - != 0), - (jboolean) ((mode & W_OK) - != 0)); - android_exception_check_1 (string); - ANDROID_DELETE_LOCAL_REF (string); - - return rc; -} - -/* Like fstat. However, look up the asset corresponding to the file - descriptor. If it exists, return the right information. */ - -int -android_fstat (int fd, struct stat *statb) -{ - if (fd < ANDROID_MAX_ASSET_FD - && (android_table[fd].flags - & ANDROID_FD_TABLE_ENTRY_IS_VALID)) - { - memcpy (statb, &android_table[fd].statb, - sizeof *statb); - return 0; - } - - return fstat (fd, statb); -} - -static int android_lookup_asset_directory_fd (int, - const char *restrict *, - const char *restrict); - -/* Like fstatat. However, if dirfd is AT_FDCWD and PATHNAME is an - asset, find the information for the corresponding asset, and if - dirfd is an offset into directory_tree as returned by - `android_dirfd', find the information within the corresponding - directory tree entry. */ - -int -android_fstatat (int dirfd, const char *restrict pathname, - struct stat *restrict statbuf, int flags) -{ - AAsset *asset_desc; - const char *asset; - const char *asset_dir; - int fd, rc; - - /* Look up whether or not DIRFD belongs to an open struct - android_dir. */ - - if (dirfd != AT_FDCWD) - dirfd - = android_lookup_asset_directory_fd (dirfd, &pathname, - pathname); - - if (dirfd == AT_FDCWD - && asset_manager - && (asset = android_get_asset_name (pathname))) - { - /* Look up whether or not PATHNAME happens to be a - directory. */ - asset_dir = android_scan_directory_tree ((char *) asset, - NULL); - - if (!asset_dir) - { - errno = ENOENT; - return -1; - } - - if (android_is_directory (asset_dir)) - { - memset (statbuf, 0, sizeof *statbuf); - - /* Fill in the stat buffer. */ - statbuf->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; - return 0; - } - - /* AASSET_MODE_STREAMING is fastest here. */ - asset_desc = AAssetManager_open (asset_manager, asset, - AASSET_MODE_STREAMING); - - if (!asset_desc) - return ENOENT; - - memset (statbuf, 0, sizeof *statbuf); - - /* Fill in the stat buffer. */ - statbuf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - statbuf->st_size = AAsset_getLength (asset_desc); - - /* Close the asset. */ - AAsset_close (asset_desc); - return 0; - } - - if (dirfd == AT_FDCWD - && android_init_gui - && android_content_name_p (pathname)) - { - /* This is actually a content:// URI. Open that file and call - stat on it. */ - - fd = android_open (pathname, O_RDONLY, 0); - - if (fd < 0) - return -1; - - rc = fstat (fd, statbuf); - android_close (fd); - return rc; - } - - return fstatat (dirfd, pathname, statbuf, flags); -} - -/* Return if NAME, a file name relative to the /assets directory, is - accessible, as long as !(amode & W_OK). */ - -static bool -android_file_access_p (const char *name, int amode) -{ - if (!asset_manager) - return false; + token = strtok_r (copy, "/", &saveptr); - if (!(amode & W_OK)) + while (token) { - if (!strcmp (name, "") || !strcmp (name, "/")) - /* /assets always exists. */ - return true; - - /* Check if the file exists by looking in the ``directory tree'' - asset generated during the build process. This is used - instead of the AAsset functions, because the latter are - buggy and treat directories inconsistently. */ - return android_scan_directory_tree ((char *) name, NULL) != NULL; - } - - return false; -} - -/* Do the same as android_hack_asset_fd, but use an unlinked temporary - file to cater to old Android kernels where ashmem files are not - readable. */ - -static int -android_hack_asset_fd_fallback (AAsset *asset) -{ - int fd; - char filename[PATH_MAX]; - size_t size; - void *mem; - - /* Assets must be small enough to fit in size_t, if off_t is - larger. */ - size = AAsset_getLength (asset); - - /* Get an unlinked file descriptor from a file in the cache - directory, which is guaranteed to only be written to by Emacs. - Creating an ashmem file descriptor and reading from it doesn't - work on these old Android versions. */ - - snprintf (filename, PATH_MAX, "%s/temp~unlinked.%d", - android_cache_dir, getpid ()); - fd = open (filename, O_CREAT | O_RDWR | O_TRUNC, - S_IRUSR | S_IWUSR); - - if (fd < 0) - return -1; - - if (unlink (filename)) - goto fail; - - if (ftruncate (fd, size)) - goto fail; - - mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0); - if (mem == MAP_FAILED) - { - __android_log_print (ANDROID_LOG_ERROR, __func__, - "mmap: %s", strerror (errno)); - goto fail; - } - - if (AAsset_read (asset, mem, size) != size) - { - /* Too little was read. Close the file descriptor and - report an error. */ - __android_log_print (ANDROID_LOG_ERROR, __func__, - "AAsset_read: %s", strerror (errno)); - goto fail; - } - - munmap (mem, size); - return fd; - - fail: - close (fd); - return -1; -} + /* Compute the number of bytes remaining in buffer excluding a + trailing null byte. */ + n = PATH_MAX - (head - buffer); -/* Pointer to the `ASharedMemory_create' function which is loaded - dynamically. */ -static int (*asharedmemory_create) (const char *, size_t); + /* Write / to the buffer. Return failure if there is no space + for it. */ -/* Return whether or not shared memory file descriptors can also be - read from, and are thus suitable for creating asset files. + if (!n) + goto failure; - This does not work on some ancient Android systems running old - versions of the kernel. */ + *head++ = '/'; + n--; -static bool -android_detect_ashmem (void) -{ - int fd, rc; - void *mem; - char test_buffer[10]; + /* Find the next token now. */ + next = strtok_r (NULL, "/", &saveptr); - memcpy (test_buffer, "abcdefghi", 10); + /* Detect and avoid encoding an encoded URL query affixed to the + end of the last component within the content file name. - /* Create the file descriptor to be used for the test. */ + Content URIs can include a query describing parameters that + must be provided to the content provider. They are separated + from the rest of the URI by a single question mark character, + which should not be encoded. - /* Android 28 and earlier let Emacs access /dev/ashmem directly, so - prefer that over using ASharedMemory. */ + However, the distinction between the separator and question + marks that appear inside file name components is lost when a + content URI is decoded into a content path. To compensate + for this loss of information, Emacs assumes that the last + question mark is always a URI separator, and suffixes content + file names which contain question marks with a trailing + question mark. */ - if (android_api_level <= 28) - { - fd = open ("/dev/ashmem", O_RDWR); - - if (fd < 0) - return false; - - /* An empty name means the memory area will exist until the file - descriptor is closed, because no other process can - attach. */ - rc = ioctl (fd, ASHMEM_SET_NAME, ""); - - if (rc < 0) + if (!next) { - close (fd); - return false; - } + /* Find the last question mark character. */ - rc = ioctl (fd, ASHMEM_SET_SIZE, sizeof test_buffer); + mark1 = strchr (token, '?'); - if (rc < 0) - { - close (fd); - return false; - } - } - else - { - /* On the other hand, SELinux restrictions on Android 29 and - later require that Emacs use a system service to obtain - shared memory. Load this dynamically, as this service is not - available on all versions of the NDK. */ - - if (!asharedmemory_create) - { - *(void **) (&asharedmemory_create) - = dlsym (RTLD_DEFAULT, "ASharedMemory_create"); - - if (!asharedmemory_create) + while (mark1) { - __android_log_print (ANDROID_LOG_FATAL, __func__, - "dlsym: %s\n", - strerror (errno)); - emacs_abort (); + mark = mark1; + mark1 = strchr (mark + 1, '?'); } } - fd = asharedmemory_create ("", sizeof test_buffer); - - if (fd < 0) - return false; - } - - /* Now map the resource and write the test contents. */ - - mem = mmap (NULL, sizeof test_buffer, PROT_WRITE, - MAP_SHARED, fd, 0); - if (mem == MAP_FAILED) - { - close (fd); - return false; - } - - /* Copy over the test contents. */ - memcpy (mem, test_buffer, sizeof test_buffer); - - /* Return anyway even if munmap fails. */ - munmap (mem, sizeof test_buffer); - - /* Try to read the content back into test_buffer. If this does not - compare equal to the original string, or the read fails, then - ashmem descriptors are not readable on this system. */ - - if ((read (fd, test_buffer, sizeof test_buffer) - != sizeof test_buffer) - || memcmp (test_buffer, "abcdefghi", sizeof test_buffer)) - { - __android_log_print (ANDROID_LOG_WARN, __func__, - "/dev/ashmem does not produce real" - " temporary files on this system, so" - " Emacs will fall back to creating" - " unlinked temporary files."); - close (fd); - return false; - } - - close (fd); - return true; -} - -/* Get a file descriptor backed by a temporary in-memory file for the - given asset. */ - -static int -android_hack_asset_fd (AAsset *asset) -{ - static bool ashmem_readable_p; - static bool ashmem_initialized; - int fd, rc; - unsigned char *mem; - size_t size; - - /* The first time this function is called, try to determine whether - or not ashmem file descriptors can be read from. */ - - if (!ashmem_initialized) - ashmem_readable_p - = android_detect_ashmem (); - ashmem_initialized = true; - - /* If it isn't, fall back. */ - - if (!ashmem_readable_p) - return android_hack_asset_fd_fallback (asset); - - /* Assets must be small enough to fit in size_t, if off_t is - larger. */ - size = AAsset_getLength (asset); - - /* Android 28 and earlier let Emacs access /dev/ashmem directly, so - prefer that over using ASharedMemory. */ - - if (android_api_level <= 28) - { - fd = open ("/dev/ashmem", O_RDWR); - - if (fd < 0) - return -1; - - /* An empty name means the memory area will exist until the file - descriptor is closed, because no other process can - attach. */ - rc = ioctl (fd, ASHMEM_SET_NAME, ""); - - if (rc < 0) + if (mark) { - __android_log_print (ANDROID_LOG_ERROR, __func__, - "ioctl ASHMEM_SET_NAME: %s", - strerror (errno)); - close (fd); - return -1; - } + /* First, encode the part leading to the question mark + character. */ - rc = ioctl (fd, ASHMEM_SET_SIZE, size); + rc = 0; + if (mark > token) + rc = android_url_encode (token, mark - token, + head, n + 1); - if (rc < 0) - { - __android_log_print (ANDROID_LOG_ERROR, __func__, - "ioctl ASHMEM_SET_SIZE: %s", - strerror (errno)); - close (fd); - return -1; - } + /* If this fails, bail out. */ - if (!size) - return fd; + if (rc < 0) + goto failure; - /* Now map the resource. */ - mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0); - if (mem == MAP_FAILED) - { - __android_log_print (ANDROID_LOG_ERROR, __func__, - "mmap: %s", strerror (errno)); - close (fd); - return -1; - } + /* Copy mark to the file name. */ - if (AAsset_read (asset, mem, size) != size) - { - /* Too little was read. Close the file descriptor and - report an error. */ - __android_log_print (ANDROID_LOG_ERROR, __func__, - "AAsset_read: %s", strerror (errno)); - close (fd); - return -1; - } + n -= rc, head += rc; + length = strlen (mark); - /* Return anyway even if munmap fails. */ - munmap (mem, size); - return fd; - } + if (n < length) + goto failure; - /* On the other hand, SELinux restrictions on Android 29 and later - require that Emacs use a system service to obtain shared memory. - Load this dynamically, as this service is not available on all - versions of the NDK. */ + strcpy (head, mark); - if (!asharedmemory_create) - { - *(void **) (&asharedmemory_create) - = dlsym (RTLD_DEFAULT, "ASharedMemory_create"); - - if (!asharedmemory_create) - { - __android_log_print (ANDROID_LOG_FATAL, __func__, - "dlsym: %s\n", - strerror (errno)); - emacs_abort (); + /* Now break out of the loop, since this is the last + component anyway. */ + break; } - } - - fd = asharedmemory_create ("", size); - - if (fd < 0) - { - __android_log_print (ANDROID_LOG_ERROR, __func__, - "ASharedMemory_create: %s", - strerror (errno)); - return -1; - } - - /* Now map the resource. */ - mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0); - if (mem == MAP_FAILED) - { - __android_log_print (ANDROID_LOG_ERROR, __func__, - "mmap: %s", strerror (errno)); - close (fd); - return -1; - } - - if (AAsset_read (asset, mem, size) != size) - { - /* Too little was read. Close the file descriptor and - report an error. */ - __android_log_print (ANDROID_LOG_ERROR, __func__, - "AAsset_read: %s", strerror (errno)); - close (fd); - return -1; - } - - /* Return anyway even if munmap fails. */ - munmap (mem, size); - return fd; -} - -/* Make FD close-on-exec. If any system call fails, do not abort, but - log a warning to the system log. */ - -static void -android_close_on_exec (int fd) -{ - int flags, rc; + else + /* Now encode this file name component into the buffer. */ + rc = android_url_encode (token, strlen (token), + head, n + 1); - flags = fcntl (fd, F_GETFD); + if (rc < 0) + goto failure; - if (flags < 0) - { - __android_log_print (ANDROID_LOG_WARN, __func__, - "fcntl: %s", strerror (errno)); - return; + head += rc; + token = next; } - rc = fcntl (fd, F_SETFD, flags | O_CLOEXEC); + /* buffer must have been null terminated by + `android_url_encode'. */ + xfree (copy); + return buffer; - if (rc < 0) - { - __android_log_print (ANDROID_LOG_WARN, __func__, - "fcntl: %s", strerror (errno)); - return; - } + failure: + xfree (copy); + return NULL; } -/* `open' and such are modified even though they exist on Android, - because Emacs treats "/assets/" as a special directory that must - contain all assets in the application package. */ +/* Return whether or not the specified FILENAME is an accessible + content URI. MODE specifies what to check. */ -int -android_open (const char *filename, int oflag, mode_t mode) +static bool +android_check_content_access (const char *filename, int mode) { const char *name; - AAsset *asset; - int fd; - size_t length; jobject string; + size_t length; + jboolean rc; - if (asset_manager && (name = android_get_asset_name (filename))) - { - /* If Emacs is trying to write to the file, return NULL. */ - - if (oflag & O_WRONLY || oflag & O_RDWR) - { - errno = EROFS; - return -1; - } - - if (oflag & O_DIRECTORY) - { - errno = ENOTSUP; - return -1; - } - - /* If given AASSET_MODE_BUFFER (which is what Emacs probably - does, given that a file descriptor is not always available), - the framework fails to uncompress the data before it returns - a file descriptor. */ - asset = AAssetManager_open (asset_manager, name, - AASSET_MODE_STREAMING); - - if (!asset) - { - errno = ENOENT; - return -1; - } - - /* Create a shared memory file descriptor containing the asset - contents. - - The documentation misleads people into thinking that - AAsset_openFileDescriptor does precisely this. However, it - instead returns an offset into any uncompressed assets in the - ZIP archive. This cannot be found in its documentation. */ - - fd = android_hack_asset_fd (asset); - - if (fd == -1) - { - AAsset_close (asset); - errno = ENXIO; - return -1; - } - - /* If O_CLOEXEC is specified, make the file descriptor close on - exec too. */ - if (oflag & O_CLOEXEC) - android_close_on_exec (fd); - - if (fd >= ANDROID_MAX_ASSET_FD || fd < 0) - { - /* Too bad. Pretend this is an out of memory error. */ - errno = ENOMEM; - - if (fd >= 0) - close (fd); - - fd = -1; - } - else - { - assert (!(android_table[fd].flags - & ANDROID_FD_TABLE_ENTRY_IS_VALID)); - android_table[fd].flags = ANDROID_FD_TABLE_ENTRY_IS_VALID; - memset (&android_table[fd].statb, 0, - sizeof android_table[fd].statb); - - /* Fill in some information that will be reported to - callers of android_fstat, among others. */ - android_table[fd].statb.st_mode - = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - - /* Owned by root. */ - android_table[fd].statb.st_uid = 0; - android_table[fd].statb.st_gid = 0; - - /* Size of the file. */ - android_table[fd].statb.st_size - = AAsset_getLength (asset); - } - - AAsset_close (asset); - return fd; - } - - if (android_init_gui && android_content_name_p (filename)) - { - /* This is a content:// URI. Ask the system for a descriptor to - that file. */ - - name = android_get_content_name (filename); - length = strlen (name); - - /* Check if the mode is valid. */ - - if (oflag & O_DIRECTORY) - { - errno = ENOTSUP; - return -1; - } - - /* Allocate a buffer to hold the file name. */ - string = (*android_java_env)->NewByteArray (android_java_env, - length); - if (!string) - { - (*android_java_env)->ExceptionClear (android_java_env); - errno = ENOMEM; - return -1; - } - (*android_java_env)->SetByteArrayRegion (android_java_env, - string, 0, length, - (jbyte *) name); - - /* Try to open the file descriptor. */ - - fd - = (*android_java_env)->CallIntMethod (android_java_env, - emacs_service, - service_class.open_content_uri, - string, - (jboolean) ((mode & O_WRONLY - || mode & O_RDWR) - != 0), - (jboolean) !(mode & O_WRONLY), - (jboolean) ((mode & O_TRUNC) - != 0)); - - if ((*android_java_env)->ExceptionCheck (android_java_env)) - { - (*android_java_env)->ExceptionClear (android_java_env); - errno = ENOMEM; - ANDROID_DELETE_LOCAL_REF (string); - return -1; - } - - /* If fd is -1, just assume that the file does not exist, - and return -1 with errno set to ENOENT. */ - - if (fd == -1) - { - errno = ENOENT; - goto skip; - } - - if (mode & O_CLOEXEC) - android_close_on_exec (fd); - - skip: - ANDROID_DELETE_LOCAL_REF (string); - return fd; - } - - return open (filename, oflag, mode); -} - -/* Like close. However, remove the file descriptor from the asset - table as well. */ - -int -android_close (int fd) -{ - if (fd < ANDROID_MAX_ASSET_FD) - android_table[fd].flags = 0; - - return close (fd); -} - -/* Like fclose. However, remove any information associated with - FILE's file descriptor from the asset table as well. */ - -int -android_fclose (FILE *stream) -{ - int fd; + name = android_get_content_name (filename); + length = strlen (name); - fd = fileno (stream); + string = (*android_java_env)->NewByteArray (android_java_env, + length); + android_exception_check (); - if (fd != -1 && fd < ANDROID_MAX_ASSET_FD) - android_table[fd].flags = 0; + (*android_java_env)->SetByteArrayRegion (android_java_env, + string, 0, length, + (jbyte *) name); + rc = (*android_java_env)->CallBooleanMethod (android_java_env, + emacs_service, + service_class.check_content_uri, + string, + (jboolean) ((mode & R_OK) + != 0), + (jboolean) ((mode & W_OK) + != 0)); + android_exception_check_1 (string); + ANDROID_DELETE_LOCAL_REF (string); - return fclose (stream); + return rc; } +#endif /* 0 */ + /* Return the current user's ``home'' directory, which is actually the app data directory on Android. */ @@ -2015,7 +1199,6 @@ android_create_lib_link (void) char *filename; char lib_directory[PATH_MAX]; int fd; - struct stat statb; /* Find the directory containing the files directory. */ filename = dirname (android_files_dir); @@ -2105,16 +1288,8 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, int pipefd[2]; pthread_t thread; const char *java_string; - AAsset *asset; - /* This may be called from multiple threads. setEmacsParams should - only ever be called once. */ - if (__atomic_fetch_add (&emacs_initialized, -1, __ATOMIC_SEQ_CST)) - { - ANDROID_THROW (env, "java/lang/IllegalArgumentException", - "Emacs was already initialized!"); - return; - } + /* This function should only be called from the main thread. */ android_pixel_density_x = pixel_density_x; android_pixel_density_y = pixel_density_y; @@ -2124,40 +1299,6 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, "Initializing "PACKAGE_STRING"...\nPlease report bugs to " PACKAGE_BUGREPORT". Thanks.\n"); - /* Set the asset manager. */ - asset_manager = AAssetManager_fromJava (env, local_asset_manager); - - /* Initialize the directory tree. */ - asset = AAssetManager_open (asset_manager, "directory-tree", - AASSET_MODE_BUFFER); - - if (!asset) - { - __android_log_print (ANDROID_LOG_FATAL, __func__, - "Failed to open directory tree"); - emacs_abort (); - } - - directory_tree = AAsset_getBuffer (asset); - - if (!directory_tree) - emacs_abort (); - - /* Now figure out how big the directory tree is, and compare the - first few bytes. */ - directory_tree_size = AAsset_getLength (asset); - if (directory_tree_size < 5 - || memcmp (directory_tree, "EMACS", 5)) - { - __android_log_print (ANDROID_LOG_FATAL, __func__, - "Directory tree has bad magic"); - emacs_abort (); - } - - /* Hold a VM reference to the asset manager to prevent the native - object from being deleted. */ - (*env)->NewGlobalRef (env, local_asset_manager); - if (emacs_service_object) { /* Create a pipe and duplicate it to stdout and stderr. Next, @@ -2304,7 +1445,10 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, /* Set up events. */ android_init_events (); - /* OK, setup is now complete. The caller may start the Emacs thread + /* Set up the Android virtual filesystem layer. */ + android_vfs_init (env, local_asset_manager); + + /* OK, setup is now complete. The caller may call initEmacs now. */ } @@ -2405,6 +1549,33 @@ #define FIND_METHOD(c_name, name, signature) \ "Landroid/view/inputmethod/ExtractedText;I)V"); FIND_METHOD (update_cursor_anchor_info, "updateCursorAnchorInfo", "(Lorg/gnu/emacs/EmacsWindow;FFFF)V"); + FIND_METHOD (get_document_authorities, "getDocumentAuthorities", + "()[Ljava/lang/String;"); + FIND_METHOD (request_directory_access, "requestDirectoryAccess", + "()I"); + FIND_METHOD (get_document_trees, "getDocumentTrees", + "([B)[Ljava/lang/String;"); + FIND_METHOD (document_id_from_name, "documentIdFromName", + "(Ljava/lang/String;[B[Ljava/lang/String;)I"); + FIND_METHOD (get_tree_uri, "getTreeUri", + "(Ljava/lang/String;Ljava/lang/String;)" + "Ljava/lang/String;"); + FIND_METHOD (stat_document, "statDocument", + "(Ljava/lang/String;Ljava/lang/String;)[J"); + FIND_METHOD (access_document, "accessDocument", + "(Ljava/lang/String;Ljava/lang/String;Z)I"); + FIND_METHOD (open_document_directory, "openDocumentDirectory", + "(Ljava/lang/String;Ljava/lang/String;)" + "Landroid/database/Cursor;"); + FIND_METHOD (read_directory_entry, "readDirectoryEntry", + "(Landroid/database/Cursor;)Lorg/gnu/emacs/" + "EmacsDirectoryEntry;"); + FIND_METHOD (open_document, "openDocument", + "(Ljava/lang/String;Ljava/lang/String;ZZ)" + "Landroid/os/ParcelFileDescriptor;"); + FIND_METHOD (create_document, "createDocument", + "(Ljava/lang/String;Ljava/lang/String;" + "Ljava/lang/String;)Ljava/lang/String;"); #undef FIND_METHOD } @@ -6255,329 +5426,6 @@ android_toggle_on_screen_keyboard (android_window window, bool show) -/* Like faccessat, except it also understands DIRFD opened using - android_dirfd. */ - -int -android_faccessat (int dirfd, const char *pathname, int mode, int flags) -{ - const char *asset; - - if (dirfd != AT_FDCWD) - dirfd - = android_lookup_asset_directory_fd (dirfd, &pathname, - pathname); - - /* Check if pathname is actually an asset. If that is the case, - simply fall back to android_file_access_p. */ - - if (dirfd == AT_FDCWD - && asset_manager - && (asset = android_get_asset_name (pathname))) - { - if (android_file_access_p (asset, mode)) - return 0; - - /* Set errno to an appropriate value. */ - errno = ENOENT; - return 1; - } - - /* Check if pathname is actually a content resolver URI. */ - - if (dirfd == AT_FDCWD - && android_init_gui - && android_content_name_p (pathname)) - { - if (android_check_content_access (pathname, mode)) - return 0; - - /* Set errno to an appropriate value. */ - errno = ENOENT; - return 1; - } - -#if __ANDROID_API__ >= 16 - /* When calling `faccessat', make sure to clear the flag AT_EACCESS. - - Android's faccessat simply fails if FLAGS contains AT_EACCESS, so - replace it with zero here. This isn't caught at configuration-time - as Emacs is being cross compiled. - - This takes place only when building for Android 16 and later, - because earlier versions use a Gnulib replacement that lacks these - issues. */ - - return faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS); -#else /* __ANDROID_API__ < 16 */ - return faccessat (dirfd, pathname, mode, flags); -#endif /* __ANDROID_API__ >= 16 */ -} - - - -/* Directory listing emulation. */ - -struct android_dir -{ - /* The real DIR *, if it exists. */ - DIR *dir; - - /* Otherwise, the pointer to the directory in directory_tree. */ - char *asset_dir; - - /* And the end of the files in asset_dir. */ - char *asset_limit; - - /* The next struct android_dir. */ - struct android_dir *next; - - /* Path to the directory relative to /. */ - char *asset_file; - - /* File descriptor used when asset_dir is set. */ - int fd; -}; - -/* List of all struct android_dir's corresponding to an asset - directory that are currently open. */ -static struct android_dir *android_dirs; - -/* Like opendir. However, return an asset directory if NAME points to - an asset. */ - -struct android_dir * -android_opendir (const char *name) -{ - struct android_dir *dir; - char *asset_dir; - const char *asset_name; - size_t limit, length; - - asset_name = android_get_asset_name (name); - - /* If the asset manager exists and NAME is an asset, return an asset - directory. */ - if (asset_manager && asset_name) - { - asset_dir - = (char *) android_scan_directory_tree ((char *) asset_name, - &limit); - - if (!asset_dir) - { - errno = ENOENT; - return NULL; - } - - length = strlen (name); - - dir = xmalloc (sizeof *dir); - dir->dir = NULL; - dir->asset_dir = asset_dir; - dir->asset_limit = (char *) directory_tree + limit; - dir->fd = -1; - dir->asset_file = xzalloc (length + 2); - - /* Make sure dir->asset_file is terminated with /. */ - strcpy (dir->asset_file, name); - if (dir->asset_file[length - 1] != '/') - dir->asset_file[length] = '/'; - - /* Make sure dir->asset_limit is within bounds. It is a limit, - and as such can be exactly one byte past directory_tree. */ - if (dir->asset_limit > directory_tree + directory_tree_size) - { - xfree (dir); - __android_log_print (ANDROID_LOG_VERBOSE, __func__, - "Invalid dir tree, limit %zu, size %zu\n", - limit, directory_tree_size); - dir = NULL; - errno = EACCES; - } - - dir->next = android_dirs; - android_dirs = dir; - - return dir; - } - - /* Otherwise, open the directory normally. */ - dir = xmalloc (sizeof *dir); - dir->asset_dir = NULL; - dir->dir = opendir (name); - - if (!dir->dir) - { - xfree (dir); - return NULL; - } - - return dir; -} - -/* Like dirfd. However, value is not a real directory file descriptor - if DIR is an asset directory. */ - -int -android_dirfd (struct android_dir *dirp) -{ - int fd; - - if (dirp->dir) - return dirfd (dirp->dir); - else if (dirp->fd != -1) - return dirp->fd; - - fd = open ("/dev/null", O_RDONLY | O_CLOEXEC); - - /* Record this file descriptor in dirp. */ - dirp->fd = fd; - return fd; -} - -/* Like readdir, except it understands asset directories. */ - -struct dirent * -android_readdir (struct android_dir *dir) -{ - static struct dirent dirent; - const char *last; - - if (dir->asset_dir) - { - /* There are no more files to read. */ - if (dir->asset_dir >= dir->asset_limit) - return NULL; - - /* Otherwise, scan forward looking for the next NULL byte. */ - last = memchr (dir->asset_dir, 0, - dir->asset_limit - dir->asset_dir); - - /* No more NULL bytes remain. */ - if (!last) - return NULL; - - /* Forward last past the NULL byte. */ - last++; - - /* Make sure it is still within the directory tree. */ - if (last >= directory_tree + directory_tree_size) - return NULL; - - /* Now, fill in the dirent with the name. */ - memset (&dirent, 0, sizeof dirent); - dirent.d_ino = 0; - dirent.d_off = 0; - dirent.d_reclen = sizeof dirent; - - /* If this is not a directory, return DT_UNKNOWN. Otherwise, - return DT_DIR. */ - - if (android_is_directory (dir->asset_dir)) - dirent.d_type = DT_DIR; - else - dirent.d_type = DT_UNKNOWN; - - /* Note that dir->asset_dir is actually a NULL terminated - string. */ - memcpy (dirent.d_name, dir->asset_dir, - MIN (sizeof dirent.d_name, - last - dir->asset_dir)); - dirent.d_name[sizeof dirent.d_name - 1] = '\0'; - - /* Strip off the trailing slash, if any. */ - if (dirent.d_name[MIN (sizeof dirent.d_name, - last - dir->asset_dir) - - 2] == '/') - dirent.d_name[MIN (sizeof dirent.d_name, - last - dir->asset_dir) - - 2] = '\0'; - - /* Finally, forward dir->asset_dir to the file past last. */ - dir->asset_dir = ((char *) directory_tree - + android_extract_long ((char *) last)); - - return &dirent; - } - - return readdir (dir->dir); -} - -/* Like closedir, but it also closes asset manager directories. */ - -void -android_closedir (struct android_dir *dir) -{ - struct android_dir **next, *tem; - - if (dir->dir) - closedir (dir->dir); - else - { - if (dir->fd != -1) - close (dir->fd); - - /* Unlink this directory from the list of all asset manager - directories. */ - - for (next = &android_dirs; (tem = *next);) - { - if (tem == dir) - *next = dir->next; - else - next = &(*next)->next; - } - - /* Free the asset file name. */ - xfree (dir->asset_file); - } - - /* There is no need to close anything else, as the directory tree - lies in statically allocated memory. */ - - xfree (dir); -} - -/* Subroutine used by android_fstatat and android_faccessat. If DIRFD - belongs to an open asset directory and FILE is a relative file - name, then return AT_FDCWD and the absolute file name of the - directory prepended to FILE in *PATHNAME. Else, return DIRFD. */ - -int -android_lookup_asset_directory_fd (int dirfd, - const char *restrict *pathname, - const char *restrict file) -{ - struct android_dir *dir; - static char *name; - - if (file[0] == '/') - return dirfd; - - for (dir = android_dirs; dir; dir = dir->next) - { - if (dir->fd == dirfd && dirfd != -1) - { - if (name) - xfree (name); - - /* dir->asset_file is always separator terminated. */ - name = xzalloc (strlen (dir->asset_file) - + strlen (file) + 1); - strcpy (name, dir->asset_file); - strcpy (name + strlen (dir->asset_file), - file); - *pathname = name; - return AT_FDCWD; - } - } - - return dirfd; -} - - - /* emacs_abort implementation for Android. This logs a stack trace. */ @@ -6787,6 +5635,28 @@ android_exception_check_2 (jobject object, jobject object1) memory_full (0); } +/* Like android_exception_check_2, except it takes more than two local + reference arguments. */ + +void +android_exception_check_3 (jobject object, jobject object1, + jobject object2) +{ + if (likely (!(*android_java_env)->ExceptionCheck (android_java_env))) + return; + + __android_log_print (ANDROID_LOG_WARN, __func__, + "Possible out of memory error. " + " The Java exception follows: "); + /* Describe exactly what went wrong. */ + (*android_java_env)->ExceptionDescribe (android_java_env); + (*android_java_env)->ExceptionClear (android_java_env); + ANDROID_DELETE_LOCAL_REF (object); + ANDROID_DELETE_LOCAL_REF (object1); + ANDROID_DELETE_LOCAL_REF (object2); + memory_full (0); +} + /* Check for JNI problems based on the value of OBJECT. Signal out of memory if OBJECT is NULL. OBJECT1 means the @@ -7160,7 +6030,7 @@ android_restart_emacs (void) turn which APIs Emacs can safely use. */ int -android_get_current_api_level (void) +(android_get_current_api_level) (void) { return android_api_level; } @@ -7206,30 +6076,25 @@ android_query_battery (struct android_battery_state *status) return 0; } -/* Display a small momentary notification on screen containing - TEXT, which must be in the modified UTF encoding used by the - JVM. */ +/* Display a file panel and grant Emacs access to the SAF directory + within it. Value is 1 upon failure and 0 upon success (which only + indicates that the panel has been displayed successfully; the panel + may still be dismissed without a file being selected.) */ -void -android_display_toast (const char *text) +int +android_request_directory_access (void) { - jstring string; + jint rc; + jmethodID method; - /* Make the string. */ - string = (*android_java_env)->NewStringUTF (android_java_env, - text); + method = service_class.request_directory_access; + rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env, + emacs_service, + service_class.class, + method); android_exception_check (); - /* Display the toast. */ - (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, - emacs_service, - service_class.class, - service_class.display_toast, - string); - android_exception_check_1 (string); - - /* Delete the local reference to the string. */ - ANDROID_DELETE_LOCAL_REF (string); + return rc; } @@ -7709,156 +6574,6 @@ android_set_fullscreen (android_window window, bool fullscreen) -/* External asset management interface. By using functions here - to read and write from files, Emacs can avoid opening a - shared memory file descriptor for each ``asset'' file. */ - -/* Like android_open. However, return a structure that can - either directly hold an AAsset or a file descriptor. - - Value is the structure upon success. Upon failure, value - consists of an uninitialized file descriptor, but its asset - field is set to -1, and errno is set accordingly. */ - -struct android_fd_or_asset -android_open_asset (const char *filename, int oflag, mode_t mode) -{ - const char *name; - struct android_fd_or_asset fd; - AAsset *asset; - - /* Initialize FD by setting its asset to an invalid - pointer. */ - fd.asset = (void *) -1; - - /* See if this is an asset. */ - - if (asset_manager && (name = android_get_asset_name (filename))) - { - /* Return failure for unsupported flags. */ - - if (oflag & O_WRONLY || oflag & O_RDWR) - { - errno = EROFS; - return fd; - } - - if (oflag & O_DIRECTORY) - { - errno = ENOTSUP; - return fd; - } - - /* Now try to open the asset. */ - asset = AAssetManager_open (asset_manager, name, - AASSET_MODE_STREAMING); - - if (!asset) - { - errno = ENOENT; - return fd; - } - - /* Return the asset. */ - fd.asset = asset; - return fd; - } - - /* If the file is not an asset, fall back to android_open and - get a regular file descriptor. */ - - fd.fd = android_open (filename, oflag, mode); - if (fd.fd < 0) - return fd; - - /* Set fd.asset to NULL, signifying that it is a file - descriptor. */ - fd.asset = NULL; - return fd; -} - -/* Like android_close. However, it takes a ``file descriptor'' - opened using android_open_asset. */ - -int -android_close_asset (struct android_fd_or_asset asset) -{ - if (!asset.asset) - return android_close (asset.fd); - - AAsset_close (asset.asset); - return 0; -} - -/* Like `emacs_read_quit'. However, it handles file descriptors - opened using `android_open_asset' as well. */ - -ssize_t -android_asset_read_quit (struct android_fd_or_asset asset, - void *buffer, size_t size) -{ - if (!asset.asset) - return emacs_read_quit (asset.fd, buffer, size); - - /* It doesn't seem possible to quit from inside AAsset_read, - sadly. */ - return AAsset_read (asset.asset, buffer, size); -} - -/* Like `read'. However, it handles file descriptors opened - using `android_open_asset' as well. */ - -ssize_t -android_asset_read (struct android_fd_or_asset asset, - void *buffer, size_t size) -{ - if (!asset.asset) - return read (asset.fd, buffer, size); - - /* It doesn't seem possible to quit from inside AAsset_read, - sadly. */ - return AAsset_read (asset.asset, buffer, size); -} - -/* Like `lseek', but it handles ``file descriptors'' opened with - android_open_asset. */ - -off_t -android_asset_lseek (struct android_fd_or_asset asset, off_t off, - int whence) -{ - if (!asset.asset) - return lseek (asset.fd, off, whence); - - return AAsset_seek (asset.asset, off, whence); -} - -/* Like `fstat'. */ - -int -android_asset_fstat (struct android_fd_or_asset asset, - struct stat *statb) -{ - if (!asset.asset) - return fstat (asset.fd, statb); - - /* Clear statb. */ - memset (statb, 0, sizeof *statb); - - /* Set the mode. */ - statb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; - - /* Owned by root. */ - statb->st_uid = 0; - statb->st_gid = 0; - - /* Size of the file. */ - statb->st_size = AAsset_getLength (asset.asset); - return 0; -} - - - /* Window cursor support. */ android_cursor @@ -8088,4 +6803,4 @@ android_project_image_nearest (struct android_image *image, emacs_abort (); } -#endif +#endif /* !ANDROID_STUBIFY */ diff --git a/src/android.h b/src/android.h index 2323690fa25..94a3ad46e74 100644 --- a/src/android.h +++ b/src/android.h @@ -36,26 +36,46 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include "androidgui.h" #include "lisp.h" -#endif +#endif /* ANDROID_STUBIFY */ extern bool android_init_gui; #ifndef ANDROID_STUBIFY +extern char *android_cache_dir; + extern int android_emacs_init (int, char **, char *); extern int android_select (int, fd_set *, fd_set *, fd_set *, struct timespec *); - -extern int android_open (const char *, int, mode_t); extern char *android_user_full_name (struct passwd *); + + + +/* File I/O operations. Many of these are defined in + androidvfs.c. */ + extern const char *android_is_special_directory (const char *, const char *); +extern const char *android_get_home_directory (void); + +extern void android_vfs_init (JNIEnv *, jobject); + +extern int android_open (const char *, int, mode_t); extern int android_fstat (int, struct stat *); extern int android_fstatat (int, const char *restrict, struct stat *restrict, int); extern int android_faccessat (int, const char *, int, int); extern int android_close (int); +extern FILE *android_fdopen (int, const char *); extern int android_fclose (FILE *); -extern const char *android_get_home_directory (void); +extern int android_unlink (const char *); +extern int android_symlink (const char *, const char *); +extern int android_rmdir (const char *); +extern int android_mkdir (const char *, mode_t); +extern int android_renameat_noreplace (int, const char *, + int, const char *); +extern int android_rename (const char *, const char *); + + extern double android_pixel_density_x, android_pixel_density_y; extern double android_scaled_pixel_density; @@ -89,6 +109,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. extern void android_exception_check (void); extern void android_exception_check_1 (jobject); extern void android_exception_check_2 (jobject, jobject); +extern void android_exception_check_3 (jobject, jobject, jobject); extern void android_exception_check_nonnull (void *, jobject); extern void android_exception_check_nonnull_1 (void *, jobject, jobject); @@ -96,18 +117,29 @@ Copyright (C) 2023 Free Software Foundation, Inc. extern void android_wait_event (void); extern void android_toggle_on_screen_keyboard (android_window, bool); extern _Noreturn void android_restart_emacs (void); -extern int android_get_current_api_level (void); +extern int android_request_directory_access (void); +extern int android_get_current_api_level (void) + __attribute__ ((pure)); + +/* Define `android_get_current_api_level' to a macro that the compiler + knows will always return at least __ANDROID_API__. */ + +#define android_get_current_api_level() \ + ({ int value; \ + \ + value = (android_get_current_api_level) (); \ + eassume (value >= __ANDROID_API__); value; }) /* Directory listing emulation. */ -struct android_dir; +struct android_vdir; -extern struct android_dir *android_opendir (const char *); -extern int android_dirfd (struct android_dir *); -extern struct dirent *android_readdir (struct android_dir *); -extern void android_closedir (struct android_dir *); +extern struct android_vdir *android_opendir (const char *); +extern int android_dirfd (struct android_vdir *); +extern struct dirent *android_readdir (struct android_vdir *); +extern void android_closedir (struct android_vdir *); @@ -211,8 +243,52 @@ #define android_is_special_directory(name, dir) ((const char *) NULL) #ifndef ANDROID_STUBIFY #include +struct android_emacs_service +{ + jclass class; + jmethodID fill_rectangle; + jmethodID fill_polygon; + jmethodID draw_rectangle; + jmethodID draw_line; + jmethodID draw_point; + jmethodID clear_window; + jmethodID clear_area; + jmethodID ring_bell; + jmethodID query_tree; + jmethodID get_screen_width; + jmethodID get_screen_height; + jmethodID detect_mouse; + jmethodID name_keysym; + jmethodID browse_url; + jmethodID restart_emacs; + jmethodID update_ic; + jmethodID reset_ic; + jmethodID open_content_uri; + jmethodID check_content_uri; + jmethodID query_battery; + jmethodID update_extracted_text; + jmethodID update_cursor_anchor_info; + jmethodID get_document_authorities; + jmethodID request_directory_access; + jmethodID get_document_trees; + jmethodID document_id_from_name; + jmethodID get_tree_uri; + jmethodID stat_document; + jmethodID access_document; + jmethodID open_document_directory; + jmethodID read_directory_entry; + jmethodID open_document; + jmethodID create_document; +}; + extern JNIEnv *android_java_env; +/* The EmacsService object. */ +extern jobject emacs_service; + +/* Various methods associated with the EmacsService. */ +extern struct android_emacs_service service_class; + #define ANDROID_DELETE_LOCAL_REF(ref) \ ((*android_java_env)->DeleteLocalRef (android_java_env, \ (ref))) diff --git a/src/androidfns.c b/src/androidfns.c index dcc9ab83427..0270f58b6b9 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -3036,6 +3036,32 @@ DEFUN ("android-query-battery", Fandroid_query_battery, +/* Directory access requests. */ + +DEFUN ("android-request-directory-access", Fandroid_request_directory_access, + Sandroid_request_directory_access, 0, 0, "", + doc: /* Request access to a directory within external storage. +On Android 5.0 and later, prompt for a directory within external or +application storage, and grant access to it; some of these directories +cannot be accessed through the regular `/sdcard' filesystem. + +If access to the directory is granted, it will eventually appear +within the directory `/content/storage'. */) + (void) +{ + if (android_get_current_api_level () < 21) + error ("Emacs can only access application storage on" + " Android 5.0 and later"); + + if (!android_init_gui) + return Qnil; + + android_request_directory_access (); + return Qnil; +} + + + /* Miscellaneous input method related stuff. */ /* Report X, Y, by the phys cursor width and height as the cursor @@ -3062,7 +3088,7 @@ android_set_preeditarea (struct window *w, int x, int y) y + w->phys_cursor_height); } -#endif +#endif /* !ANDROID_STUBIFY */ @@ -3220,6 +3246,7 @@ syms_of_androidfns (void) defsubr (&Sx_server_version); #ifndef ANDROID_STUBIFY defsubr (&Sandroid_query_battery); + defsubr (&Sandroid_request_directory_access); tip_timer = Qnil; staticpro (&tip_timer); @@ -3235,5 +3262,5 @@ syms_of_androidfns (void) staticpro (&tip_dx); tip_dy = Qnil; staticpro (&tip_dy); -#endif +#endif /* !ANDROID_STUBIFY */ } diff --git a/src/androidvfs.c b/src/androidvfs.c new file mode 100644 index 00000000000..a32471d250e --- /dev/null +++ b/src/androidvfs.c @@ -0,0 +1,6137 @@ +/* Android virtual file-system support for GNU Emacs. + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "android.h" +#include "systime.h" + +#if __ANDROID_API__ >= 9 +#include +#include +#else /* __ANDROID_API__ < 9 */ +#include "android-asset.h" +#endif /* __ANDROID_API__ >= 9 */ + +#include + +/* This file implements support for the various special-purpose + directories found on Android systems. Such directories are not + mounted in the Unix virtual file-system, instead being accessible + through special API calls; Emacs pretends they are mounted at + specific folders within the root directory. + + There are presently two directories: /assets, granting access to + asset files stored within the APK, and /content, providing direct + access to content URIs (in Android 4.4 and later) and content + directory trees (in Android 5.0 and later.) + + This file implements substitutes for the C library `open', `fstat', + `close', `fclose', `unlink', `symlink', `rmdir', `rename', `stat' + system call wrappers, which process file names through ``VFS + nodes'' representing conceptual files, that are really no more than + tables of function pointers. + + The primary function of a node is to `name' children. This takes a + relative file name and returns a second VFS node tied to a child + that exists within this node (or a child thereof, ad infinite.) + + Other functions are also defined: functions to open file + descriptors, and substitutes for each of the C library system call + wrappers replaced. Each of these functions accepts two vnodes, and + is expected to otherwise behave like the C library system calls + replaced. + + When the virtual file system needs to locate the vnode associated + with a file name, it starts searching at the root vnode. Its + `name' function then creates vnodes as appropriate for the + components of the file name, which repeats recursively until the + vnode designating the file name is found. + + The substitute functions defined have two caveats, which however + don't prove problematic in an Emacs context: the first is that the + treatment of `..' is inconsistent with Unix, and has not really + been tested, while the second is that errno values do not always + conform to what the corresponding Unix system calls may return. */ + +/* Structure describing an array of VFS operations. */ + +struct android_vnode; + +struct android_vdir +{ + /* Return a `struct dirent' describing the next file in this + directory stream, or NULL if the stream has reached its end. */ + struct dirent *(*readdir) (struct android_vdir *); + + /* Close and release all resources allocated for this directory + stream. */ + void (*closedir) (struct android_vdir *); + + /* Return a ``file descriptor'' tied to this directory stream. */ + int (*dirfd) (struct android_vdir *); +}; + +struct android_vops +{ + /* Name a child of the given VFS node, which should be a + directory. + + LENGTH should be the length of NAME, excluding that of any + trailing NULL byte. + + NAME should be a normalized and NULL-terminated relative file + name; it may contain a leading separator characters, but no + consecutive ones. + + If NAME is empty, create another VFS node designating the same + file instead. + + NAME should also be located within writable storage; it may be + overwritten as the vnode sees fit. + + Value is a VFS node corresponding to the child, or NULL upon + failure. + + A VFS node may be returned even if NAME does not exist, the + expectation being that either a later filesystem operation will + fail, or will create the file. */ + struct android_vnode *(*name) (struct android_vnode *, char *, size_t); + + /* Open the specified VNODE, returning either a file descriptor or + an asset file descriptor. + + FLAGS and MODE mean the same as they do to the Unix `open' system + call. + + ASSET_P stipulates if an asset file descriptor may be returned; + if true, *ASSET may be set to an asset file descriptor. + + If an asset file descriptor is unavailable or ASSET_P is false, + *FD will be set to a file descriptor. + + If the vnode cannot be opened, value is -1 with errno set + accordingly. Otherwise, value is 0 if a file descriptor was + returned, and 1 if an asset file descriptor was returned. */ + int (*open) (struct android_vnode *, int, mode_t, bool, + int *, AAsset **); + + /* Close the specified VNODE, releasing all of its resources. + Save errno before making system calls that may set it, and + restore it to its original value before returning. + + This is unrelated to `android_close', which primarily releases on + stat buffers linked to file or asset file descriptors. */ + void (*close) (struct android_vnode *); + + /* Unlink the file and the specified VNODE. Value and errno are the + same as Unix `unlink'. */ + int (*unlink) (struct android_vnode *); + + /* Create a symlink from the specified VNODE to the target TARGET. + Value and errno are the same as `symlink' on Linux (which notably + means that errno is set to EPERM if VNODE doesn't support + symlinks.) */ + int (*symlink) (const char *, struct android_vnode *); + + /* Remove VNODE from its parent directory. VNODE must be an empty + directory. Value and errno are the same as Unix `rmdir'. */ + int (*rmdir) (struct android_vnode *); + + /* Move the file designated by SRC to DST, overwriting DST if + KEEP_EXISTING is false. + + If KEEP_EXISTING is true and DST already exists, value is -1 with + errno set to EEXIST. + + If VNODE does not natively support checking for a preexisting DST + and KEEP_EXISTING is true, value is -1 with errno set to ENOSYS. + + Value is otherwise the same as `rename'. */ + int (*rename) (struct android_vnode *, struct android_vnode *, bool); + + /* Return statistics for the specified VNODE. + Value and errno are the same as with Unix `stat'. */ + int (*stat) (struct android_vnode *, struct stat *); + + /* Return whether or not VNODE is accessible. + Value, errno and MODE are the same as with Unix `access'. */ + int (*access) (struct android_vnode *, int); + + /* Make a directory designated by VNODE, like Unix `mkdir'. */ + int (*mkdir) (struct android_vnode *, mode_t); + + /* Open the specified VNODE as a directory. + Value is a ``directory handle'', or NULL upon failure. */ + struct android_vdir *(*opendir) (struct android_vnode *); +}; + +struct android_vnode +{ + /* Operations associated with this vnode. */ + struct android_vops *ops; + + /* Type of this vnode and its flags. */ + short type, flags; +}; + +/* Structure describing a special named vnode relative to the root + vnode, or another directory vnode. */ + +struct android_special_vnode +{ + /* The name of the special file. */ + const char *name; + + /* The length of that name. */ + size_t length; + + /* Function called to create the initial vnode from the rest of the + component. */ + struct android_vnode *(*initial) (char *, size_t); +}; + +enum android_vnode_type + { + ANDROID_VNODE_UNIX, + ANDROID_VNODE_AFS, + ANDROID_VNODE_CONTENT, + ANDROID_VNODE_CONTENT_AUTHORITY, + ANDROID_VNODE_SAF_ROOT, + ANDROID_VNODE_SAF_TREE, + ANDROID_VNODE_SAF_FILE, + ANDROID_VNODE_SAF_NEW, + }; + + + +/* Structure describing the android.database.Cursor class. */ + +struct android_cursor_class +{ + jclass class; + jmethodID close; +}; + +/* Structure describing the EmacsDirectoryEntry class. */ + +struct emacs_directory_entry_class +{ + jclass class; + jfieldID d_type; + jfieldID d_name; +}; + +/* Structure describing the android.os.ParcelFileDescriptor class used + to wrap file descriptors sent over IPC. */ + +struct android_parcel_file_descriptor_class +{ + jclass class; + jmethodID close; + jmethodID get_fd; + jmethodID detach_fd; +}; + +/* The java.lang.String class. */ +static jclass java_string_class; + +/* Fields and methods associated with the Cursor class. */ +static struct android_cursor_class cursor_class; + +/* Fields and methods associated with the EmacsDirectoryEntry + class. */ +static struct emacs_directory_entry_class entry_class; + +/* Fields and methods associated with the ParcelFileDescriptor + class. */ +static struct android_parcel_file_descriptor_class fd_class; + +/* Initialize `cursor_class' using the given JNI environment ENV. + Calling this function is not necessary on Android 4.4 and + earlier. */ + +static void +android_init_cursor_class (JNIEnv *env) +{ + jclass old; + + cursor_class.class + = (*env)->FindClass (env, "android/database/Cursor"); + eassert (cursor_class.class); + + old = cursor_class.class; + cursor_class.class + = (jclass) (*env)->NewGlobalRef (env, (jobject) old); + (*env)->DeleteLocalRef (env, old); + + if (!cursor_class.class) + emacs_abort (); + +#define FIND_METHOD(c_name, name, signature) \ + cursor_class.c_name \ + = (*env)->GetMethodID (env, cursor_class.class, \ + name, signature); \ + assert (cursor_class.c_name); + FIND_METHOD (close, "close", "()V"); +#undef FIND_METHOD +} + +/* Initialize `entry_class' using the given JNI environment ENV. + Calling this function is not necessary on Android 4.4 and + earlier. */ + +static void +android_init_entry_class (JNIEnv *env) +{ + jclass old; + + entry_class.class + = (*env)->FindClass (env, "org/gnu/emacs/EmacsDirectoryEntry"); + eassert (entry_class.class); + + old = entry_class.class; + entry_class.class + = (jclass) (*env)->NewGlobalRef (env, (jobject) old); + (*env)->DeleteLocalRef (env, old); + + if (!entry_class.class) + emacs_abort (); + + entry_class.d_type = (*env)->GetFieldID (env, entry_class.class, + "d_type", "I"); + entry_class.d_name = (*env)->GetFieldID (env, entry_class.class, + "d_name", + "Ljava/lang/String;"); + assert (entry_class.d_type && entry_class.d_name); +} + + +/* Initialize `fd_class' using the given JNI environment ENV. Calling + this function is not necessary on Android 4.4 and earlier. */ + +static void +android_init_fd_class (JNIEnv *env) +{ + jclass old; + + fd_class.class + = (*env)->FindClass (env, "android/os/ParcelFileDescriptor"); + eassert (fd_class.class); + + old = fd_class.class; + fd_class.class + = (jclass) (*env)->NewGlobalRef (env, (jobject) old); + (*env)->DeleteLocalRef (env, old); + + if (!fd_class.class) + emacs_abort (); + +#define FIND_METHOD(c_name, name, signature) \ + fd_class.c_name \ + = (*env)->GetMethodID (env, fd_class.class, \ + name, signature); \ + assert (fd_class.c_name); + FIND_METHOD (close, "close", "()V"); + FIND_METHOD (get_fd, "getFd", "()I"); + FIND_METHOD (detach_fd, "detachFd", "()I"); +#undef FIND_METHOD +} + + + +/* Delete redundant instances of `.' and `..' from NAME in-place. + NAME must be *LENGTH long, excluding a mandatory trailing NULL + byte. + + Transform each directory component in NAME to avoid instances + of the `.' and `..' directories. For example, turn: + + a/../b/c/. + + into + + b/c/ + + and return NULL, writing the new length of NAME into *LENGTH. + + If there are more `..' components in NAME than there are normal + file name components, return NAME incremented to the position after + the first `..' component that cannot be transformed. For example, + if NAME is + + a/../../a + + value will be + + a + + If NAME is a directory separator and LENGTH is 1, return without + modifying NAME. In any other case, omit any leading directory + separator when writing to NAME. This is useful when a vnode that + can only be opened as a directory is desired, as this status is + made clear by suffixing the file name with a trailing + directory separator. */ + +static char * +android_vfs_canonicalize_name (char *name, size_t *length) +{ + size_t nellipsis, i; + char *last_component, *prev_component, *fill, *orig_name; + size_t size; + + /* Special case described in the last paragraph of the comment + above. */ + + size = *length; + orig_name = name; + + if (*name == '/' && size == 1) + return NULL; + else if (*name == '/') + size -= 1; + + nellipsis = 0; /* Number of ellipsis encountered within the current + file name component, or -1. */ + prev_component = NULL; /* Pointer to the separator character of + the component immediately before the + component currently being written. */ + last_component = name; /* Pointer to the separator character of + the component currently being read. */ + fill = name; /* Pointer to the next character that will be written + within NAME. */ + + /* Adjust name to skip the leading directory separator. But only + after fill is set. */ + if (*name == '/') + name++; + + for (i = 0; i < size; ++i) + { + switch (name[i]) + { + case '/': + /* See if the previous component was `..' or `.'. + + If it is .., and if no previous directory separator was + encountered, return or look up a vnode representing the + parent. */ + + if (nellipsis == 2) + { + /* .. */ + + if (!prev_component) + goto parent_vnode; + + /* Return to the last component. */ + fill = prev_component; + + /* Restore last_component to prev_component, and + prev_component back to the component before that. */ + last_component = prev_component; + + if (last_component != name) + prev_component = memrchr (name, '/', + last_component - name - 1); + else + prev_component = NULL; + + /* prev_component may now be NULL. If last_component is + the same as NAME, then fill has really been returned + to the beginning of the string, so leave it be. But + if it's something else, then it must be the first + separator character in the string, so set + prev_component to NAME itself. */ + + if (!prev_component && last_component != name) + prev_component = name; + } + else if (nellipsis == 1) + /* If it's ., return to this component. */ + fill = last_component; + else + { + /* Record the position of the last directory separator, + so NAME can be overwritten from there onwards if `..' + or `.' are encountered. */ + prev_component = last_component; + last_component = fill; + } + + /* Allow tracking ellipses again. */ + nellipsis = 0; + break; + + case '.': + if (nellipsis != -1) + nellipsis++; + break; + + default: + nellipsis = -1; + break; + } + + /* Now copy this character over from NAME. */ + *fill++ = name[i]; + } + + /* See if the previous component was `..' or `.'. + + If it is .., and if no previous directory separator was + encountered, return or look up a vnode representing the + parent. */ + + if (nellipsis == 2) + { + /* .. */ + + if (!prev_component) + /* Look up the rest of the vnode in its parent. */ + goto parent_vnode; + + /* Return to the last component. */ + fill = prev_component; + nellipsis = -2; + } + else if (nellipsis == 1) + { + /* If it's ., return to this component. */ + fill = last_component; + nellipsis = -2; + } + + /* Now, if there's enough room and an ellipsis file name was the + last component of END, append a trailing `/' before NULL + terminating it, indicating that the file name must be a + directory. */ + + if (fill + 1 < name + size && nellipsis == -2) + *fill++ = '/'; + + /* NULL terminate fill. */ + *fill = '\0'; + *length = fill - orig_name; + return NULL; + + parent_vnode: + /* .. was encountered and the parent couldn't be found through + stripping off preceding components. + + Find the parent vnode and name the rest of NAME starting from + there. */ + return name + i; +} + + + +/* Unix vnode implementation. These VFS nodes directly wrap around + the Unix filesystem, with the exception of the root vnode. */ + +struct android_unix_vnode +{ + /* The vnode data itself. */ + struct android_vnode vnode; + + /* Length of the name without a trailing null byte. */ + size_t name_length; + + /* Name of the vnode. */ + char *name; +}; + +struct android_unix_vdir +{ + /* The directory function table. */ + struct android_vdir vdir; + + /* The directory stream. */ + DIR *directory; +}; + +/* The vnode representing the root filesystem. */ +static struct android_unix_vnode root_vnode; + +static struct android_vnode *android_unix_name (struct android_vnode *, + char *, size_t); +static int android_unix_open (struct android_vnode *, int, + mode_t, bool, int *, AAsset **); +static void android_unix_close (struct android_vnode *); +static int android_unix_unlink (struct android_vnode *); +static int android_unix_symlink (const char *, struct android_vnode *); +static int android_unix_rmdir (struct android_vnode *); +static int android_unix_rename (struct android_vnode *, + struct android_vnode *, bool); +static int android_unix_stat (struct android_vnode *, struct stat *); +static int android_unix_access (struct android_vnode *, int); +static int android_unix_mkdir (struct android_vnode *, mode_t); +static struct android_vdir *android_unix_opendir (struct android_vnode *); + +/* Vector of VFS operations associated with Unix filesystem VFS + nodes. */ + +static struct android_vops unix_vfs_ops = + { + android_unix_name, + android_unix_open, + android_unix_close, + android_unix_unlink, + android_unix_symlink, + android_unix_rmdir, + android_unix_rename, + android_unix_stat, + android_unix_access, + android_unix_mkdir, + android_unix_opendir, + }; + +static struct android_vnode * +android_unix_name (struct android_vnode *vnode, char *name, + size_t length) +{ + struct android_unix_vnode *vp, *input, temp; + char *fill, *remainder; + size_t j; + + /* Canonicalize NAME. */ + input = (struct android_unix_vnode *) vnode; + remainder = android_vfs_canonicalize_name (name, &length); + + /* If remainder is set, it's a name relative to the parent + vnode. */ + if (remainder) + goto parent_vnode; + + /* Create a new unix vnode. */ + vp = xmalloc (sizeof *vp); + + /* If name is empty, duplicate the current vnode. */ + + if (length < 1) + { + memcpy (vp, vnode, sizeof *vp); + vp->name = xstrdup (vp->name); + return &vp->vnode; + } + + /* Otherwise, fill in the vnode. */ + + vp->vnode.ops = &unix_vfs_ops; + vp->vnode.type = ANDROID_VNODE_UNIX; + vp->vnode.flags = 0; + + /* Generate the new name of the vnode. Remove any trailing slash + from vp->name. */ + + vp->name_length = input->name_length + length; + vp->name = xmalloc (vp->name_length + 2); + + /* Copy the parent name over. */ + fill = mempcpy (vp->name, input->name, input->name_length); + + /* Check if it contains a trailing slash. input->name cannot be + empty, as the root vnode's name is `/'. */ + + if (fill[-1] != '/' && *name != '/') + /* If not, append a trailing slash and adjust vp->name_length + correspondingly. */ + *fill++ = '/', vp->name_length++; + else if (fill[-1] == '/' && *name == '/') + /* If name has a leading slash and fill does too, move fill + backwards so that name's slash will override that of fill. */ + fill--, vp->name_length--; + + /* Now copy NAME. */ + fill = mempcpy (fill, name, length); + + /* And NULL terminate fill. */ + *fill = '\0'; + return &vp->vnode; + + parent_vnode: + /* .. was encountered and the parent couldn't be found through + stripping off preceding components. + + Find the parent vnode and name the rest of NAME starting from + there. */ + + if (input->name_length == 1) + /* This is the vnode representing the root directory; just look + within itself... */ + vnode = &root_vnode.vnode; + else + { + /* Create a temporary asset vnode within the parent and use it + instead. First, establish the length of vp->name before its + last component. */ + + for (j = input->name_length - 1; j; --j) + { + if (input->name[j - 1] == '/') + break; + } + + /* There must be at least one leading directory separator in an + asset vnode's `name' field. */ + + if (!j) + abort (); + + /* j is now the length of the string minus the size of its last + component. Create a temporary vnode with that as its + name. */ + + temp.vnode.ops = &unix_vfs_ops; + temp.vnode.type = ANDROID_VNODE_UNIX; + temp.vnode.flags = 0; + temp.name_length = j; + temp.name = xmalloc (j + 1); + fill = mempcpy (temp.name, input->name, j); + *fill = '\0'; + + /* Search for the remainder of NAME relative to its parent. */ + vnode = android_unix_name (&temp.vnode, remainder, + strlen (remainder)); + xfree (temp.name); + return vnode; + } + + return (*vnode->ops->name) (vnode, remainder, strlen (remainder)); +} + +/* Create a Unix vnode representing the given file NAME. Use this + function to create vnodes that aren't rooted in the root VFS + node. */ + +static struct android_vnode * +android_unix_vnode (const char *name) +{ + struct android_unix_vnode *vp; + + vp = xmalloc (sizeof *vp); + vp->vnode.ops = &unix_vfs_ops; + vp->vnode.type = ANDROID_VNODE_UNIX; + vp->vnode.flags = 0; + vp->name_length = strlen (name); + vp->name = xstrdup (name); + return &vp->vnode; +} + +static int +android_unix_open (struct android_vnode *vnode, int flags, + mode_t mode, bool asset_p, int *fd, + AAsset **asset) +{ + struct android_unix_vnode *vp; + int fds; + + vp = (struct android_unix_vnode *) vnode; + fds = open (vp->name, flags, mode); + + if (fds < 0) + return -1; + + *fd = fds; + return 0; +} + +static void +android_unix_close (struct android_vnode *vnode) +{ + struct android_unix_vnode *vp; + int save_errno; + + save_errno = errno; + vp = (struct android_unix_vnode *) vnode; + xfree (vp->name); + xfree (vp); + errno = save_errno; +} + +static int +android_unix_unlink (struct android_vnode *vnode) +{ + struct android_unix_vnode *vp; + + vp = (struct android_unix_vnode *) vnode; + return unlink (vp->name); +} + +static int +android_unix_symlink (const char *target, struct android_vnode *vnode) +{ + struct android_unix_vnode *vp; + + vp = (struct android_unix_vnode *) vnode; + return symlink (target, vp->name); +} + +static int +android_unix_rmdir (struct android_vnode *vnode) +{ + struct android_unix_vnode *vp; + + vp = (struct android_unix_vnode *) vnode; + return rmdir (vp->name); +} + +static int +android_unix_rename (struct android_vnode *src, + struct android_vnode *dst, + bool keep_existing) +{ + struct android_unix_vnode *vp, *dest; + + if (src->type != dst->type) + { + /* If the types of both vnodes differ, complain that they're on + two different filesystems (which is correct from a abstract + viewpoint.) */ + errno = EXDEV; + return -1; + } + + vp = (struct android_unix_vnode *) src; + dest = (struct android_unix_vnode *) dst; + + return (keep_existing + ? renameat_noreplace (AT_FDCWD, vp->name, + AT_FDCWD, dest->name) + : rename (vp->name, dest->name)); +} + +static int +android_unix_stat (struct android_vnode *vnode, struct stat *statb) +{ + struct android_unix_vnode *vp; + + vp = (struct android_unix_vnode *) vnode; + return stat (vp->name, statb); +} + +static int +android_unix_access (struct android_vnode *vnode, int mode) +{ + struct android_unix_vnode *vp; + + vp = (struct android_unix_vnode *) vnode; + return access (vp->name, mode); +} + +static int +android_unix_mkdir (struct android_vnode *vnode, mode_t mode) +{ + struct android_unix_vnode *vp; + + vp = (struct android_unix_vnode *) vnode; + return mkdir (vp->name, mode); +} + +static struct dirent * +android_unix_readdir (struct android_vdir *vdir) +{ + struct android_unix_vdir *dir; + + dir = (struct android_unix_vdir *) vdir; + return readdir (dir->directory); +} + +static void +android_unix_closedir (struct android_vdir *vdir) +{ + struct android_unix_vdir *dir; + + dir = (struct android_unix_vdir *) vdir; + closedir (dir->directory); + xfree (vdir); +} + +static int +android_unix_dirfd (struct android_vdir *vdir) +{ + struct android_unix_vdir *dir; + + dir = (struct android_unix_vdir *) vdir; + return dirfd (dir->directory); +} + +static struct android_vdir * +android_unix_opendir (struct android_vnode *vnode) +{ + struct android_unix_vnode *vp; + struct android_unix_vdir *dir; + DIR *directory; + + /* Try to opendir the vnode. */ + vp = (struct android_unix_vnode *) vnode; + directory = opendir (vp->name); + + if (!directory) + return NULL; + + dir = xmalloc (sizeof *dir); + dir->vdir.readdir = android_unix_readdir; + dir->vdir.closedir = android_unix_closedir; + dir->vdir.dirfd = android_unix_dirfd; + dir->directory = directory; + return &dir->vdir; +} + + + +/* Asset directory handling functions. ``directory-tree'' is a file in + the root of the assets directory describing its contents. + + See lib-src/asset-directory-tool for more details. */ + +/* The Android directory tree. */ +static const char *directory_tree; + +/* The size of the directory tree. */ +static size_t directory_tree_size; + +/* The asset manager being used. */ +static AAssetManager *asset_manager; + +/* Read an unaligned (32-bit) long from the address POINTER. */ + +static unsigned int +android_extract_long (char *pointer) +{ + unsigned int number; + + memcpy (&number, pointer, sizeof number); + return number; +} + +/* Scan to the file FILE in the asset directory tree. Return a + pointer to the end of that file (immediately before any children) + in the directory tree, or NULL if that file does not exist. + + If returning non-NULL, also return the offset to the end of the + last subdirectory or file in *LIMIT_RETURN. LIMIT_RETURN may be + NULL. + + FILE must have less than 11 levels of nesting. If it ends with a + trailing slash, then NULL will be returned if it is not actually a + directory. */ + +static const char * +android_scan_directory_tree (char *file, size_t *limit_return) +{ + char *token, *saveptr, *copy, *copy1, *start, *max, *limit; + size_t token_length, ntokens, i; + char *tokens[10]; + + USE_SAFE_ALLOCA; + + /* Skip past the 5 byte header. */ + start = (char *) directory_tree + 5; + + /* Figure out the current limit. */ + limit = (char *) directory_tree + directory_tree_size; + + /* Now, split `file' into tokens, with the delimiter being the file + name separator. Look for the file and seek past it. */ + + ntokens = 0; + saveptr = NULL; + copy = copy1 = xstrdup (file); + memset (tokens, 0, sizeof tokens); + + while ((token = strtok_r (copy, "/", &saveptr))) + { + copy = NULL; + + /* Make sure ntokens is within bounds. */ + if (ntokens == ARRAYELTS (tokens)) + { + xfree (copy1); + goto fail; + } + + tokens[ntokens] = SAFE_ALLOCA (strlen (token) + 1); + memcpy (tokens[ntokens], token, strlen (token) + 1); + ntokens++; + } + + /* Free the copy created for strtok_r. */ + xfree (copy1); + + /* If there are no tokens, just return the start of the directory + tree. */ + + if (!ntokens) + { + SAFE_FREE (); + + /* Return the size of the directory tree as the limit. + Do not subtract the initial header bytes, as the limit + is an offset from the start of the file. */ + + if (limit_return) + *limit_return = directory_tree_size; + + return start; + } + + /* Loop through tokens, indexing the directory tree each time. */ + + for (i = 0; i < ntokens; ++i) + { + token = tokens[i]; + + /* Figure out how many bytes to compare. */ + token_length = strlen (token); + + again: + + /* If this would be past the directory, return NULL. */ + if (start + token_length > limit) + goto fail; + + /* Now compare the file name. */ + if (!memcmp (start, token, token_length)) + { + /* They probably match. Find the NULL byte. It must be + either one byte past start + token_length, with the last + byte a trailing slash (indicating that it is a + directory), or just start + token_length. Return 4 bytes + past the next NULL byte. */ + + max = memchr (start, 0, limit - start); + + if (max != start + token_length + && !(max == start + token_length + 1 + && *(max - 1) == '/')) + goto false_positive; + + /* Return it if it exists and is in range, and this is the + last token. Otherwise, set it as start and the limit as + start + the offset and continue the loop. */ + + if (max && max + 5 <= limit) + { + if (i < ntokens - 1) + { + start = max + 5; + limit = ((char *) directory_tree + + android_extract_long (max + 1)); + + /* Make sure limit is still in range. */ + if (limit > directory_tree + directory_tree_size + || start > directory_tree + directory_tree_size) + goto fail; + + continue; + } + + /* Now see if max is not a directory and file is. If + file is a directory, then return NULL. */ + if (*(max - 1) != '/' && file[strlen (file) - 1] == '/') + max = NULL; + else + { + /* Figure out the limit. */ + if (limit_return) + *limit_return = android_extract_long (max + 1); + + /* Go to the end of this file. */ + max += 5; + } + + SAFE_FREE (); + return max; + } + + /* Return NULL otherwise. */ + __android_log_print (ANDROID_LOG_WARN, __func__, + "could not scan to end of directory tree" + ": %s", file); + goto fail; + } + + false_positive: + + /* No match was found. Set start to the next sibling and try + again. */ + + start = memchr (start, 0, limit - start); + + if (!start || start + 5 > limit) + goto fail; + + start = ((char *) directory_tree + + android_extract_long (start + 1)); + + /* Make sure start is still in bounds. */ + + if (start > limit) + goto fail; + + /* Continue the loop. */ + goto again; + } + + fail: + SAFE_FREE (); + return NULL; +} + +/* Return whether or not the directory tree entry DIR is a + directory. + + DIR should be a value returned by + `android_scan_directory_tree'. */ + +static bool +android_is_directory (const char *dir) +{ + /* If the directory is the directory tree, then it is a + directory. */ + if (dir == directory_tree + 5) + return true; + + /* Otherwise, look 5 bytes behind. If it is `/', then it is a + directory. */ + return (dir - 6 >= directory_tree + && *(dir - 6) == '/'); +} + +/* Initialize asset retrieval. ENV should be a JNI environment for + the Emacs thread, and MANAGER should be a local reference to a Java + asset manager object created for the Emacs service context. */ + +static void +android_init_assets (JNIEnv *env, jobject manager) +{ + AAsset *asset; + + /* Set the asset manager. */ + asset_manager = AAssetManager_fromJava (env, manager); + + /* Initialize the directory tree. */ + asset = AAssetManager_open (asset_manager, "directory-tree", + AASSET_MODE_BUFFER); + + if (!asset) + { + __android_log_print (ANDROID_LOG_FATAL, __func__, + "Failed to open directory tree"); + emacs_abort (); + } + + directory_tree = AAsset_getBuffer (asset); + + if (!directory_tree) + emacs_abort (); + + /* Now figure out how big the directory tree is, and compare the + first few bytes. */ + directory_tree_size = AAsset_getLength (asset); + if (directory_tree_size < 5 + || memcmp (directory_tree, "EMACS", 5)) + { + __android_log_print (ANDROID_LOG_FATAL, __func__, + "Directory tree has bad magic"); + emacs_abort (); + } + + /* Hold a VM reference to the asset manager to prevent the native + object from being deleted. */ + (*env)->NewGlobalRef (env, manager); + + /* Abort if there's no more memory for the global reference. */ + if ((*env)->ExceptionCheck (env)) + abort (); +} + + + +/* Asset-to-file descriptor conversion. */ + +/* Pointer to the `ASharedMemory_create' function which is loaded + dynamically. */ +static int (*asharedmemory_create) (const char *, size_t); + +/* Do the same as android_hack_asset_fd, but use an unlinked temporary + file to cater to old Android kernels where ashmem files are not + readable. */ + +static int +android_hack_asset_fd_fallback (AAsset *asset) +{ + int fd; + char filename[PATH_MAX]; + size_t size; + void *mem; + + /* Assets must be small enough to fit in size_t, if off_t is + larger. */ + size = AAsset_getLength (asset); + + /* Get an unlinked file descriptor from a file in the cache + directory, which is guaranteed to only be written to by Emacs. + Creating an ashmem file descriptor and reading from it doesn't + work on these old Android versions. */ + + snprintf (filename, PATH_MAX, "%s/temp~unlinked.%d", + android_cache_dir, getpid ()); + fd = open (filename, O_CREAT | O_RDWR | O_TRUNC, + S_IRUSR | S_IWUSR); + + if (fd < 0) + return -1; + + if (unlink (filename)) + goto fail; + + if (ftruncate (fd, size)) + goto fail; + + mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0); + if (mem == MAP_FAILED) + { + __android_log_print (ANDROID_LOG_ERROR, __func__, + "mmap: %s", strerror (errno)); + goto fail; + } + + if (AAsset_read (asset, mem, size) != size) + { + /* Too little was read. Close the file descriptor and + report an error. */ + __android_log_print (ANDROID_LOG_ERROR, __func__, + "AAsset_read: %s", strerror (errno)); + goto fail; + } + + munmap (mem, size); + return fd; + + fail: + close (fd); + return -1; +} + +/* Return whether or not shared memory file descriptors can also be + read from, and are thus suitable for creating asset files. + + This does not work on some ancient Android systems running old + versions of the kernel. */ + +static bool +android_detect_ashmem (void) +{ + int fd, rc; + void *mem; + char test_buffer[10]; + + memcpy (test_buffer, "abcdefghi", 10); + + /* Create the file descriptor to be used for the test. */ + + /* Android 28 and earlier let Emacs access /dev/ashmem directly, so + prefer that over using ASharedMemory. */ + + if (android_get_current_api_level () <= 28) + { + fd = open ("/dev/ashmem", O_RDWR); + + if (fd < 0) + return false; + + /* An empty name means the memory area will exist until the file + descriptor is closed, because no other process can + attach. */ + rc = ioctl (fd, ASHMEM_SET_NAME, ""); + + if (rc < 0) + { + close (fd); + return false; + } + + rc = ioctl (fd, ASHMEM_SET_SIZE, sizeof test_buffer); + + if (rc < 0) + { + close (fd); + return false; + } + } + else + { + /* On the other hand, SELinux restrictions on Android 29 and + later require that Emacs use a system service to obtain + shared memory. Load this dynamically, as this service is not + available on all versions of the NDK. */ + + if (!asharedmemory_create) + { + *(void **) (&asharedmemory_create) + = dlsym (RTLD_DEFAULT, "ASharedMemory_create"); + + if (!asharedmemory_create) + { + __android_log_print (ANDROID_LOG_FATAL, __func__, + "dlsym: %s\n", + strerror (errno)); + emacs_abort (); + } + } + + fd = (*asharedmemory_create) ("", sizeof test_buffer); + + if (fd < 0) + return false; + } + + /* Now map the resource and write the test contents. */ + + mem = mmap (NULL, sizeof test_buffer, PROT_WRITE, + MAP_SHARED, fd, 0); + if (mem == MAP_FAILED) + { + close (fd); + return false; + } + + /* Copy over the test contents. */ + memcpy (mem, test_buffer, sizeof test_buffer); + + /* Return anyway even if munmap fails. */ + munmap (mem, sizeof test_buffer); + + /* Try to read the content back into test_buffer. If this does not + compare equal to the original string, or the read fails, then + ashmem descriptors are not readable on this system. */ + + if ((read (fd, test_buffer, sizeof test_buffer) + != sizeof test_buffer) + || memcmp (test_buffer, "abcdefghi", sizeof test_buffer)) + { + __android_log_print (ANDROID_LOG_WARN, __func__, + "/dev/ashmem does not produce real" + " temporary files on this system, so" + " Emacs will fall back to creating" + " unlinked temporary files."); + close (fd); + return false; + } + + close (fd); + return true; +} + +/* Get a file descriptor backed by a temporary in-memory file for the + given asset. */ + +static int +android_hack_asset_fd (AAsset *asset) +{ + static bool ashmem_readable_p; + static bool ashmem_initialized; + int fd, rc; + unsigned char *mem; + size_t size; + + /* The first time this function is called, try to determine whether + or not ashmem file descriptors can be read from. */ + + if (!ashmem_initialized) + ashmem_readable_p + = android_detect_ashmem (); + ashmem_initialized = true; + + /* If it isn't, fall back. */ + + if (!ashmem_readable_p) + return android_hack_asset_fd_fallback (asset); + + /* Assets must be small enough to fit in size_t, if off_t is + larger. */ + size = AAsset_getLength (asset); + + /* Android 28 and earlier let Emacs access /dev/ashmem directly, so + prefer that over using ASharedMemory. */ + + if (android_get_current_api_level () <= 28) + { + fd = open ("/dev/ashmem", O_RDWR); + + if (fd < 0) + return -1; + + /* An empty name means the memory area will exist until the file + descriptor is closed, because no other process can + attach. */ + rc = ioctl (fd, ASHMEM_SET_NAME, ""); + + if (rc < 0) + { + __android_log_print (ANDROID_LOG_ERROR, __func__, + "ioctl ASHMEM_SET_NAME: %s", + strerror (errno)); + close (fd); + return -1; + } + + rc = ioctl (fd, ASHMEM_SET_SIZE, size); + + if (rc < 0) + { + __android_log_print (ANDROID_LOG_ERROR, __func__, + "ioctl ASHMEM_SET_SIZE: %s", + strerror (errno)); + close (fd); + return -1; + } + + if (!size) + return fd; + + /* Now map the resource. */ + mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0); + if (mem == MAP_FAILED) + { + __android_log_print (ANDROID_LOG_ERROR, __func__, + "mmap: %s", strerror (errno)); + close (fd); + return -1; + } + + if (AAsset_read (asset, mem, size) != size) + { + /* Too little was read. Close the file descriptor and + report an error. */ + __android_log_print (ANDROID_LOG_ERROR, __func__, + "AAsset_read: %s", strerror (errno)); + close (fd); + return -1; + } + + /* Return anyway even if munmap fails. */ + munmap (mem, size); + return fd; + } + + /* On the other hand, SELinux restrictions on Android 29 and later + require that Emacs use a system service to obtain shared memory. + Load this dynamically, as this service is not available on all + versions of the NDK. */ + + if (!asharedmemory_create) + { + *(void **) (&asharedmemory_create) + = dlsym (RTLD_DEFAULT, "ASharedMemory_create"); + + if (!asharedmemory_create) + { + __android_log_print (ANDROID_LOG_FATAL, __func__, + "dlsym: %s\n", + strerror (errno)); + emacs_abort (); + } + } + + fd = (*asharedmemory_create) ("", size); + + if (fd < 0) + { + __android_log_print (ANDROID_LOG_ERROR, __func__, + "ASharedMemory_create: %s", + strerror (errno)); + return -1; + } + + /* Now map the resource. */ + mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0); + if (mem == MAP_FAILED) + { + __android_log_print (ANDROID_LOG_ERROR, __func__, + "mmap: %s", strerror (errno)); + close (fd); + return -1; + } + + if (AAsset_read (asset, mem, size) != size) + { + /* Too little was read. Close the file descriptor and + report an error. */ + __android_log_print (ANDROID_LOG_ERROR, __func__, + "AAsset_read: %s", strerror (errno)); + close (fd); + return -1; + } + + /* Return anyway even if munmap fails. */ + munmap (mem, size); + return fd; +} + + + +/* ``Asset file system'' vnode implementation. These vnodes map to + asset files within the application package, provided by the Android + ``asset manager''. */ + +struct android_afs_vnode +{ + /* The vnode data itself. */ + struct android_vnode vnode; + + /* Length of the name without a trailing null byte. */ + size_t name_length; + + /* Name of the vnode. */ + char *name; +}; + +struct android_afs_vdir +{ + /* The directory function table. */ + struct android_vdir vdir; + + /* The next directory stream in `all_afs_vdirs'. */ + struct android_afs_vdir *next; + + /* Pointer to the directory in directory_tree. */ + char *asset_dir; + + /* And the end of the files in asset_dir. */ + char *asset_limit; + + /* Path to the directory relative to /. */ + char *asset_file; + + /* File descriptor representing this directory stream, or NULL. */ + int fd; +}; + +struct android_afs_open_fd +{ + /* The next table entry. */ + struct android_afs_open_fd *next; + + /* The open file descriptor. */ + int fd; + + /* The stat buffer associated with this entry. */ + struct stat statb; +}; + +static struct android_vnode *android_afs_name (struct android_vnode *, + char *, size_t); +static int android_afs_open (struct android_vnode *, int, + mode_t, bool, int *, AAsset **); +static void android_afs_close (struct android_vnode *); +static int android_afs_unlink (struct android_vnode *); +static int android_afs_symlink (const char *, struct android_vnode *); +static int android_afs_rmdir (struct android_vnode *); +static int android_afs_rename (struct android_vnode *, + struct android_vnode *, bool); +static int android_afs_stat (struct android_vnode *, struct stat *); +static int android_afs_access (struct android_vnode *, int); +static int android_afs_mkdir (struct android_vnode *, mode_t); +static struct android_vdir *android_afs_opendir (struct android_vnode *); + +/* Vector of VFS operations associated with asset VFS nodes. */ + +static struct android_vops afs_vfs_ops = + { + android_afs_name, + android_afs_open, + android_afs_close, + android_afs_unlink, + android_afs_symlink, + android_afs_rmdir, + android_afs_rename, + android_afs_stat, + android_afs_access, + android_afs_mkdir, + android_afs_opendir, + }; + +/* Chain consisting of all open asset directory streams. */ +static struct android_afs_vdir *all_afs_vdirs; + +/* List linking open file descriptors to asset information. This + assumes Emacs does not use dup on regular files. */ +static struct android_afs_open_fd *afs_file_descriptors; + +static struct android_vnode * +android_afs_name (struct android_vnode *vnode, char *name, + size_t length) +{ + size_t j; + char *remainder, *fill; + struct android_afs_vnode *vp, *input; + struct android_afs_vnode temp; + + input = (struct android_afs_vnode *) vnode; + + /* Canonicalize NAME. */ + remainder = android_vfs_canonicalize_name (name, &length); + + /* If remainder is set, it's a name relative to the parent + vnode. */ + if (remainder) + goto parent_vnode; + + /* Allocate a new vnode. */ + vp = xmalloc (sizeof *vp); + + /* See the specified name is empty. */ + + if (length < 1) + { + memcpy (vp, vnode, sizeof *vp); + vp->name = xstrdup (vp->name); + return &vp->vnode; + } + + /* Recompute length. */ + vp->vnode.ops = &afs_vfs_ops; + vp->vnode.type = ANDROID_VNODE_AFS; + vp->vnode.flags = 0; + + /* Generate the new name of the vnode. Remove any trailing slash + from vp->name. */ + + vp->name_length = input->name_length + length; + vp->name = xmalloc (vp->name_length + 2); + + /* Copy the parent name over. */ + fill = mempcpy (vp->name, input->name, input->name_length); + + /* Check if it contains a trailing slash. input->name cannot be + empty, as the root vnode's name is `/'. */ + + if (fill[-1] != '/' && *name != '/') + /* If not, append a trailing slash and adjust vp->name_length + correspondingly. */ + *fill++ = '/', vp->name_length++; + else if (fill[-1] == '/' && *name == '/') + /* If name has a leading slash and fill does too, move fill + backwards so that name's slash will override that of fill. */ + fill--, vp->name_length--; + + /* Now copy NAME. */ + fill = mempcpy (fill, name, length); + + /* And NULL terminate fill. */ + *fill = '\0'; + return &vp->vnode; + + parent_vnode: + /* .. was encountered and the parent couldn't be found through + stripping off preceding components. + + Find the parent vnode and name the rest of NAME starting from + there. */ + + if (input->name_length == 1) + /* This is the vnode representing the /assets directory... */ + vnode = &root_vnode.vnode; + else + { + /* Create a temporary asset vnode within the parent and use it + instead. First, establish the length of vp->name before its + last component. */ + + for (j = input->name_length - 1; j; --j) + { + if (input->name[j - 1] == '/') + break; + } + + /* There must be at least one leading directory separator in an + asset vnode's `name' field. */ + + if (!j) + abort (); + + /* j is now the length of the string minus the size of its last + component. Create a temporary vnode with that as its + name. */ + + temp.vnode.ops = &afs_vfs_ops; + temp.vnode.type = ANDROID_VNODE_AFS; + temp.vnode.flags = 0; + temp.name_length = j; + temp.name = xmalloc (j + 1); + fill = mempcpy (temp.name, input->name, j); + *fill = '\0'; + + /* Search for the remainder of NAME relative to its parent. */ + vnode = android_afs_name (&temp.vnode, remainder, + strlen (remainder)); + xfree (temp.name); + return vnode; + } + + return (*vnode->ops->name) (vnode, remainder, strlen (remainder)); +} + +/* Find the vnode designated by the normalized NAME relative to the + root of the asset file system. NAME may be modified, and must be + LENGTH bytes long, excluding its terminating NULL byte. */ + +static struct android_vnode * +android_afs_initial (char *name, size_t length) +{ + struct android_afs_vnode temp; + + /* Create a temporary vnode at the root of the asset file + system. */ + + temp.vnode.ops = &afs_vfs_ops; + temp.vnode.type = ANDROID_VNODE_AFS; + temp.vnode.flags = 0; + temp.name_length = 1; + temp.name = "/"; + + /* Try to name this vnode. If NAME is empty, it will be duplicated + instead. */ + return android_afs_name (&temp.vnode, name, length); +} + +/* Make FD close-on-exec. If any system call fails, do not abort, but + log a warning to the system log. */ + +static void +android_close_on_exec (int fd) +{ + int flags, rc; + + flags = fcntl (fd, F_GETFD); + + if (flags < 0) + { + __android_log_print (ANDROID_LOG_WARN, __func__, + "fcntl: %s", strerror (errno)); + return; + } + + rc = fcntl (fd, F_SETFD, flags | O_CLOEXEC); + + if (rc < 0) + { + __android_log_print (ANDROID_LOG_WARN, __func__, + "fcntl: %s", strerror (errno)); + return; + } +} + +static int +android_afs_open (struct android_vnode *vnode, int flags, + mode_t mode, bool asset_p, int *fd_return, + AAsset **asset_return) +{ + AAsset *asset; + struct android_afs_vnode *vp; + const char *asset_dir; + int fd; + struct android_afs_open_fd *info; + + vp = (struct android_afs_vnode *) vnode; + + /* Return suitable error indications for unsupported file + operations. */ + + if ((flags & O_WRONLY) || (flags & O_RDWR)) + { + errno = EROFS; + return -1; + } + + if (flags & O_DIRECTORY) + { + errno = ENOSYS; + return -1; + } + + /* Now try to open this asset. Asset manager APIs expect there to + be no trailing directory separator. */ + asset = AAssetManager_open (asset_manager, vp->name + 1, + AASSET_MODE_STREAMING); + + /* If it can't be opened, return an error indication. */ + + if (!asset) + { + /* Scan the directory tree for this file. */ + asset_dir = android_scan_directory_tree (vp->name, NULL); + + /* Default errno to ENOTENT. */ + errno = ENOENT; + + /* Maybe the caller wants to open a directory vnode as a + file? */ + + if (asset_dir && android_is_directory (asset_dir)) + /* In that case, set errno to ENOSYS. */ + errno = ENOSYS; + + return -1; + } + + /* An asset has been opened. If the caller wants a file descriptor, + a temporary one must be created and the file contents read + inside. */ + + if (!asset_p) + { + /* Create a shared memory file descriptor containing the asset + contents. + + The documentation misleads people into thinking that + AAsset_openFileDescriptor does precisely this. However, it + instead returns an offset into any uncompressed assets in the + ZIP archive. This cannot be found in its documentation. */ + + fd = android_hack_asset_fd (asset); + + if (fd == -1) + { + AAsset_close (asset); + errno = EIO; + return -1; + } + + /* If O_CLOEXEC is specified, make the file descriptor close on + exec too. */ + + if (flags & O_CLOEXEC) + android_close_on_exec (fd); + + /* Keep a record linking ``hacked'' file descriptors with + their file status. */ + info = xzalloc (sizeof *info); + info->fd = fd; + info->next = afs_file_descriptors; + + /* Fill in some information that will be reported to + callers of android_fstat, among others. */ + info->statb.st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + + /* Owned by root. */ + info->statb.st_uid = 0; + info->statb.st_gid = 0; + + /* Concoct a nonexistent device and an inode number. */ + info->statb.st_dev = -1; + info->statb.st_ino = 0; + + /* Size of the file. */ + info->statb.st_size = AAsset_getLength (asset); + + /* Chain info onto afs_file_descriptors. */ + afs_file_descriptors = info; + + AAsset_close (asset); + + /* Return the file descriptor. */ + *fd_return = fd; + return 0; + } + + /* Return the asset itself. */ + *asset_return = asset; + return 1; +} + +static void +android_afs_close (struct android_vnode *vnode) +{ + struct android_afs_vnode *vp; + int save_errno; + + save_errno = errno; + vp = (struct android_afs_vnode *) vnode; + xfree (vp->name); + xfree (vp); + errno = save_errno; +} + +static int +android_afs_unlink (struct android_vnode *vnode) +{ + const char *dir; + struct android_afs_vnode *vp; + + /* If the vnode already exists, return EROFS. Else, return + ENOENT. */ + + vp = (struct android_afs_vnode *) vnode; + dir = android_scan_directory_tree (vp->name, NULL); + + if (dir) + errno = EROFS; + else + errno = ENOENT; + + return -1; +} + +static int +android_afs_symlink (const char *linkname, struct android_vnode *vnode) +{ + struct android_afs_vnode *vp; + + /* If this vnode already exists, return EEXIST. */ + vp = (struct android_afs_vnode *) vnode; + + if (android_scan_directory_tree (vp->name, NULL)) + { + errno = EEXIST; + return -1; + } + + /* Symlinks aren't supported on this (read-only) ``file system'', + so return -1 with EROFS. */ + errno = EROFS; + return -1; +} + +static int +android_afs_rmdir (struct android_vnode *vnode) +{ + const char *dir; + struct android_afs_vnode *vp; + + /* If the vnode already exists and is a directory, return EROFS. + Else, return ENOTDIR or ENOENT. */ + + vp = (struct android_afs_vnode *) vnode; + dir = android_scan_directory_tree (vp->name, NULL); + + if (dir && android_is_directory (dir)) + errno = EROFS; + else if (dir) + errno = ENOTDIR; + else + errno = ENOENT; + + return -1; +} + +static int +android_afs_rename (struct android_vnode *src, struct android_vnode *dst, + bool keep_existing) +{ + /* If src and dst are different kinds of vnodes, return EXDEV. + Else, return EROFS. */ + + errno = EROFS; + if (src->type != dst->type) + errno = EXDEV; + + return -1; +} + +static int +android_afs_stat (struct android_vnode *vnode, struct stat *statb) +{ + const char *dir; + struct android_afs_vnode *vp; + AAsset *asset_desc; + + /* Scan for the vnode to see whether or not it exists. */ + + vp = (struct android_afs_vnode *) vnode; + dir = android_scan_directory_tree (vp->name, NULL); + + if (!dir) + { + /* Return ENOENT; whether the lookup failed because directory + components within vp->path weren't really directories is not + important to Emacs's error reporting. */ + errno = ENOENT; + return -1; + } + + if (android_is_directory (dir)) + { + memset (statb, 0, sizeof *statb); + + /* Fill in the stat buffer. */ + statb->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH; + + /* Concoct a nonexistent device and an inode number. */ + statb->st_dev = -1; + statb->st_ino = 0; + return 0; + } + + /* AASSET_MODE_STREAMING is fastest here. */ + asset_desc = AAssetManager_open (asset_manager, vp->name + 1, + AASSET_MODE_STREAMING); + + if (!asset_desc) + { + /* If the asset exists in the directory tree but can't be + located by the asset manager, report OOM. */ + errno = ENOMEM; + return 1; + } + + memset (statb, 0, sizeof *statb); + + /* Fill in the stat buffer. */ + statb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + statb->st_dev = -1; + statb->st_ino = 0; + statb->st_size = AAsset_getLength (asset_desc); + + /* Close the asset. */ + AAsset_close (asset_desc); + return 0; +} + +static int +android_afs_access (struct android_vnode *vnode, int mode) +{ + const char *dir; + struct android_afs_vnode *vp; + + /* Validate MODE. */ + + if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK))) + { + errno = EINVAL; + return -1; + } + + /* Scan for the vnode to see whether or not it exists. */ + + vp = (struct android_afs_vnode *) vnode; + dir = android_scan_directory_tree (vp->name, NULL); + + if (dir) + { + /* It exists. If MODE contains W_OK or X_OK, return + EACCESS. */ + + if (mode & (W_OK | X_OK)) + { + errno = EACCES; + return -1; + } + + /* If vp->name is a directory and DIR isn't, return ENOTDIR. */ + + if (vp->name[vp->name_length] == '/' + && !android_is_directory (dir)) + { + errno = ENOTDIR; + return -1; + } + + return 0; + } + + errno = ENOENT; + return -1; +} + +static int +android_afs_mkdir (struct android_vnode *vnode, mode_t mode) +{ + struct android_afs_vnode *vp; + const char *dir; + + /* If the vnode already exists, return EEXIST in lieu of EROFS. */ + + vp = (struct android_afs_vnode *) vnode; + dir = android_scan_directory_tree (vp->name, NULL); + + if (dir) + errno = EEXIST; + else + errno = EROFS; + + return -1; +} + +static struct dirent * +android_afs_readdir (struct android_vdir *vdir) +{ + static struct dirent dirent; + const char *last; + struct android_afs_vdir *dir; + + dir = (struct android_afs_vdir *) vdir; + + /* There are no more files to read. */ + if (dir->asset_dir >= dir->asset_limit) + return NULL; + + /* Otherwise, scan forward looking for the next NULL byte. */ + last = memchr (dir->asset_dir, 0, + dir->asset_limit - dir->asset_dir); + + /* No more NULL bytes remain. */ + if (!last) + return NULL; + + /* Forward last past the NULL byte. */ + last++; + + /* Make sure it is still within the directory tree. */ + if (last >= directory_tree + directory_tree_size) + return NULL; + + /* Now, fill in the dirent with the name. */ + memset (&dirent, 0, sizeof dirent); + dirent.d_ino = 0; + dirent.d_off = 0; + dirent.d_reclen = sizeof dirent; + + /* Note that dir->asset_dir is actually a NULL terminated + string. */ + memcpy (dirent.d_name, dir->asset_dir, + MIN (sizeof dirent.d_name, + last - dir->asset_dir)); + dirent.d_name[sizeof dirent.d_name - 1] = '\0'; + + /* Strip off the trailing slash, if any. */ + if (dirent.d_name[MIN (sizeof dirent.d_name, + last - dir->asset_dir) + - 2] == '/') + dirent.d_name[MIN (sizeof dirent.d_name, + last - dir->asset_dir) + - 2] = '\0'; + + /* If this is not a directory, return DT_REG. Otherwise, return + DT_DIR. */ + + if (last - 2 >= directory_tree && last[-2] == '/') + dirent.d_type = DT_DIR; + else + dirent.d_type = DT_REG; + + /* Forward dir->asset_dir to the file past last. */ + dir->asset_dir = ((char *) directory_tree + + android_extract_long ((char *) last)); + + return &dirent; +} + +static void +android_afs_closedir (struct android_vdir *vdir) +{ + struct android_afs_vdir *dir, **next, *tem; + + dir = (struct android_afs_vdir *) vdir; + + /* If the ``directory file descriptor'' has been opened, close + it. */ + + if (dir->fd != -1) + close (dir->fd); + + xfree (dir->asset_file); + + /* Now unlink this directory. */ + + for (next = &all_afs_vdirs; (tem = *next);) + { + if (tem == dir) + *next = dir->next; + else + next = &(*next)->next; + } + + /* Free the directory itself. */ + + xfree (dir); +} + +static int +android_afs_dirfd (struct android_vdir *vdir) +{ + struct android_afs_vdir *dir; + + dir = (struct android_afs_vdir *) vdir; + + /* Since `android_afs_opendir' tries to avoid opening a file + descriptor if readdir isn't called, dirfd can fail if open fails. + + open sets errno to a set of errors different from what POSIX + stipulates for dirfd, but for ease of implementation the open + errors are used instead. */ + + if (dir->fd >= 0) + return dir->fd; + + dir->fd = open ("/dev/null", O_RDONLY | O_CLOEXEC); + return dir->fd; +} + +static struct android_vdir * +android_afs_opendir (struct android_vnode *vnode) +{ + char *asset_dir; + struct android_afs_vdir *dir; + struct android_afs_vnode *vp; + size_t limit; + + vp = (struct android_afs_vnode *) vnode; + + /* Scan for the asset directory by vp->name. */ + + asset_dir + = (char *) android_scan_directory_tree (vp->name, &limit); + + if (!asset_dir) + { + errno = ENOENT; + return NULL; + } + + /* Verify that asset_dir is indeed a directory. */ + + if (!android_is_directory (asset_dir)) + { + errno = ENOTDIR; + return NULL; + } + + /* Fill in the directory stream. */ + dir = xmalloc (sizeof *dir); + dir->vdir.readdir = android_afs_readdir; + dir->vdir.closedir = android_afs_closedir; + dir->vdir.dirfd = android_afs_dirfd; + dir->asset_dir = asset_dir; + dir->asset_limit = (char *) directory_tree + limit; + dir->fd = -1; + dir->asset_file = xzalloc (vp->name_length + 2); + strcpy (dir->asset_file, vp->name); + + /* Make sure dir->asset_file is terminated with /. */ + if (dir->asset_file[vp->name_length - 1] != '/') + dir->asset_file[vp->name_length] = '/'; + + /* Make sure dir->asset_limit is within bounds. It is a limit, + and as such can be exactly one byte past directory_tree. */ + if (dir->asset_limit > directory_tree + directory_tree_size) + { + xfree (dir); + xfree (dir->asset_file); + errno = EACCES; + return NULL; + } + + dir->next = all_afs_vdirs; + all_afs_vdirs = dir; + return &dir->vdir; +} + +/* Return the file name corresponding to DIRFD if it is a + ``directory'' file descriptor returned by `android_afs_dirfd' or + NULL otherwise. These file names are relative to the `/assets' + directory, but with a leading separator character. */ + +static char * +android_afs_get_directory_name (int dirfd) +{ + struct android_afs_vdir *dir; + + for (dir = all_afs_vdirs; dir; dir = dir->next) + { + if (dir->fd == dirfd && dirfd != -1) + return dir->asset_file; + } + + return NULL; +} + + + +struct android_content_vdir +{ + /* The directory function table. */ + struct android_vdir vdir; + + /* The next directory stream in `all_content_vdirs'. */ + struct android_content_vdir *next; + + /* Pointer to the next file to return. */ + const char **next_name; + + /* Temporary file descriptor used to identify this directory to + at-funcs, or -1. */ + int fd; +}; + +static struct android_vnode *android_authority_initial (char *, size_t); +static struct android_vnode *android_saf_root_initial (char *, size_t); + +/* Content provider meta-interface. This implements a vnode at + /content, which is a directory itself containing two additional + directories. + + /content/storage only exists on Android 5.0 and later, and contains + a list of each directory tree Emacs has been granted permanent + access to through the Storage Access Framework. + + /content/by-authority exists on Android 4.4 and later; it contains + no directories, but provides a `name' function that converts + children into content URIs. */ + +static struct android_vnode *android_content_name (struct android_vnode *, + char *, size_t); +static int android_content_open (struct android_vnode *, int, + mode_t, bool, int *, AAsset **); +static void android_content_close (struct android_vnode *); +static int android_content_unlink (struct android_vnode *); +static int android_content_symlink (const char *, struct android_vnode *); +static int android_content_rmdir (struct android_vnode *); +static int android_content_rename (struct android_vnode *, + struct android_vnode *, bool); +static int android_content_stat (struct android_vnode *, struct stat *); +static int android_content_access (struct android_vnode *, int); +static int android_content_mkdir (struct android_vnode *, mode_t); +static struct android_vdir *android_content_opendir (struct android_vnode *); + +/* Vector of VFS operations associated with the content VFS node. */ + +static struct android_vops content_vfs_ops = + { + android_content_name, + android_content_open, + android_content_close, + android_content_unlink, + android_content_symlink, + android_content_rmdir, + android_content_rename, + android_content_stat, + android_content_access, + android_content_mkdir, + android_content_opendir, + }; + +/* Table of directories contained within a top-level vnode. */ + +static const char *content_directory_contents[] = + { + "storage", "by-authority", + }; + +/* Chain consisting of all open content directory streams. */ +static struct android_content_vdir *all_content_vdirs; + +static struct android_vnode * +android_content_name (struct android_vnode *vnode, char *name, + size_t length) +{ + char *remainder; + struct android_vnode *vp; + char *component_end; + struct android_special_vnode *special; + size_t i; + int api; + + static struct android_special_vnode content_vnodes[] = { + { "storage", 7, android_saf_root_initial, }, + { "by-authority", 12, android_authority_initial, }, + }; + + /* Canonicalize NAME. */ + remainder = android_vfs_canonicalize_name (name, &length); + + /* If remainder is set, it's a name relative to the root vnode. */ + if (remainder) + goto parent_vnode; + + /* If LENGTH is empty or NAME is a single directory separator, + return a copy of this vnode. */ + + if (length < 1 || (*name == '/' && length == 1)) + { + vp = xmalloc (sizeof *vp); + memcpy (vp, vnode, sizeof *vp); + return vp; + } + + api = android_get_current_api_level (); + + /* If NAME starts with a directory separator, move it past that. */ + + if (*name == '/') + name++, length -= 1; + + /* Look for the first directory separator. */ + component_end = strchr (name, '/'); + + /* If not there, use name + length. */ + + if (!component_end) + component_end = name + length; + else + /* Move past the separator character. */ + component_end++; + + /* Now, find out if the first component is a special vnode; if so, + call its root lookup function with the rest of NAME there. */ + + if (api < 19) + i = 2; + else if (api < 21) + i = 1; + else + i = 0; + + for (; i < ARRAYELTS (content_vnodes); ++i) + { + special = &content_vnodes[i]; + + if (component_end - name == special->length + && !memcmp (special->name, name, special->length)) + return (*special->initial) (component_end, + length - special->length); + + /* Detect the case where a special is named with a trailing + directory separator. */ + + if (component_end - name == special->length + 1 + && !memcmp (special->name, name, special->length) + && name[special->length] == '/') + /* Make sure to include the directory separator. */ + return (*special->initial) (component_end - 1, + length - special->length); + } + + errno = ENOENT; + return NULL; + + parent_vnode: + /* The parent of this vnode is always the root filesystem. */ + vp = &root_vnode.vnode; + return (*vnode->ops->name) (vnode, remainder, strlen (remainder)); +} + +static int +android_content_open (struct android_vnode *vnode, int flags, + mode_t mode, bool asset_p, int *fd, + AAsset **asset) +{ + /* Don't allow opening this special directory. */ + errno = ENOSYS; + return -1; +} + +static void +android_content_close (struct android_vnode *vnode) +{ + int save_errno; + + save_errno = errno; + xfree (vnode); + errno = save_errno; +} + +static int +android_content_unlink (struct android_vnode *vnode) +{ + errno = ENOSYS; + return -1; +} + +static int +android_content_symlink (const char *target, struct android_vnode *vnode) +{ + errno = ENOSYS; + return -1; +} + +static int +android_content_rmdir (struct android_vnode *vnode) +{ + errno = ENOSYS; + return -1; +} + +static int +android_content_rename (struct android_vnode *src, + struct android_vnode *dst, + bool keep_existing) +{ + if (src->type != dst->type) + { + /* If the types of both vnodes differ, complain that they're on + two different filesystems (which is correct from a abstract + viewpoint.) */ + errno = EXDEV; + return -1; + } + + /* Otherwise, return ENOSYS. */ + errno = ENOSYS; + return -1; +} + +static int +android_content_stat (struct android_vnode *vnode, + struct stat *statb) +{ + memset (statb, 0, sizeof *statb); + + statb->st_uid = getuid (); + statb->st_gid = getgid (); + statb->st_ino = 0; + statb->st_dev = -2; + statb->st_mode = S_IFDIR | S_IRUSR; + return 0; +} + +static int +android_content_access (struct android_vnode *vnode, int mode) +{ + /* Validate MODE. */ + + if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK))) + { + errno = EINVAL; + return -1; + } + + /* Return EROFS if the caller is trying to check for write access to + this vnode. */ + + if (mode != F_OK && (mode & (W_OK | X_OK))) + { + errno = EROFS; + return -1; + } + + return 0; +} + +static int +android_content_mkdir (struct android_vnode *vnode, mode_t mode) +{ + errno = EEXIST; + return 0; +} + +static struct dirent * +android_content_readdir (struct android_vdir *vdir) +{ + static struct dirent dirent; + struct android_content_vdir *dir; + const char *name; + + dir = (struct android_content_vdir *) vdir; + + /* There are no more files to be read. */ + if (dir->next_name == (content_directory_contents + + ARRAYELTS (content_directory_contents))) + return NULL; + + /* Get the next child. */ + name = *dir->next_name++; + + /* Now, fill in the dirent with the name. */ + memset (&dirent, 0, sizeof dirent); + dirent.d_ino = 0; + dirent.d_off = 0; + dirent.d_reclen = sizeof dirent; + dirent.d_type = DT_DIR; + strcpy (dirent.d_name, name); + return &dirent; +} + +static void +android_content_closedir (struct android_vdir *vdir) +{ + struct android_content_vdir *dir, **next, *tem; + + dir = (struct android_content_vdir *) vdir; + + /* If the ``directory file descriptor'' has been opened, close + it. */ + + if (dir->fd != -1) + close (dir->fd); + + /* Now unlink this directory. */ + + for (next = &all_content_vdirs; (tem = *next);) + { + if (tem == dir) + *next = dir->next; + else + next = &(*next)->next; + } + + xfree (dir); +} + +static int +android_content_dirfd (struct android_vdir *vdir) +{ + struct android_content_vdir *dir; + + dir = (struct android_content_vdir *) vdir; + + /* Since `android_content_opendir' tries to avoid opening a file + descriptor if readdir isn't called, dirfd can fail if open fails. + + open sets errno to a set of errors different from what POSIX + stipulates for dirfd, but for ease of implementation the open + errors are used instead. */ + + if (dir->fd >= 0) + return dir->fd; + + dir->fd = open ("/dev/null", O_RDONLY | O_CLOEXEC); + return dir->fd; +} + +static struct android_vdir * +android_content_opendir (struct android_vnode *vnode) +{ + struct android_content_vdir *dir; + int api; + + /* Allocate the virtual directory. */ + dir = xmalloc (sizeof *dir); + dir->vdir.readdir = android_content_readdir; + dir->vdir.closedir = android_content_closedir; + dir->vdir.dirfd = android_content_dirfd; + dir->fd = -1; + + /* Fill in the directory contents. */ + dir->next_name = content_directory_contents; + api = android_get_current_api_level (); + + /* Android 4.4 and earlier don't support /content/storage. */ + + if (api < 21) + dir->next_name++; + + /* Android 4.3 and earlier don't support /content/by-authority. */ + + if (api < 19) + dir->next_name++; + + /* Link this stream onto the list of all content directory + streams. */ + dir->next = all_content_vdirs; + all_content_vdirs = dir; + return &dir->vdir; +} + +/* Return the file name corresponding to DIRFD if it is a + ``directory'' file descriptor returned by `android_content_dirfd' + or NULL otherwise. */ + +static char * +android_content_get_directory_name (int dirfd) +{ + struct android_content_vdir *dir; + + for (dir = all_content_vdirs; dir; dir = dir->next) + { + if (dir->fd == dirfd && dirfd != -1) + return "/content"; + } + + return NULL; +} + +/* Find the vnode designated by the normalized NAME relative to the + root of the content file system. NAME may be modified, and must be + LENGTH bytes long, excluding its terminating NULL byte. */ + +static struct android_vnode * +android_content_initial (char *name, size_t length) +{ + struct android_vnode temp; + + /* Create a temporary vnode at the root of the asset file + system. */ + + temp.ops = &content_vfs_ops; + temp.type = ANDROID_VNODE_CONTENT; + temp.flags = 0; + + /* Try to name this vnode. If NAME is empty, it will be duplicated + instead. */ + return android_content_name (&temp, name, length); +} + + + +/* Content URI management functions. */ + +/* Return the content URI corresponding to a `/content/by-authority' + file name, or NULL if it is invalid for some reason. FILENAME + should be relative to /content/by-authority, with no leading + directory separator character. + + This function is not reentrant. */ + +static const char * +android_get_content_name (const char *filename) +{ + static char buffer[PATH_MAX + 1], *fill; + + /* Make sure FILENAME isn't obviously invalid: it must contain an + authority name and a file name component. */ + + fill = strchr (filename, '/'); + if (!fill || *(fill + 1) == '\0') + { + errno = ENOENT; + return NULL; + } + + /* FILENAME must also not be a directory. */ + + if (filename[strlen (filename)] == '/') + { + errno = ENOTDIR; + return NULL; + } + + snprintf (buffer, PATH_MAX + 1, "content://%s", filename); + return buffer; +} + +/* Return whether or not the specified URI is an accessible content + URI. MODE specifies what to check. */ + +static bool +android_check_content_access (const char *uri, int mode) +{ + jobject string; + size_t length; + jboolean rc; + + length = strlen (uri); + + string = (*android_java_env)->NewByteArray (android_java_env, + length); + android_exception_check (); + + (*android_java_env)->SetByteArrayRegion (android_java_env, + string, 0, length, + (jbyte *) uri); + rc = (*android_java_env)->CallBooleanMethod (android_java_env, + emacs_service, + service_class.check_content_uri, + string, + (jboolean) ((mode & R_OK) + != 0), + (jboolean) ((mode & W_OK) + != 0)); + android_exception_check_1 (string); + ANDROID_DELETE_LOCAL_REF (string); + + return rc; +} + + + +/* Content authority-based vnode implementation. + + /contents/by-authority is a simple vnode implementation that converts + components to content:// URIs. + + It does not canonicalize file names by removing parent directory + separators, as these characters can appear in legitimate content + file names. */ + +struct android_authority_vnode +{ + /* The vnode data itself. */ + struct android_vnode vnode; + + /* URI associated with this vnode, or NULL if this is the root of + the content authority tree. */ + char *uri; +}; + +static struct android_vnode *android_authority_name (struct android_vnode *, + char *, size_t); +static int android_authority_open (struct android_vnode *, int, + mode_t, bool, int *, AAsset **); +static void android_authority_close (struct android_vnode *); +static int android_authority_unlink (struct android_vnode *); +static int android_authority_symlink (const char *, struct android_vnode *); +static int android_authority_rmdir (struct android_vnode *); +static int android_authority_rename (struct android_vnode *, + struct android_vnode *, bool); +static int android_authority_stat (struct android_vnode *, struct stat *); +static int android_authority_access (struct android_vnode *, int); +static int android_authority_mkdir (struct android_vnode *, mode_t); +static struct android_vdir *android_authority_opendir (struct android_vnode *); + +/* Vector of VFS operations associated with the content VFS node. */ + +static struct android_vops authority_vfs_ops = + { + android_authority_name, + android_authority_open, + android_authority_close, + android_authority_unlink, + android_authority_symlink, + android_authority_rmdir, + android_authority_rename, + android_authority_stat, + android_authority_access, + android_authority_mkdir, + android_authority_opendir, + }; + +static struct android_vnode * +android_authority_name (struct android_vnode *vnode, char *name, + size_t length) +{ + struct android_authority_vnode *vp; + const char *uri_name; + + if (!android_init_gui) + { + errno = EIO; + return NULL; + } + + /* If NAME is empty or consists of a single directory separator + _and_ VP->uri is NULL, return a copy of VNODE. */ + + vp = (struct android_authority_vnode *) vnode; + + if (length < 1 || (*name == '/' && length == 1 && !vp->uri)) + { + vp = xmalloc (sizeof *vp); + memcpy (vp, vnode, sizeof *vp); + + if (vp->uri) + vp->uri = xstrdup (vp->uri); + + return &vp->vnode; + } + + /* Else, if VP->uri is NULL, then it is the root of the by-authority + tree. If NAME starts with a directory separator character, + remove it. */ + + if (!vp->uri) + { + if (*name == '/') + name++, length -= 1; + + uri_name = android_get_content_name (name); + if (!uri_name) + goto error; + + /* Now fill in the vnode. */ + vp = xmalloc (sizeof *vp); + vp->vnode.ops = &authority_vfs_ops; + vp->vnode.type = ANDROID_VNODE_CONTENT_AUTHORITY; + vp->vnode.flags = 0; + vp->uri = xstrdup (uri_name); + return &vp->vnode; + } + + /* Content files can't have children. */ + errno = ENOENT; + error: + return NULL; +} + +static int +android_authority_open (struct android_vnode *vnode, int flags, + mode_t mode, bool asset_p, int *fd_return, + AAsset **asset) +{ + struct android_authority_vnode *vp; + size_t length; + jobject string; + int fd; + + vp = (struct android_authority_vnode *) vnode; + + if (vp->uri == NULL) + { + /* This is the `by-authority' directory itself, which can't be + opened. */ + errno = ENOSYS; + return -1; + } + + /* Allocate a buffer to hold the file name. */ + length = strlen (vp->uri); + string = (*android_java_env)->NewByteArray (android_java_env, + length); + if (!string) + { + (*android_java_env)->ExceptionClear (android_java_env); + errno = ENOMEM; + return -1; + } + + /* Copy the URI into this byte array. */ + (*android_java_env)->SetByteArrayRegion (android_java_env, + string, 0, length, + (jbyte *) vp->uri); + + /* Try to open the file descriptor. */ + + fd + = (*android_java_env)->CallIntMethod (android_java_env, + emacs_service, + service_class.open_content_uri, + string, + (jboolean) ((mode & O_WRONLY + || mode & O_RDWR) + != 0), + (jboolean) !(mode & O_WRONLY), + (jboolean) ((mode & O_TRUNC) + != 0)); + + if ((*android_java_env)->ExceptionCheck (android_java_env)) + { + (*android_java_env)->ExceptionClear (android_java_env); + errno = ENOMEM; + ANDROID_DELETE_LOCAL_REF (string); + return -1; + } + + /* If fd is -1, just assume that the file does not exist, + and return -1 with errno set to ENOENT. */ + + if (fd == -1) + { + errno = ENOENT; + goto skip; + } + + if (mode & O_CLOEXEC) + android_close_on_exec (fd); + + skip: + ANDROID_DELETE_LOCAL_REF (string); + + if (fd == -1) + return -1; + + *fd_return = fd; + return 0; +} + +static void +android_authority_close (struct android_vnode *vnode) +{ + struct android_authority_vnode *vp; + int save_errno; + + vp = (struct android_authority_vnode *) vnode; + save_errno = errno; + xfree (vp->uri); + xfree (vp); + errno = save_errno; +} + +static int +android_authority_unlink (struct android_vnode *vnode) +{ + errno = EROFS; + return -1; +} + +static int +android_authority_symlink (const char *target, + struct android_vnode *vnode) +{ + errno = EROFS; + return -1; +} + +static int +android_authority_rmdir (struct android_vnode *vnode) +{ + errno = EROFS; + return -1; +} + +static int +android_authority_rename (struct android_vnode *src, + struct android_vnode *dst, + bool keep_existing) +{ + if (src->type != dst->type) + { + /* If the types of both vnodes differ, complain that they're on + two different filesystems (which is correct from a abstract + viewpoint.) */ + errno = EXDEV; + return -1; + } + + /* Otherwise, return ENOSYS. */ + errno = ENOSYS; + return -1; +} + +static int +android_authority_stat (struct android_vnode *vnode, + struct stat *statb) +{ + int rc, fd, save_errno; + struct android_authority_vnode *vp; + + /* If this is a vnode representing `by-authority', return some + information about this directory. */ + + vp = (struct android_authority_vnode *) vnode; + + if (!vp->uri) + { + memset (statb, 0, sizeof *statb); + statb->st_uid = getuid (); + statb->st_gid = getgid (); + statb->st_ino = 0; + statb->st_dev = -3; + statb->st_mode = S_IFDIR | S_IRUSR; + return 0; + } + + /* Try to open the file and call fstat. */ + rc = (*vnode->ops->open) (vnode, O_RDONLY, 0, false, &fd, NULL); + + if (rc < 0) + return -1; + + /* If rc is 1, then an asset file descriptor has been returned. + This is impossible, so assert that it doesn't transpire. */ + assert (rc != 1); + + /* Now, try to stat the file. */ + rc = fstat (fd, statb); + save_errno = errno; + + /* Close the file descriptor. */ + close (fd); + + /* Restore errno. */ + errno = save_errno; + return rc; +} + +static int +android_authority_access (struct android_vnode *vnode, int mode) +{ + struct android_authority_vnode *vp; + + vp = (struct android_authority_vnode *) vnode; + + /* Validate MODE. */ + + if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK))) + { + errno = EINVAL; + return -1; + } + + if (!vp->uri) + { + /* Return EACCES if the caller is trying to check for write + access to `by-authority'. */ + + if (mode != F_OK && (mode & (W_OK | X_OK))) + { + errno = EACCES; + return -1; + } + + return 0; + } + + return (android_check_content_access (vp->uri, mode) + ? 0 : -1); +} + +static int +android_authority_mkdir (struct android_vnode *vnode, mode_t mode) +{ + errno = EACCES; + return -1; +} + +static struct android_vdir * +android_authority_opendir (struct android_vnode *vnode) +{ + struct android_authority_vnode *vp; + + /* Forbid listing the `by-authority' directory. */ + vp = (struct android_authority_vnode *) vnode; + errno = vp->uri ? ENOTDIR : EACCES; + return NULL; +} + +/* Find the vnode designated by NAME relative to the root of the + by-authority directory. + + If NAME is empty or a single leading separator character, return + a vnode representing the by-authority directory itself. + + Otherwise, represent the remainder of NAME as a URI (without + normalizing it) and return a vnode corresponding to that. + + Value may also be NULL with errno set if the designated vnode is + not available, such as when Android windowing has not been + initialized. */ + +static struct android_vnode * +android_authority_initial (char *name, size_t length) +{ + struct android_authority_vnode temp; + + temp.vnode.ops = &authority_vfs_ops; + temp.vnode.type = ANDROID_VNODE_CONTENT_AUTHORITY; + temp.vnode.flags = 0; + temp.uri = NULL; + + return android_authority_name (&temp.vnode, name, length); +} + + + +/* SAF ``root'' vnode implementation. + + The Storage Access Framework is a system service that manages a + registry of document providers, which are a type of file system + server. + + Normally, document providers can only provide individual files + through preestablished ``content URIs''. Android 5.0 and later add + to that ``tree URIs'', which designate entire file system trees. + + Authorization to access document trees and content URIs is granted + transiently by default when an Intent is received, but Emacs can + also receive persistent authorization for a given document tree. + + /content/storage returns a list of directories, each representing a + single authority providing at least one tree URI Emacs holds + persistent authorization for. + + Each one of those directories then contains one document tree that + Emacs is authorized to access. */ + +struct android_saf_root_vnode +{ + /* The vnode data. */ + struct android_vnode vnode; + + /* The name of the document authority this directory represents, or + NULL. */ + char *authority; +}; + +struct android_saf_root_vdir +{ + /* The directory stream function table. */ + struct android_vdir vdir; + + /* The next directory stream in `all_saf_root_vdirs'. */ + struct android_saf_root_vdir *next; + + /* Array of strings, one for each directory to return. */ + jobjectArray array; + + /* Name of the authority this directory lists, or NULL. */ + char *authority; + + /* Length of that array, and the current within it. */ + jsize length, i; + + /* ``Directory'' file descriptor associated with this stream, or + -1. */ + int fd; +}; + +static struct android_vnode *android_saf_root_name (struct android_vnode *, + char *, size_t); +static int android_saf_root_open (struct android_vnode *, int, + mode_t, bool, int *, AAsset **); +static void android_saf_root_close (struct android_vnode *); +static int android_saf_root_unlink (struct android_vnode *); +static int android_saf_root_symlink (const char *, struct android_vnode *); +static int android_saf_root_rmdir (struct android_vnode *); +static int android_saf_root_rename (struct android_vnode *, + struct android_vnode *, bool); +static int android_saf_root_stat (struct android_vnode *, struct stat *); +static int android_saf_root_access (struct android_vnode *, int); +static int android_saf_root_mkdir (struct android_vnode *, mode_t); +static struct android_vdir *android_saf_root_opendir (struct android_vnode *); + +/* Vector of VFS operations associated with the SAF root VFS node. */ + +static struct android_vops saf_root_vfs_ops = + { + android_saf_root_name, + android_saf_root_open, + android_saf_root_close, + android_saf_root_unlink, + android_saf_root_symlink, + android_saf_root_rmdir, + android_saf_root_rename, + android_saf_root_stat, + android_saf_root_access, + android_saf_root_mkdir, + android_saf_root_opendir, + }; + +/* Chain containing all SAF root directories. */ +static struct android_saf_root_vdir *all_saf_root_vdirs; + +/* Defined in the next page. */ +static struct android_vnode *android_saf_tree_from_name (char *, const char *, + const char *); + +static struct android_vnode * +android_saf_root_name (struct android_vnode *vnode, char *name, + size_t length) +{ + char *remainder, *component_end; + struct android_saf_root_vnode *vp; + struct android_vnode *new; + char component[PATH_MAX]; + + /* Canonicalize NAME. */ + remainder = android_vfs_canonicalize_name (name, &length); + + /* If remainder is set, it's a name relative to the root vnode. */ + if (remainder) + goto parent_vnode; + + /* If LENGTH is empty or NAME is a single directory separator, + return a copy of this vnode. */ + + if (length < 1 || (*name == '/' && length == 1)) + { + vp = xmalloc (sizeof *vp); + memcpy (vp, vnode, sizeof *vp); + + if (vp->authority) + vp->authority = xstrdup (vp->authority); + + return &vp->vnode; + } + + vp = (struct android_saf_root_vnode *) vnode; + + /* If NAME starts with a directory separator, move it past that. */ + + if (*name == '/') + name++, length -= 1; + + /* Look for the first directory separator. */ + component_end = strchr (name, '/'); + + /* If not there, use name + length. */ + + if (!component_end) + component_end = name + length; + + if (component_end - name >= PATH_MAX) + { + errno = ENAMETOOLONG; + return NULL; + } + + /* Copy the component over. */ + memcpy (component, name, component_end - name); + component[component_end - name] = '\0'; + + /* Create a SAF document vnode for this tree if it represents an + authority. */ + + if (vp->authority) + return android_saf_tree_from_name (component_end, component, + vp->authority); + + /* Otherwise, find the first component of NAME and create a vnode + representing it as an authority. */ + + /* Create the vnode. */ + vp = xmalloc (sizeof *vp); + vp->vnode.ops = &saf_root_vfs_ops; + vp->vnode.type = ANDROID_VNODE_SAF_ROOT; + vp->vnode.flags = 0; + vp->authority = xstrdup (component); + + /* If there is more of this component to be named, name it through + the new vnode. */ + + if (component_end != name + length) + { + new = (*vp->vnode.ops->name) (&vp->vnode, component_end, + length - (component_end - name)); + (*vp->vnode.ops->close) (&vp->vnode); + + return new; + } + + return &vp->vnode; + + parent_vnode: + vp = (struct android_saf_root_vnode *) vnode; + + /* .. was encountered and the parent couldn't be found through + stripping off preceding components. + + Find the parent vnode and name the rest of NAME starting from + there. */ + + if (!vp->authority) + /* Look this file name up relative to the root of the contents + directory. */ + return android_content_initial (remainder, strlen (remainder)); + else + /* Look this file name up relative to the root of the storage + directory. */ + return android_saf_root_initial (remainder, strlen (remainder)); +} + +static int +android_saf_root_open (struct android_vnode *vnode, int flags, + mode_t mode, bool asset_p, int *fd_return, + AAsset **asset) +{ + /* /content/storage or one of its authority children cannot be + opened, as they are virtual directories. */ + + errno = ENOSYS; + return -1; +} + +static void +android_saf_root_close (struct android_vnode *vnode) +{ + struct android_saf_root_vnode *vp; + int save_errno; + + vp = (struct android_saf_root_vnode *) vnode; + save_errno = errno; + xfree (vp->authority); + xfree (vp); + errno = save_errno; +} + +static int +android_saf_root_unlink (struct android_vnode *vnode) +{ + errno = EROFS; + return -1; +} + +static int +android_saf_root_symlink (const char *target, + struct android_vnode *vnode) +{ + errno = EROFS; + return -1; +} + +static int +android_saf_root_rmdir (struct android_vnode *vnode) +{ + errno = EROFS; + return -1; +} + +static int +android_saf_root_rename (struct android_vnode *src, + struct android_vnode *dst, + bool keep_existing) +{ + errno = EROFS; + return -1; +} + +static int +android_saf_root_stat (struct android_vnode *vnode, + struct stat *statb) +{ + /* Make up some imaginary statistics for this vnode. */ + + memset (statb, 0, sizeof *statb); + statb->st_uid = getuid (); + statb->st_gid = getgid (); + statb->st_ino = 0; + statb->st_dev = -4; + statb->st_mode = S_IFDIR | S_IRUSR; + return 0; +} + +static int +android_saf_root_access (struct android_vnode *vnode, int mode) +{ + /* Validate MODE. */ + + if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK))) + { + errno = EINVAL; + return -1; + } + + /* Now, don't allow writing or executing this directory. */ + + if (mode != F_OK && (mode & (W_OK | X_OK))) + { + errno = EROFS; + return -1; + } + + return 0; +} + +static int +android_saf_root_mkdir (struct android_vnode *vnode, mode_t mode) +{ + errno = EROFS; + return -1; +} + +static struct dirent * +android_saf_root_readdir (struct android_vdir *vdir) +{ + static struct dirent *dirent; + jobject string; + const char *chars; + size_t length, size; + struct android_saf_root_vdir *dir; + + dir = (struct android_saf_root_vdir *) vdir; + + if (dir->i == dir->length) + { + /* At the end of the stream. Free dirent and return NULL. */ + + xfree (dirent); + dirent = NULL; + return NULL; + } + + /* Get this string. */ + string = (*android_java_env)->GetObjectArrayElement (android_java_env, + dir->array, dir->i++); + android_exception_check (); + chars = (*android_java_env)->GetStringUTFChars (android_java_env, + (jstring) string, + NULL); + android_exception_check_nonnull ((void *) chars, string); + + /* Figure out how large it is, and then resize dirent to fit. */ + length = strlen (chars) + 1; + size = offsetof (struct dirent, d_name) + length; + dirent = xrealloc (dirent, size); + + /* Clear dirent. */ + memset (dirent, 0, size); + + /* Fill in the generic directory information and copy the string + over. */ + dirent->d_ino = 0; + dirent->d_off = 0; + dirent->d_reclen = size; + dirent->d_type = DT_DIR; + strcpy (dirent->d_name, chars); + + /* Release the string data and the local reference to STRING. */ + (*android_java_env)->ReleaseStringUTFChars (android_java_env, + (jstring) string, chars); + ANDROID_DELETE_LOCAL_REF (string); + return dirent; +} + +static void +android_saf_root_closedir (struct android_vdir *vdir) +{ + struct android_saf_root_vdir *dir, **next, *tem; + + dir = (struct android_saf_root_vdir *) vdir; + + /* If the ``directory file descriptor'' has been opened, close + it. */ + + if (dir->fd != -1) + close (dir->fd); + + /* Delete the local reference to the file name array. */ + ANDROID_DELETE_LOCAL_REF (dir->array); + + /* Free the authority name if set. */ + xfree (dir->authority); + + /* Now unlink this directory. */ + + for (next = &all_saf_root_vdirs; (tem = *next);) + { + if (tem == dir) + *next = dir->next; + else + next = &(*next)->next; + } + + /* Free the directory itself. */ + xfree (dir); +} + +static int +android_saf_root_dirfd (struct android_vdir *vdir) +{ + struct android_saf_root_vdir *dir; + + dir = (struct android_saf_root_vdir *) vdir; + + /* Since `android_saf_root_opendir' tries to avoid opening a file + descriptor if readdir isn't called, dirfd can fail if open fails. + + open sets errno to a set of errors different from what POSIX + stipulates for dirfd, but for ease of implementation the open + errors are used instead. */ + + if (dir->fd >= 0) + return dir->fd; + + dir->fd = open ("/dev/null", O_RDONLY | O_CLOEXEC); + return dir->fd; +} + +static struct android_vdir * +android_saf_root_opendir (struct android_vnode *vnode) +{ + struct android_saf_root_vnode *vp; + jobjectArray array; + jmethodID method; + jbyteArray authority; + struct android_saf_root_vdir *dir; + size_t length; + + vp = (struct android_saf_root_vnode *) vnode; + + if (vp->authority) + { + /* Build a string containing the authority. */ + length = strlen (vp->authority); + authority = (*android_java_env)->NewByteArray (android_java_env, + length); + android_exception_check (); + + /* Copy the authority name to that byte array. */ + (*android_java_env)->SetByteArrayRegion (android_java_env, + authority, 0, length, + (jbyte *) vp->authority); + + /* Acquire a list of every tree provided by this authority. */ + + method = service_class.get_document_trees; + array + = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, + emacs_service, + service_class.class, + method, authority); + android_exception_check_1 (authority); + ANDROID_DELETE_LOCAL_REF (authority); + + /* Ascertain the length of the array. If it is empty or NULL, + return ENOENT. */ + + if (!array) + { + errno = ENOENT; + return NULL; + } + + length = (*android_java_env)->GetArrayLength (android_java_env, array); + + if (!length) + { + ANDROID_DELETE_LOCAL_REF (array); + errno = ENOENT; + return NULL; + } + + /* Now allocate the directory stream. It will retain a local + reference to the array, and should thus only be used within the + same JNI local reference frame. */ + + dir = xmalloc (sizeof *dir); + dir->vdir.readdir = android_saf_root_readdir; + dir->vdir.closedir = android_saf_root_closedir; + dir->vdir.dirfd = android_saf_root_dirfd; + dir->fd = -1; + dir->array = array; + dir->length = length; + dir->i = 0; + dir->authority = xstrdup (vp->authority); + + /* Link this stream onto the list of all SAF root directory + streams. */ + dir->next = all_saf_root_vdirs; + all_saf_root_vdirs = dir; + return &dir->vdir; + } + + /* Acquire a list of every document authority. */ + + method = service_class.get_document_authorities; + array = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, + emacs_service, + service_class.class, + method); + android_exception_check (); + + if (!array) + emacs_abort (); + + /* Now allocate the directory stream. It will retain a local + reference to the array, and should thus only be used within the + same JNI local reference frame. */ + + dir = xmalloc (sizeof *dir); + dir->vdir.readdir = android_saf_root_readdir; + dir->vdir.closedir = android_saf_root_closedir; + dir->vdir.dirfd = android_saf_root_dirfd; + dir->fd = -1; + dir->array = array; + dir->length = (*android_java_env)->GetArrayLength (android_java_env, + array); + dir->i = 0; + dir->authority = NULL; + + /* Link this stream onto the list of all SAF root directory + streams. */ + dir->next = all_saf_root_vdirs; + all_saf_root_vdirs = dir; + return &dir->vdir; +} + +/* Find the vnode designated by NAME relative to the root of the + storage directory. + + If NAME is empty or a single leading separator character, return a + vnode representing the storage directory itself. + + If NAME actually resides in a parent directory, look for it within + the vnode representing the content directory. */ + +static struct android_vnode * +android_saf_root_initial (char *name, size_t length) +{ + struct android_saf_root_vnode temp; + + temp.vnode.ops = &saf_root_vfs_ops; + temp.vnode.type = ANDROID_VNODE_SAF_ROOT; + temp.vnode.flags = 0; + temp.authority = NULL; + + return android_saf_root_name (&temp.vnode, name, length); +} + +/* Return any open SAF root directory stream for which dirfd has + returned the file descriptor DIRFD. Return NULL otherwise. */ + +static struct android_saf_root_vdir * +android_saf_root_get_directory (int dirfd) +{ + struct android_saf_root_vdir *dir; + + for (dir = all_saf_root_vdirs; dir; dir = dir->next) + { + if (dir->fd == dirfd && dirfd != -1) + return dir; + } + + return NULL; +} + + + +/* Functions common to both SAF directory and file nodes. */ + +/* Return file status for the document designated by ID_NAME within + the document tree identified by URI_NAME. + + If the file status is available, place it within *STATB and return + 0. If not, return -1 and set errno to EPERM. */ + +static int +android_saf_stat (const char *uri_name, const char *id_name, + struct stat *statb) +{ + jmethodID method; + jstring uri, id; + jobject status; + jlong mode, size, mtim, *longs; + + /* Build strings for both URI and ID. */ + uri = (*android_java_env)->NewStringUTF (android_java_env, uri_name); + android_exception_check (); + + if (id_name) + { + id = (*android_java_env)->NewStringUTF (android_java_env, + id_name); + android_exception_check_1 (uri); + } + else + id = NULL; + + /* Try to retrieve the file status. */ + method = service_class.stat_document; + status = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, + emacs_service, + service_class.class, + method, uri, id); + + /* Check for exceptions and release unneeded local references. */ + + if (id) + { + android_exception_check_2 (uri, id); + ANDROID_DELETE_LOCAL_REF (id); + } + else + android_exception_check_1 (uri); + + ANDROID_DELETE_LOCAL_REF (uri); + + /* Check for failure. */ + + if (!status) + { + errno = EPERM; + return -1; + } + + /* Read the file status from the array returned. */ + + longs = (*android_java_env)->GetLongArrayElements (android_java_env, + status, NULL); + android_exception_check_nonnull (longs, status); + mode = longs[0]; + size = longs[1]; + mtim = longs[2]; + (*android_java_env)->ReleaseLongArrayElements (android_java_env, status, + longs, JNI_ABORT); + ANDROID_DELETE_LOCAL_REF (status); + + /* Fill in STATB with this information. */ + memset (statb, 0, sizeof *statb); + statb->st_size = MAX (0, MIN (TYPE_MAXIMUM (off_t), size)); + statb->st_mode = mode; + statb->st_mtim.tv_sec = mtim / 1000; + statb->st_mtim.tv_nsec = (mtim % 1000) * 1000000; + statb->st_uid = getuid (); + statb->st_gid = getgid (); + return 0; +} + +/* Detect if Emacs has access to the document designated by the the + documen ID ID_NAME within the tree URI_NAME. If ID_NAME is NULL, + use the document ID in URI_NAME itself. + + If WRITABLE, also check that the file is writable, which is true + if it is either a directory or its flags contains + FLAG_SUPPORTS_WRITE. + + Value is 0 if the file is accessible, and -1 with errno set + appropriately if not. */ + +static int +android_saf_access (const char *uri_name, const char *id_name, + bool writable) +{ + jmethodID method; + jstring uri, id; + jint rc; + + /* Build strings for both URI and ID. */ + uri = (*android_java_env)->NewStringUTF (android_java_env, uri_name); + android_exception_check (); + + if (id_name) + { + id = (*android_java_env)->NewStringUTF (android_java_env, + id_name); + android_exception_check_1 (uri); + } + else + id = NULL; + + /* Try to retrieve the file status. */ + method = service_class.access_document; + rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env, + emacs_service, + service_class.class, + method, uri, id, + (jboolean) writable); + + /* Check for exceptions and release unneeded local references. */ + + if (id) + { + android_exception_check_2 (uri, id); + ANDROID_DELETE_LOCAL_REF (id); + } + else + android_exception_check_1 (uri); + + ANDROID_DELETE_LOCAL_REF (uri); + + switch (rc) + { + case -1: + /* -1 means it doesn't exist. */ + errno = ENOENT; + return -1; + + case -2: + /* -2 means access has been denied. */ + errno = EACCES; + return -1; + + case -3: + /* -3 refers to an internal error. */ + errno = EIO; + return -1; + } + + /* Return success. */ + return 0; +} + + + +/* SAF directory vnode. A file within a SAF directory tree is + identified by the URI of the directory tree itself, an opaque + ``file identifier'' value, and a display name. This information is + recorded in each vnode representing either a directory or a file + itself. */ + +struct android_saf_tree_vnode +{ + /* The vnode data itself. */ + struct android_vnode vnode; + + /* The URI of the directory tree represented. This is Java string + data in ``modified UTF format'', which is essentially a modified + UTF-8 format capable of storing NULL bytes while also utilizing + NULL termination. */ + const char *tree_uri; + + /* The ID of the document tree designated by TREE_URI. */ + char *tree_id; + + /* The document ID of the directory represented, or NULL if this is + the root directory of the tree. */ + char *document_id; + + /* The file name of this tree vnode. This is a ``path'' to the + file, where each directory component consists of the display name + of a directory leading up to a file within, terminated with a + directory separator character. */ + char *name; +}; + +struct android_saf_tree_vdir +{ + /* The virtual directory stream function table. */ + struct android_vdir vdir; + + /* The next directory in `all_saf_tree_vdirs'. */ + struct android_saf_tree_vdir *next; + + /* Name of this directory relative to the root file system. */ + char *name; + + /* Local reference to the cursor representing the directory + stream. */ + jobject cursor; + + /* The ``directory'' file descriptor used to identify this directory + stream, or -1. */ + int fd; +}; + +static struct android_vnode *android_saf_tree_name (struct android_vnode *, + char *, size_t); +static int android_saf_tree_open (struct android_vnode *, int, + mode_t, bool, int *, AAsset **); +static void android_saf_tree_close (struct android_vnode *); +static int android_saf_tree_unlink (struct android_vnode *); +static int android_saf_tree_symlink (const char *, struct android_vnode *); +static int android_saf_tree_rmdir (struct android_vnode *); +static int android_saf_tree_rename (struct android_vnode *, + struct android_vnode *, bool); +static int android_saf_tree_stat (struct android_vnode *, struct stat *); +static int android_saf_tree_access (struct android_vnode *, int); +static int android_saf_tree_mkdir (struct android_vnode *, mode_t); +static struct android_vdir *android_saf_tree_opendir (struct android_vnode *); + +/* Vector of VFS operations associated with SAF tree VFS nodes. */ + +static struct android_vops saf_tree_vfs_ops = + { + android_saf_tree_name, + android_saf_tree_open, + android_saf_tree_close, + android_saf_tree_unlink, + android_saf_tree_symlink, + android_saf_tree_rmdir, + android_saf_tree_rename, + android_saf_tree_stat, + android_saf_tree_access, + android_saf_tree_mkdir, + android_saf_tree_opendir, + }; + +/* Vector of VFS operations associated with SAF file VFS nodes. + Defined later in the next page. */ +static struct android_vops saf_file_vfs_ops; + +/* Vector of VFS operations associated with SAF ``new'' VFS nodes. + Defined two pages below. */ +static struct android_vops saf_new_vfs_ops; + +/* Chain of all open SAF directory streams. */ +static struct android_saf_tree_vdir *all_saf_tree_vdirs; + +/* Find the document ID of the file within TREE_URI designated by + NAME. + + NAME is a ``file name'' comprised of the display names of + individual files. Each constituent component prior to the last + must name a directory file within TREE_URI. + + Upon success, return 0 or 1 (contingent upon whether or not the + last component within NAME is a directory) and place the document + ID of the named file in ID. + + If the designated file doesn't exist, but the penultimate component + within NAME does and is also a directory, return -2 and place the + document ID of that directory within *ID. + + If the designated file can't be located, return -1. */ + +static int +android_document_id_from_name (const char *tree_uri, char *name, + char **id) +{ + jobjectArray result; + jstring uri; + jbyteArray java_name; + size_t length; + jint rc; + jmethodID method; + const char *doc_id; + + /* First, create the array that will hold the result. */ + result = (*android_java_env)->NewObjectArray (android_java_env, 1, + java_string_class, + NULL); + android_exception_check (); + + /* Next, create the string for the tree URI and name. */ + length = strlen (name); + java_name = (*android_java_env)->NewByteArray (android_java_env, length); + android_exception_check_1 (result); + (*android_java_env)->SetByteArrayRegion (android_java_env, java_name, + 0, length, (jbyte *) name); + uri = (*android_java_env)->NewStringUTF (android_java_env, tree_uri); + android_exception_check_2 (result, java_name); + + /* Now, call documentIdFromName. */ + method = service_class.document_id_from_name; + rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env, + emacs_service, + service_class.class, + method, + uri, java_name, + result); + android_exception_check_3 (result, uri, java_name); + ANDROID_DELETE_LOCAL_REF (uri); + ANDROID_DELETE_LOCAL_REF (java_name); + + /* If rc indicates failure, don't try to copy from result. */ + + if (rc == -1) + { + ANDROID_DELETE_LOCAL_REF (result); + goto finish; + } + + eassert (rc == -2 || rc >= 0); + + /* Otherwise, obtain the contents of the string returned in Java + ``UTF-8'' encoding. */ + uri = (*android_java_env)->GetObjectArrayElement (android_java_env, + result, 0); + android_exception_check_nonnull (uri, result); + ANDROID_DELETE_LOCAL_REF (result); + + doc_id = (*android_java_env)->GetStringUTFChars (android_java_env, + uri, NULL); + android_exception_check_nonnull ((void *) doc_id, uri); + + /* Make *ID its copy. */ + *id = xstrdup (doc_id); + + /* And release it. */ + (*android_java_env)->ReleaseStringUTFChars (android_java_env, + (jstring) uri, doc_id); + ANDROID_DELETE_LOCAL_REF (uri); + + finish: + return rc; +} + +static struct android_vnode * +android_saf_tree_name (struct android_vnode *vnode, char *name, + size_t length) +{ + char *remainder; + int rc; + struct android_saf_tree_vnode *vp, *new; + size_t vp_length; + char *filename, *fill, *doc_id, *end; + struct android_saf_root_vnode root; + struct android_saf_tree_vnode tree; + + /* Canonicalize NAME. */ + remainder = android_vfs_canonicalize_name (name, &length); + + /* If remainder is set, it's a name relative to the root vnode. */ + if (remainder) + goto parent_vnode; + + /* If LENGTH is empty or NAME is a single directory separator, + return a copy of this vnode. */ + + if (length < 1 || (*name == '/' && length == 1)) + { + vp = xmalloc (sizeof *vp); + memcpy (vp, vnode, sizeof *vp); + + /* Duplicate the information contained within VNODE. */ + + vp->tree_uri = xstrdup (vp->tree_uri); + vp->tree_id = xstrdup (vp->tree_id); + vp->name = xstrdup (vp->name); + + if (vp->document_id) + vp->document_id = xstrdup (vp->name); + + return &vp->vnode; + } + + /* Now, search for the document ID of the file designated by NAME + relative to this vnode. */ + + vp = (struct android_saf_tree_vnode *) vnode; + vp_length = strlen (vp->name); + + /* If NAME starts with a directory separator, move it past that. */ + + if (*name == '/') + name++, length -= 1; + + /* Concatenate VP->name with NAME. Leave one byte at the end for an + extra trailing directory separator. */ + + filename = xmalloc (vp_length + length + 2); + fill = stpcpy (filename, vp->name); + fill = stpcpy (fill, name); + + /* And search for a document ID in the result. */ + rc = android_document_id_from_name (vp->tree_uri, name, + &doc_id); + + if (rc < 0) + { + if (rc == -2) + { + /* This is a vnode representing a nonexistent file in a real + directory, so create a vnode whose sole use is to create + the file. */ + + new = xmalloc (sizeof *new); + new->vnode.ops = &saf_new_vfs_ops; + new->vnode.type = ANDROID_VNODE_SAF_NEW; + new->vnode.flags = 0; + + /* Here, doc_id is actually the ID of the penultimate + component in NAME. */ + + new->document_id = doc_id; + new->tree_uri = xstrdup (vp->tree_uri); + new->tree_id = xstrdup (vp->tree_id); + new->name = filename; + return &new->vnode; + } + + /* The document ID can't be found. */ + xfree (filename); + errno = ENOENT; + return NULL; + } + + if (!rc) + { + /* rc set to 0 means that NAME is a regular file. Detect if + NAME is supposed to be a directory; if it is, set errno to + ENODIR. */ + + if (name[length - 1] == '/') + { + xfree (filename); + xfree (doc_id); + errno = ENOTDIR; + return NULL; + } + } + + /* So this is either a directory or really a file. Fortunately, + directory and file vnodes share everything in common except for a + few file operations, so create a new directory vnode with the new + file name and return it. */ + + new = xmalloc (sizeof *new); + new->vnode.ops = (rc ? &saf_tree_vfs_ops + : &saf_file_vfs_ops); + new->vnode.type = (rc ? ANDROID_VNODE_SAF_TREE + : ANDROID_VNODE_SAF_FILE); + new->vnode.flags = 0; + + if (rc) + { + /* If fill[-1] is not a directory separator character, append + one to the end of filename. */ + + if (fill[-1] != '/') + { + *fill++ = '/'; + *fill = '\0'; + } + } + + new->document_id = doc_id; + new->tree_uri = xstrdup (vp->tree_uri); + new->tree_id = xstrdup (vp->tree_id); + new->name = filename; + return &new->vnode; + + parent_vnode: + vp = (struct android_saf_tree_vnode *) vnode; + + /* .. was encountered and the parent couldn't be found through + stripping off preceding components. + + Find the parent vnode and name the rest of NAME starting from + there. */ + + if (!vp->document_id) + { + /* VP->document_id is NULL, meaning this is the root of this + directory tree. The parent vnode is an SAF root vnode with + VP->tree_uri's authority. */ + + root.vnode.ops = &saf_root_vfs_ops; + root.vnode.type = ANDROID_VNODE_SAF_ROOT; + root.vnode.flags = 0; + + /* Find the authority from the URI. */ + + fill = (char *) vp->tree_uri; + + if (strncmp (fill, "content://", 10)) + emacs_abort (); + + /* Skip the content header. */ + fill += sizeof "content://" - 1; + + /* The authority segment of the URI is between here and the + next slash. */ + + end = strchr (fill, '/'); + + if (!end) + emacs_abort (); + + root.authority = xmalloc (end - fill + 1); + memcpy (root.authority, fill, end - fill); + root.authority[end - fill] = '\0'; + + /* Now search using this vnode. */ + vnode = (*root.vnode.ops->name) (&root.vnode, remainder, + strlen (remainder)); + xfree (root.authority); + return vnode; + } + + /* Otherwise, strip off the last directory component. */ + + fill = strrchr (vp->name, '/'); + if (!fill) + emacs_abort (); + + /* Create a new vnode at the top of the directory tree, and search + for remainder from there. */ + + tree.vnode.ops = &saf_tree_vfs_ops; + tree.vnode.type = ANDROID_VNODE_SAF_TREE; + tree.vnode.flags = 0; + tree.document_id = NULL; + tree.name = "/"; + tree.tree_uri = vp->tree_uri; + tree.tree_id = vp->tree_id; + + length = strlen (remainder + (*remainder == '/')); + filename = xmalloc (fill - vp->name + length + 2); + fill = mempcpy (filename, vp->name, + /* Include the separator character (*FILL) within + this copy. */ + fill - vp->name + 1); + /* Skip a leading separator in REMAINDER. */ + strcpy (fill, remainder + (*remainder == '/')); + + /* Use this filename to find a vnode relative to the start of this + tree. */ + + vnode = android_saf_tree_name (&tree.vnode, filename, + strlen (filename)); + xfree (filename); + return vnode; +} + +static int +android_saf_tree_open (struct android_vnode *vnode, int flags, + mode_t mode, bool asset_p, int *fd, + AAsset **asset) +{ + /* Don't allow opening this special directory. */ + errno = ENOSYS; + return -1; +} + +static void +android_saf_tree_close (struct android_vnode *vnode) +{ + struct android_saf_tree_vnode *vp; + int save_errno; + + vp = (struct android_saf_tree_vnode *) vnode; + + save_errno = errno; + xfree ((void *) vp->tree_uri); + xfree (vp->tree_id); + xfree (vp->name); + xfree (vp->document_id); + xfree (vp); + errno = save_errno; +} + +static int +android_saf_tree_unlink (struct android_vnode *vnode) +{ + errno = EISDIR; + return -1; +} + +static int +android_saf_tree_symlink (const char *target, struct android_vnode *vnode) +{ + errno = EPERM; + return -1; +} + +static int +android_saf_tree_rmdir (struct android_vnode *vnode) +{ + /* TODO */ + errno = ENOSYS; + return -1; +} + +static int +android_saf_tree_rename (struct android_vnode *src, + struct android_vnode *dst, + bool keep_existing) +{ + /* TODO */ + errno = ENOSYS; + return -1; +} + +static int +android_saf_tree_stat (struct android_vnode *vnode, + struct stat *statb) +{ + struct android_saf_tree_vnode *vp; + + vp = (struct android_saf_tree_vnode *) vnode; + + return android_saf_stat (vp->tree_uri, vp->document_id, + statb); +} + +static int +android_saf_tree_access (struct android_vnode *vnode, int mode) +{ + struct android_saf_tree_vnode *vp; + + vp = (struct android_saf_tree_vnode *) vnode; + + /* Validate MODE. */ + + if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK))) + { + errno = EINVAL; + return -1; + } + + return android_saf_access (vp->tree_uri, vp->document_id, + mode & W_OK); +} + +static int +android_saf_tree_mkdir (struct android_vnode *vnode, mode_t mode) +{ + /* Since tree vnodes represent files that already exist, return + EEXIST. */ + errno = EEXIST; + return -1; +} + +/* Open a database Cursor containing each directory entry within the + supplied SAF tree vnode VP. + + Value is NULL upon failure, a local reference to the Cursor object + otherwise. */ + +static jobject +android_saf_tree_opendir_1 (struct android_saf_tree_vnode *vp) +{ + jobject uri, id, cursor; + jmethodID method; + + /* Build strings for both URI and ID. */ + uri = (*android_java_env)->NewStringUTF (android_java_env, + vp->tree_uri); + android_exception_check (); + + if (vp->document_id) + { + id = (*android_java_env)->NewStringUTF (android_java_env, + vp->document_id); + android_exception_check_1 (uri); + } + else + id = NULL; + + /* Try to open the cursor. */ + method = service_class.open_document_directory; + cursor + = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, + emacs_service, + service_class.class, + method, uri, id); + + if (id) + { + android_exception_check_2 (id, uri); + ANDROID_DELETE_LOCAL_REF (id); + } + else + android_exception_check_1 (uri); + + ANDROID_DELETE_LOCAL_REF (uri); + + /* Return the resulting cursor. */ + return cursor; +} + +static struct dirent * +android_saf_tree_readdir (struct android_vdir *vdir) +{ + struct android_saf_tree_vdir *dir; + static struct dirent *dirent; + jobject entry, d_name; + jint d_type; + jmethodID method; + size_t length, size; + const char *chars; + + dir = (struct android_saf_tree_vdir *) vdir; + + /* Try to read one entry from the cursor. */ + method = service_class.read_directory_entry; + entry + = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, + emacs_service, + service_class.class, + method, dir->cursor); + android_exception_check (); + + /* If ENTRY is NULL, we're at the end of the directory. */ + + if (!entry) + { + xfree (entry); + entry = NULL; + return NULL; + } + + /* Load both fields from ENTRY. */ + d_name = (*android_java_env)->GetObjectField (android_java_env, entry, + entry_class.d_name); + if (!d_name) + { + /* If an error transpires, d_name is set to NULL. */ + (*android_java_env)->ExceptionClear (android_java_env); + ANDROID_DELETE_LOCAL_REF (entry); + + /* XXX: what would be a better error indication? */ + errno = EIO; + return NULL; + } + + /* d_type is 1 if this is a directory, and 0 if it's a regular + file. */ + d_type = (*android_java_env)->GetIntField (android_java_env, entry, + entry_class.d_type); + ANDROID_DELETE_LOCAL_REF (entry); + + /* Copy the name of the directory over. */ + chars = (*android_java_env)->GetStringUTFChars (android_java_env, + (jstring) d_name, + NULL); + android_exception_check_nonnull ((void *) chars, d_name); + + /* Figure out how large it is, and then resize dirent to fit. */ + length = strlen (chars) + 1; + size = offsetof (struct dirent, d_name) + length; + dirent = xrealloc (dirent, size); + + /* Clear dirent. */ + memset (dirent, 0, size); + + /* Fill in the generic directory information and copy the string + over. */ + dirent->d_ino = 0; + dirent->d_off = 0; + dirent->d_reclen = size; + dirent->d_type = d_type ? DT_DIR : DT_UNKNOWN; + strcpy (dirent->d_name, chars); + + /* Release the string data and the local reference to STRING. */ + (*android_java_env)->ReleaseStringUTFChars (android_java_env, + (jstring) d_name, + chars); + ANDROID_DELETE_LOCAL_REF (d_name); + return dirent; +} + +static void +android_saf_tree_closedir (struct android_vdir *vdir) +{ + struct android_saf_tree_vdir *dir, **next, *tem; + + dir = (struct android_saf_tree_vdir *) vdir; + + /* dir->name is allocated by asprintf, which uses regular + malloc. */ + free (dir->name); + + /* Yes, DIR->cursor is a local reference. */ + ANDROID_DELETE_LOCAL_REF (dir->cursor); + + /* If the ``directory file descriptor'' has been opened, close + it. */ + if (dir->fd != -1) + close (dir->fd); + + /* Now unlink this directory. */ + + for (next = &all_saf_tree_vdirs; (tem = *next);) + { + if (tem == dir) + *next = dir->next; + else + next = &(*next)->next; + } + + xfree (dir); +} + +static int +android_saf_tree_dirfd (struct android_vdir *vdir) +{ + struct android_saf_tree_vdir *dir; + + dir = (struct android_saf_tree_vdir *) vdir; + + /* Since `android_saf_tree_opendir' tries to avoid opening a file + descriptor if readdir isn't called, dirfd can fail if open fails. + + open sets errno to a set of errors different from what POSIX + stipulates for dirfd, but for ease of implementation the open + errors are used instead. */ + + if (dir->fd >= 0) + return dir->fd; + + dir->fd = open ("/dev/null", O_RDONLY | O_CLOEXEC); + return dir->fd; +} + +static struct android_vdir * +android_saf_tree_opendir (struct android_vnode *vnode) +{ + struct android_saf_tree_vnode *vp; + struct android_saf_tree_vdir *dir; + char *fill, *end; + jobject cursor; + char component[PATH_MAX]; + + vp = (struct android_saf_tree_vnode *) vnode; + + /* First, fill the directory stream with the right functions and + file name. */ + + dir = xmalloc (sizeof *dir); + dir->vdir.readdir = android_saf_tree_readdir; + dir->vdir.closedir = android_saf_tree_closedir; + dir->vdir.dirfd = android_saf_tree_dirfd; + + /* Find the authority from the URI. */ + + fill = (char *) vp->tree_uri; + + if (strncmp (fill, "content://", 10)) + emacs_abort (); + + /* Skip the content header. */ + fill += sizeof "content://" - 1; + + /* The authority segment of the URI is between here and the + next slash. */ + + end = strchr (fill, '/'); + + if (!end) + emacs_abort (); + + if (end - fill >= PATH_MAX) + { + errno = ENAMETOOLONG; + xfree (dir); + return NULL; + } + + /* Copy the authority over. */ + + memcpy (component, fill, end - fill); + component[end - fill] = '\0'; + + if (asprintf (&dir->name, "/content/storage/%s/%s%s", + component, vp->tree_id, vp->name) < 0) + { + /* Out of memory. */ + xfree (dir); + memory_full (0); + } + + /* Now open a cursor that iterates through each file in this + directory. */ + + cursor = android_saf_tree_opendir_1 (vp); + + if (!cursor) + { + xfree (dir); + xfree (dir->name); + errno = ENOENT; + return NULL; + } + + dir->cursor = cursor; + dir->fd = -1; + dir->next = all_saf_tree_vdirs; + all_saf_tree_vdirs = dir; + return &dir->vdir; +} + +/* Create a vnode designating the file NAME within a directory tree + whose identifier is TREE. As with all other `name' functions, NAME + may be modified. + + AUTHORITY is the name of the content provider authority that is + offering TREE. + + Value is NULL if no document tree or provider by those names + exists, or some other error takes place (for example, if TREE and + AUTHORITY aren't encoded correctly.) */ + +static struct android_vnode * +android_saf_tree_from_name (char *name, const char *tree, + const char *authority) +{ + struct android_saf_tree_vnode root; + jobject tree_string, authority_string, result; + jmethodID method; + const char *uri; + struct android_vnode *vp; + + /* Assume that TREE and NAME are in ``modified UTF-8 format''. */ + tree_string = (*android_java_env)->NewStringUTF (android_java_env, + tree); + android_exception_check (); + + authority_string + = (*android_java_env)->NewStringUTF (android_java_env, + authority); + android_exception_check_1 (tree_string); + + /* Now create the URI and detect if Emacs has the rights to access + it. */ + + method = service_class.get_tree_uri; + result + = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, + emacs_service, + service_class.class, + method, tree_string, + authority_string); + android_exception_check_2 (tree_string, authority_string); + ANDROID_DELETE_LOCAL_REF (tree_string); + ANDROID_DELETE_LOCAL_REF (authority_string); + + /* If it doesn't, return NULL and set errno to ENOENT. */ + + if (!result) + { + errno = ENOENT; + return NULL; + } + + /* Otherwise, decode this string. */ + uri = (*android_java_env)->GetStringUTFChars (android_java_env, result, + NULL); + android_exception_check_nonnull ((void *) uri, result); + + /* Fill in root.tree_uri with values that represent the root of this + document tree. */ + + root.vnode.ops = &saf_tree_vfs_ops; + root.vnode.type = ANDROID_VNODE_SAF_TREE; + root.vnode.flags = 0; + root.tree_uri = uri; + root.tree_id = (char *) tree; + root.document_id = NULL; + root.name = "/"; + + vp = (*root.vnode.ops->name) (&root.vnode, name, strlen (name)); + (*android_java_env)->ReleaseStringUTFChars (android_java_env, + (jstring) result, uri); + ANDROID_DELETE_LOCAL_REF (result); + return vp; +} + +/* Return any open SAF tree directory stream for which dirfd has + returned the file descriptor DIRFD. Return NULL otherwise. */ + +static struct android_saf_tree_vdir * +android_saf_tree_get_directory (int dirfd) +{ + struct android_saf_tree_vdir *dir; + + for (dir = all_saf_tree_vdirs; dir; dir = dir->next) + { + if (dir->fd == dirfd && dirfd != -1) + return dir; + } + + return NULL; +} + + + +/* SAF file vnode. The information used to uniquely identify a file + is identical to that used to identify an SAF directory, but the + vnode operations are different. */ + +/* Define `struct android_saf_file_vnode' to be identical to a file + vnode. */ + +#define android_saf_file_vnode android_saf_tree_vnode + +/* Structure describing an open ParcelFileDescriptor. */ + +struct android_parcel_fd +{ + /* The next open parcel file descriptor. */ + struct android_parcel_fd *next; + + /* Global reference to this parcel file descriptor. */ + jobject descriptor; + + /* The modification time of this parcel file descriptor, or + `invalid_timespec'. */ + struct timespec mtime; + + /* The file descriptor itself. */ + int fd; +}; + +static struct android_vnode *android_saf_file_name (struct android_vnode *, + char *, size_t); +static int android_saf_file_open (struct android_vnode *, int, + mode_t, bool, int *, AAsset **); +static int android_saf_file_unlink (struct android_vnode *); +static int android_saf_file_rmdir (struct android_vnode *); +static struct android_vdir *android_saf_file_opendir (struct android_vnode *); + +/* Vector of VFS operations associated with SAF tree VFS nodes. */ + +static struct android_vops saf_file_vfs_ops = + { + android_saf_file_name, + android_saf_file_open, + android_saf_tree_close, + android_saf_file_unlink, + android_saf_tree_symlink, + android_saf_file_rmdir, + android_saf_tree_rename, + android_saf_tree_stat, + android_saf_tree_access, + android_saf_tree_mkdir, + android_saf_file_opendir, + }; + +/* Chain of all parcel file descriptors currently open. */ +static struct android_parcel_fd *open_parcel_fds; + +static struct android_vnode * +android_saf_file_name (struct android_vnode *vnode, char *name, + size_t length) +{ + struct android_saf_file_vnode *vp; + + /* If LENGTH is empty, make a copy of this vnode and return it. */ + + if (length < 1) + { + vp = xmalloc (sizeof *vp); + memcpy (vp, vnode, sizeof *vp); + + /* Duplicate the information contained within VNODE. */ + + vp->tree_uri = xstrdup (vp->tree_uri); + vp->tree_id = xstrdup (vp->tree_id); + vp->name = xstrdup (vp->name); + vp->document_id = xstrdup (vp->name); + + return &vp->vnode; + } + + /* A file vnode has no children of its own. */ + errno = ENOTDIR; + return NULL; +} + +static int +android_saf_file_open (struct android_vnode *vnode, int flags, + mode_t mode, bool asset_p, int *fd_return, + AAsset **asset) +{ + struct android_saf_file_vnode *vp; + jobject uri, id, descriptor; + jmethodID method; + jboolean trunc, write; + jint fd; + struct android_parcel_fd *info; + struct stat statb; + + /* Build strings for both the URI and ID. */ + + vp = (struct android_saf_file_vnode *) vnode; + uri = (*android_java_env)->NewStringUTF (android_java_env, + vp->tree_uri); + android_exception_check (); + id = (*android_java_env)->NewStringUTF (android_java_env, + vp->document_id); + android_exception_check_1 (uri); + + /* Open a parcel file descriptor according to flags. */ + + method = service_class.open_document; + trunc = flags & O_TRUNC; + write = ((flags & O_RDWR) == O_RDWR || (flags & O_WRONLY)); + descriptor + = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, + emacs_service, + service_class.class, + method, uri, id, + write, trunc); + android_exception_check_2 (uri, id); + ANDROID_DELETE_LOCAL_REF (uri); + ANDROID_DELETE_LOCAL_REF (id); + + if (!descriptor) + { + /* Assume that permission has been denied if DESCRIPTOR cannot + be opened. */ + errno = EPERM; + return -1; + } + + /* Allocate a record for this file descriptor. Parcel file + descriptors should be closed using their own `close' function, + which takes care of notifying the source that it has been + closed. */ + info = xmalloc (sizeof *info); + + /* Now obtain the file descriptor. */ + fd = (*android_java_env)->CallIntMethod (android_java_env, + descriptor, + fd_class.get_fd); + android_exception_check_1 (descriptor); + + /* Create a global reference to descriptor. */ + info->descriptor + = (*android_java_env)->NewGlobalRef (android_java_env, + descriptor); + + if (!info->descriptor) + { + /* If the global reference can't be created, delete + descriptor. */ + (*android_java_env)->ExceptionClear (android_java_env); + (*android_java_env)->CallVoidMethod (android_java_env, + descriptor, + fd_class.close); + (*android_java_env)->ExceptionClear (android_java_env); + ANDROID_DELETE_LOCAL_REF (descriptor); + + /* Free INFO. */ + xfree (info); + + /* Set errno to EMFILE and return. */ + errno = EMFILE; + return -1; + } + + /* Delete the local ref to DESCRIPTOR. */ + ANDROID_DELETE_LOCAL_REF (descriptor); + + /* Try to retrieve the modification time of this file from the + content provider. */ + + if (!android_saf_stat (vp->tree_uri, vp->document_id, + &statb)) + info->mtime = statb.st_mtim; + else + info->mtime = invalid_timespec (); + + /* Set info->fd and chain it onto the list. */ + info->fd = fd; + info->next = open_parcel_fds; + open_parcel_fds = info; + + /* Return the file descriptor. */ + *fd_return = fd; + return 0; +} + +static int +android_saf_file_unlink (struct android_vnode *vnode) +{ + /* TODO */ + errno = ENOSYS; + return -1; +} + +static int +android_saf_file_rmdir (struct android_vnode *vnode) +{ + errno = ENOTDIR; + return -1; +} + +static struct android_vdir * +android_saf_file_opendir (struct android_vnode *vnode) +{ + errno = ENOTDIR; + return NULL; +} + +/* Close FD if it's a parcel file descriptor and return true. + If FD isn't, return false. + + Such file descriptors need to be closed using a function + written in Java, to tell the sender that it has been + closed. */ + +static bool +android_close_parcel_fd (int fd) +{ + struct android_parcel_fd *tem, **next, *temp; + + for (next = &open_parcel_fds; (tem = *next);) + { + if (tem->fd == fd) + { + (*android_java_env)->CallVoidMethod (android_java_env, + tem->descriptor, + fd_class.close); + + /* Ignore exceptions for the same reason EINTR errors from + `close' should be ignored. */ + (*android_java_env)->ExceptionClear (android_java_env); + (*android_java_env)->DeleteGlobalRef (android_java_env, + tem->descriptor); + + temp = tem->next; + xfree (tem); + *next = temp; + + return true; + } + else + next = &(*next)->next; + } + + return false; +} + + + +/* SAF ``new'' vnodes. These nodes share their data structures + with tree and file vnodes, but represent files that don't actually + exist within a directory. In them, the document ID represents not + the file designated by the vnode itself, but rather its parent + directory. + + The only vops defined serve to create directories or files, at + which point the vnode becomes invalid. */ + +#define android_saf_new_vnode android_saf_tree_vnode + +static struct android_vnode *android_saf_new_name (struct android_vnode *, + char *, size_t); +static int android_saf_new_open (struct android_vnode *, int, + mode_t, bool, int *, AAsset **); +static int android_saf_new_unlink (struct android_vnode *); +static int android_saf_new_symlink (const char *, struct android_vnode *); +static int android_saf_new_rmdir (struct android_vnode *); +static int android_saf_new_rename (struct android_vnode *, + struct android_vnode *, bool); +static int android_saf_new_stat (struct android_vnode *, struct stat *); +static int android_saf_new_access (struct android_vnode *, int); +static int android_saf_new_mkdir (struct android_vnode *, mode_t); +static struct android_vdir *android_saf_new_opendir (struct android_vnode *); + +/* Vector of VFS operations associated with SAF new VFS nodes. */ + +static struct android_vops saf_new_vfs_ops = + { + android_saf_new_name, + android_saf_new_open, + android_saf_tree_close, + android_saf_new_unlink, + android_saf_new_symlink, + android_saf_new_rmdir, + android_saf_new_rename, + android_saf_new_stat, + android_saf_new_access, + android_saf_new_mkdir, + android_saf_new_opendir, + }; + +static struct android_vnode * +android_saf_new_name (struct android_vnode *vnode, char *name, + size_t length) +{ + struct android_saf_new_vnode *vp; + + /* If LENGTH is empty, make a copy of this vnode and return it. */ + + if (length < 1) + { + vp = xmalloc (sizeof *vp); + memcpy (vp, vnode, sizeof *vp); + + /* Duplicate the information contained within VNODE. */ + + vp->tree_uri = xstrdup (vp->tree_uri); + vp->tree_id = xstrdup (vp->tree_id); + vp->name = xstrdup (vp->name); + vp->document_id = xstrdup (vp->name); + + return &vp->vnode; + } + + /* A nonexistent vnode has no children of its own. */ + errno = ENOTDIR; + return NULL; +} + +static int +android_saf_new_open (struct android_vnode *vnode, int flags, + mode_t mode, bool asset_p, int *fd_return, + AAsset **asset) +{ + struct android_saf_new_vnode *vp; + char *end; + jstring name, id, uri, new_id; + const char *new_doc_id; + jmethodID method; + + /* If creating a file wasn't intended, return ENOENT. */ + + if (!(flags & O_CREAT)) + { + errno = ENOENT; + return -1; + } + + /* If vp->name indicates that it's a directory, return ENOENT. */ + + vp = (struct android_saf_new_vnode *) vnode; + end = strrchr (vp->name, '/'); + + /* VP->name must contain at least one directory separator. */ + eassert (end); + + if (end[1] == '\0') + { + errno = ENOENT; + return -1; + } + + /* Otherwise, try to create a new document. First, build strings + for the name, ID and document URI. */ + + name = (*android_java_env)->NewStringUTF (android_java_env, + end + 1); + android_exception_check (); + id = (*android_java_env)->NewStringUTF (android_java_env, + vp->document_id); + android_exception_check_1 (name); + uri = (*android_java_env)->NewStringUTF (android_java_env, + vp->tree_uri); + android_exception_check_2 (name, id); + + /* Next, try to create a new document and retrieve its ID. */ + + method = service_class.create_document; + new_id = (*android_java_env)->CallNonvirtualObjectMethod (android_java_env, + emacs_service, + service_class.class, + method, uri, id, + name); + android_exception_check_3 (name, id, uri); + + /* Delete unused local references. */ + ANDROID_DELETE_LOCAL_REF (name); + ANDROID_DELETE_LOCAL_REF (id); + ANDROID_DELETE_LOCAL_REF (uri); + + if (!new_id) + { + /* The file couldn't be created for some reason. */ + errno = EIO; + return -1; + } + + /* Now, free VP->document_id and replace it with the service + document ID. */ + + new_doc_id = (*android_java_env)->GetStringUTFChars (android_java_env, + new_id, NULL); + android_exception_check_nonnull ((void *) new_doc_id, new_id); + + xfree (vp->document_id); + vp->document_id = xstrdup (new_doc_id); + + (*android_java_env)->ReleaseStringUTFChars (android_java_env, + new_id, new_doc_id); + ANDROID_DELETE_LOCAL_REF (new_id); + + /* Finally, transform this vnode into a file vnode and call its + `open' function. */ + vp->vnode.type = ANDROID_VNODE_SAF_FILE; + vp->vnode.ops = &saf_file_vfs_ops; + return (*vp->vnode.ops->open) (vnode, flags, mode, asset_p, + fd_return, asset); +} + +static int +android_saf_new_unlink (struct android_vnode *vnode) +{ + errno = ENOENT; + return -1; +} + +static int +android_saf_new_symlink (const char *target, struct android_vnode *vnode) +{ + errno = EPERM; + return -1; +} + +static int +android_saf_new_rmdir (struct android_vnode *vnode) +{ + errno = ENOENT; + return -1; +} + +static int +android_saf_new_rename (struct android_vnode *src, + struct android_vnode *dst, + bool keep_existing) +{ + errno = ENOENT; + return -1; +} + +static int +android_saf_new_stat (struct android_vnode *vnode, + struct stat *statb) +{ + errno = ENOENT; + return -1; +} + +static int +android_saf_new_access (struct android_vnode *vnode, int mode) +{ + if (mode != F_OK && !(mode & (W_OK | X_OK | R_OK))) + errno = EINVAL; + else + errno = ENOENT; + + return -1; +} + +static int +android_saf_new_mkdir (struct android_vnode *vnode, mode_t mode) +{ + /* TODO */ + errno = ENOSYS; + return -1; +} + +static struct android_vdir * +android_saf_new_opendir (struct android_vnode *vnode) +{ + errno = ENOENT; + return NULL; +} + + + +/* Root vnode. This vnode represents the root inode, and is a regular + Unix vnode with modifications to `name' that make it return asset + vnodes. */ + +static struct android_vnode *android_root_name (struct android_vnode *, + char *, size_t); + +/* Vector of VFS operations associated with Unix root filesystem VFS + nodes. */ + +static struct android_vops root_vfs_ops = + { + android_root_name, + android_unix_open, + android_unix_close, + android_unix_unlink, + android_unix_symlink, + android_unix_rmdir, + android_unix_rename, + android_unix_stat, + android_unix_access, + android_unix_mkdir, + android_unix_opendir, + }; + +/* Array of special named vnodes. */ + +static struct android_special_vnode special_vnodes[] = + { + { "assets", 6, android_afs_initial, }, + { "content", 7, android_content_initial, }, + }; + +static struct android_vnode * +android_root_name (struct android_vnode *vnode, char *name, + size_t length) +{ + char *component_end; + struct android_special_vnode *special; + size_t i; + + /* Skip any leading separator in NAME. */ + + if (*name == '/') + name++, length--; + + /* Look for the first directory separator. */ + component_end = strchr (name, '/'); + + /* If not there, use name + length. */ + + if (!component_end) + component_end = name + length; + else + /* Move past the spearator character. */ + component_end++; + + /* Now, find out if the first component is a special vnode; if so, + call its root lookup function with the rest of NAME there. */ + + for (i = 0; i < ARRAYELTS (special_vnodes); ++i) + { + special = &special_vnodes[i]; + + if (component_end - name == special->length + && !memcmp (special->name, name, special->length)) + return (*special->initial) (component_end, + length - special->length); + + /* Detect the case where a special is named with a trailing + directory separator. */ + + if (component_end - name == special->length + 1 + && !memcmp (special->name, name, special->length) + && name[special->length] == '/') + /* Make sure to include the directory separator. */ + return (*special->initial) (component_end - 1, + length - special->length); + } + + /* Otherwise, continue searching for a vnode normally. */ + return android_unix_name (vnode, name, length); +} + + + +/* File system lookup. */ + +/* Look up the vnode that designates NAME, a file name that is at + least N bytes. + + NAME may be either an absolute file name or a name relative to the + current working directory. It must not be longer than PATH_MAX + bytes. + + Value is NULL upon failure with errno set accordingly, or the + vnode. */ + +static struct android_vnode * +android_name_file (const char *name) +{ + char buffer[PATH_MAX + 1], *head; + const char *end; + size_t len; + int nslash, c; + struct android_vnode *vp; + + len = strlen (name); + if (len > PATH_MAX) + { + errno = ENAMETOOLONG; + return NULL; + } + + /* Now, try to ``normalize'' the file name by removing consecutive + slash characters while copying it to BUFFER. */ + + head = buffer; + nslash = 0; + for (end = name + len; name < end; ++name) + { + c = *name; + + switch (c) + { + case '/': + /* This is a directory separator character. Two consecutive + separator characters should be replaced by a single + character; more than three in a row means that the + section of the file name before the last slash character + should be discarded. */ + + if (!nslash) + *head++ = '/'; + + nslash++; + + if (nslash >= 3) + /* Return to the root directory. */ + head = buffer, *head++ = '/', nslash = 0; + break; + + default: + /* Otherwise, copy the file name over. */ + nslash = 0; + *head++ = *name; + break; + } + } + + /* Terminate the file name. */ + *head = '\0'; + + /* If HEAD is a relative file name, it can't reside inside the + virtual mounts; create a Unix vnode instead. */ + + if (head == buffer || buffer[0] != '/') + return android_unix_vnode (buffer); + + /* Start looking from the root vnode. */ + vp = &root_vnode.vnode; + + /* If buffer is empty, this will create a duplicate of the root + vnode. */ + return (*vp->ops->name) (vp, buffer + 1, head - buffer - 1); +} + + + +/* Initialize the virtual filesystem layer. Load the directory tree + from the given asset MANAGER (which should be a local reference + within ENV) that will be used to access assets in the future, and + create the root vnode. + + ENV should be a JNI environment valid for future calls to VFS + functions. */ + +void +android_vfs_init (JNIEnv *env, jobject manager) +{ + jclass old; + + android_init_assets (env, manager); + + /* Create the root vnode, which is used to locate all other + vnodes. */ + root_vnode.vnode.ops = &root_vfs_ops; + root_vnode.vnode.type = ANDROID_VNODE_UNIX; + root_vnode.vnode.flags = 0; + root_vnode.name_length = 1; + root_vnode.name = "/"; + + /* Initialize some required classes. */ + java_string_class = (*env)->FindClass (env, "java/lang/String"); + assert (java_string_class); + + old = java_string_class; + java_string_class = (jclass) (*env)->NewGlobalRef (env, + java_string_class); + assert (java_string_class); + (*env)->DeleteLocalRef (env, old); + + /* And initialize those used on Android 5.0 and later. */ + + if (android_get_current_api_level () < 21) + return; + + android_init_cursor_class (env); + android_init_entry_class (env); + android_init_fd_class (env); +} + +/* The replacement functions that follow have several major + drawbacks: + + The first is that CWD relative file names will always be Unix + vnodes, and looking up their parents will always return another + Unix vnode. For example, with the working directory set to + /sdcard: + + ../content/storage + + will find /sdcard/../content/storage on the Unix filesystem, + opposed to /content/storage within the ``content'' VFS. + + Emacs only uses file names expanded through `expand-file-name', so + this is unproblematic in practice. + + The second is that `..' components do not usually check that their + preceding component is a directory. This is a side effect of their + removal from file names as part of a pre-processing step before + they are opened. So, even if: + + /sdcard/foo.txt + + is a file, opening the directory: + + /sdcard/foo.txt/.. + + will be successful. + + The third is that the handling of `..' components relative to + another vnode hasn't been tested and is only assumed to work + because the code has been written. It does not pose a practical + problem, however, as Emacs only names files starting from the root + vnode. + + The fourth is that errno values from vnode operations don't always + reflect what the Unix system calls they emulate can return: for + example, `open' may return EIO, while trying to `mkdir' within + /content will return ENOENT instead of EROFS. This is a + consequence of how accessing a non-existent file may fail at vnode + lookup, instead of when a vop is used. This problem hasn't made a + sufficient nuisance of itself to justify its fix yet. + + The fifth is that trailing directory separators may be lost when + naming files relative to another vnode, as a consequence of an + optimization used to avoid allocating too much stack or heap + space. + + The sixth is that flags and other argument checking is nowhere near + exhaustive on vnode types other than Unix vnodes. + + And the final drawback is that directories cannot be directly + opened. Instead, `dirfd' must be called on a directory stream used + by `openat'. + + Caveat emptor! */ + +/* Open the VFS node designated by NAME, taking into account FLAGS and + MODE, both of which mean the same as they do in a call to `open'. + + Value is -1 upon failure with errno set accordingly, and a file + descriptor otherwise. */ + +int +android_open (const char *name, int flags, mode_t mode) +{ + struct android_vnode *vp; + int fd, rc; + + vp = android_name_file (name); + if (!vp) + return -1; + + rc = (*vp->ops->open) (vp, flags, mode, false, &fd, NULL); + (*vp->ops->close) (vp); + + if (rc < 0) + return -1; + + /* If rc is 1, then an asset file descriptor has been returned. + This is impossible, so assert that it doesn't transpire. */ + assert (rc != 1); + return fd; +} + +/* Unlink the VFS node designated by the specified FILE. + Value is -1 upon failure with errno set, and 0 otherwise. */ + +int +android_unlink (const char *name) +{ + struct android_vnode *vp; + int rc; + + vp = android_name_file (name); + if (!vp) + return -1; + + rc = (*vp->ops->unlink) (vp); + (*vp->ops->close) (vp); + return rc; +} + +/* Symlink the VFS node designated by LINKPATH to TARGET. + Value is -1 upon failure with errno set, and 0 otherwise. */ + +int +android_symlink (const char *target, const char *linkpath) +{ + struct android_vnode *vp; + int rc; + + vp = android_name_file (linkpath); + if (!vp) + return -1; + + rc = (*vp->ops->symlink) (target, vp); + (*vp->ops->close) (vp); + return rc; +} + +/* Remove the empty directory at the VFS node designated by NAME. + Value is -1 upon failure with errno set, and 0 otherwise. */ + +int +android_rmdir (const char *name) +{ + struct android_vnode *vp; + int rc; + + vp = android_name_file (name); + if (!vp) + return -1; + + rc = (*vp->ops->rmdir) (vp); + (*vp->ops->close) (vp); + return rc; +} + +/* Create a directory at the VFS node designated by NAME and the given + access MODE. Value is -1 upon failure with errno set, 0 + otherwise. */ + +int +android_mkdir (const char *name, mode_t mode) +{ + struct android_vnode *vp; + int rc; + + vp = android_name_file (name); + if (!vp) + return -1; + + rc = (*vp->ops->mkdir) (vp, mode); + (*vp->ops->close) (vp); + return rc; +} + +/* Rename the vnode designated by SRC to the vnode designated by DST. + If DST already exists, return -1 and set errno to EEXIST. + + SRCFD and DSTFD should be AT_FDCWD, or else value is -1 and errno + is ENOSYS. + + If the filesystem or vnodes containing either DST or SRC does not + support rename operations that also check for a preexisting + destination, return -1 and set errno to ENOSYS. + + Otherwise, value and errno are identical to that of Unix + `rename' with the same arguments. */ + +int +android_renameat_noreplace (int srcfd, const char *src, + int dstfd, const char *dst) +{ + struct android_vnode *vp, *vdst; + int rc; + + if (srcfd != AT_FDCWD || dstfd != AT_FDCWD) + { + errno = ENOSYS; + return -1; + } + + /* Find vnodes for both src and dst. */ + + vp = android_name_file (src); + if (!vp) + goto error; + + vdst = android_name_file (dst); + if (!vdst) + goto error1; + + /* Now try to rename vp to vdst. */ + rc = (*vp->ops->rename) (vp, vdst, true); + (*vp->ops->close) (vp); + (*vp->ops->close) (vdst); + return rc; + + error1: + (*vp->ops->close) (vp); + error: + return -1; +} + +/* Like `android_renameat_noreplace', but don't check for DST's + existence and don't accept placeholder SRCFD and DSTFD + arguments. */ + +int +android_rename (const char *src, const char *dst) +{ + struct android_vnode *vp, *vdst; + int rc; + + /* Find vnodes for both src and dst. */ + + vp = android_name_file (src); + if (!vp) + goto error; + + vdst = android_name_file (dst); + if (!vdst) + goto error1; + + /* Now try to rename vp to vdst. */ + rc = (*vp->ops->rename) (vp, vdst, false); + (*vp->ops->close) (vp); + (*vp->ops->close) (vdst); + return rc; + + error1: + (*vp->ops->close) (vp); + error: + return -1; +} + + + +/* fstat, fstatat, faccessat, close/fclose etc. These functions are + somewhat tricky to wrap: they (at least partially) operate on file + descriptors, which sometimes provide a base directory for the + filesystem operations they perform. VFS nodes aren't mapped to + file descriptors opened through them, which makes this troublesome. + + openat is not wrapped at all; uses are defined out when Emacs is + being built for Android. The other functions fall back to directly + making Unix system calls when their base directory arguments are + not AT_FDCWD and no directory stream returned from + `android_opendir' ever returned that file descriptor, which is + enough to satisfy Emacs's current requirements for those functions + when a directory file descriptor is supplied. + + fclose and close are finally wrapped because they need to erase + information used to link file descriptors with file statistics from + their origins; fstat is also wrapped to take this information into + account, so that it can return correct file statistics for asset + directory files. */ + +/* Like fstat. However, look up the asset corresponding to the file + descriptor. If it exists, return the right information. */ + +int +android_fstat (int fd, struct stat *statb) +{ + struct android_afs_open_fd *tem; + struct android_parcel_fd *parcel_fd; + int rc; + + for (tem = afs_file_descriptors; tem; tem = tem->next) + { + if (tem->fd == fd) + { + memcpy (statb, &tem->statb, sizeof *statb); + return 0; + } + } + + rc = fstat (fd, statb); + + /* Now look for a matching parcel file descriptor and use its + mtime if available. */ + + parcel_fd = open_parcel_fds; + for (; parcel_fd; parcel_fd = parcel_fd->next) + { + if (parcel_fd->fd == fd + && timespec_valid_p (parcel_fd->mtime)) + { + statb->st_mtim = parcel_fd->mtime; + break; + } + } + + return rc; +} + +/* If DIRFD is a file descriptor returned by `android_readdir' for a + non-Unix file stream, return FILENAME relative to the file name of + the directory represented by that stream within BUFFER, a buffer + SIZE bytes long. + + Value is 0 if a file name is returned, 1 otherwise. */ + +static int +android_fstatat_1 (int dirfd, const char *filename, + char *restrict buffer, size_t size) +{ + char *dir_name; + struct android_saf_root_vdir *vdir; + struct android_saf_tree_vdir *vdir1; + + /* Now establish whether DIRFD is a file descriptor corresponding to + an open asset directory stream. */ + + dir_name = android_afs_get_directory_name (dirfd); + + if (dir_name) + { + /* Look for PATHNAME relative to this directory within an asset + vnode. */ + snprintf (buffer, size, "/assets%s%s", dir_name, + filename); + return 0; + } + + /* Do the same, but for /content directories instead. */ + + dir_name = android_content_get_directory_name (dirfd); + + if (dir_name) + { + /* Look for PATHNAME relative to this directory within an asset + vnode. */ + snprintf (buffer, size, "%s/%s", dir_name, + filename); + return 0; + } + + /* And for /content/storage. */ + + vdir = android_saf_root_get_directory (dirfd); + + if (vdir) + { + if (vdir->authority) + snprintf (buffer, size, "/content/storage/%s/%s", + vdir->authority, filename); + else + snprintf (buffer, size, "/content/storage/%s", + filename); + + return 0; + } + + /* /content/storage/foo/... */ + + vdir1 = android_saf_tree_get_directory (dirfd); + + if (vdir1) + { + snprintf (buffer, size, "%s%s", vdir1->name, filename); + return 0; + } + + return 1; +} + +/* If DIRFD is AT_FDCWD or a file descriptor returned by + `android_dirfd', or PATHNAME is an absolute file name, return the + file status of the VFS node designated by PATHNAME relative to the + VFS node corresponding to DIRFD, or relative to the current working + directory if DIRFD is AT_FDCWD. + + Otherwise, call `fstatat' with DIRFD, PATHNAME, STATBUF and + FLAGS. */ + +int +android_fstatat (int dirfd, const char *restrict pathname, + struct stat *restrict statbuf, int flags) +{ + char buffer[PATH_MAX + 1]; + struct android_vnode *vp; + int rc; + + /* Emacs uses AT_SYMLINK_NOFOLLOW, but fortunately (?) DIRFD is + never known to Emacs or AT_FDCWD when it originates from a VFS + node representing a filesystem that supports symlinks. */ + + if (dirfd == AT_FDCWD || pathname[0] == '/') + goto vfs; + + /* Now establish whether DIRFD is a file descriptor corresponding to + an open VFS directory stream. */ + + if (!android_fstatat_1 (dirfd, pathname, buffer, PATH_MAX + 1)) + { + pathname = buffer; + goto vfs; + } + + /* Fall back to fstatat. */ + return fstatat (dirfd, pathname, statbuf, flags); + + vfs: + vp = android_name_file (pathname); + if (!vp) + return -1; + + rc = (*vp->ops->stat) (vp, statbuf); + (*vp->ops->close) (vp); + return rc; +} + +/* Like `android_fstatat', but check file accessibility instead of + status. */ + +int +android_faccessat (int dirfd, const char *restrict pathname, + int mode, int flags) +{ + char buffer[PATH_MAX + 1]; + struct android_vnode *vp; + int rc; + + /* Emacs uses AT_SYMLINK_NOFOLLOW, but fortunately (?) DIRFD is + never known to Emacs or AT_FDCWD when it originates from a VFS + node representing a filesystem that supports symlinks. */ + + if (dirfd == AT_FDCWD || pathname[0] == '/') + goto vfs; + + /* Now establish whether DIRFD is a file descriptor corresponding to + an open VFS directory stream. */ + + if (!android_fstatat_1 (dirfd, pathname, buffer, PATH_MAX + 1)) + { + pathname = buffer; + goto vfs; + } + + /* Fall back to faccessat. */ + return faccessat (dirfd, pathname, mode, flags); + + vfs: + vp = android_name_file (pathname); + if (!vp) + return -1; + + rc = (*vp->ops->access) (vp, mode); + (*vp->ops->close) (vp); + return rc; +} + +/* Like `fdopen', but if FD is a parcel file descriptor, ``detach'' it + from the original. + + This is necessary because ownership over parcel file descriptors is + retained by the ParcelFileDescriptor objects that return them, + while file streams also require ownership over file descriptors + they are created on behalf of. + + Detaching the parcel file descriptor linked to FD consequentially + prevents the owner from being notified when it is eventually + closed, but for now that hasn't been demonstrated to be problematic + yet, as Emacs doesn't write to file streams. */ + +FILE * +android_fdopen (int fd, const char *mode) +{ + struct android_parcel_fd *tem, **next, *temp; + int new_fd; + + for (next = &open_parcel_fds; (tem = *next);) + { + if (tem->fd == fd) + { + new_fd + = (*android_java_env)->CallIntMethod (android_java_env, + tem->descriptor, + fd_class.detach_fd); + temp = tem->next; + xfree (tem); + *next = temp; + android_exception_check (); + + /* Assert that FD (returned from `getFd') is identical to + the file descriptor returned by `detachFd'. */ + + if (fd != new_fd) + emacs_abort (); + + goto open_file; + } + } + + open_file: + return fdopen (fd, mode); +} + +/* Like close. However, remove the file descriptor from the asset + table as well. */ + +int +android_close (int fd) +{ + struct android_afs_open_fd *tem, **next, *temp; + + if (android_close_parcel_fd (fd)) + return 0; + + for (next = &afs_file_descriptors; (tem = *next);) + { + if (tem->fd == fd) + { + temp = tem->next; + xfree (tem); + *next = temp; + + break; + } + else + next = &(*next)->next; + } + + return close (fd); +} + +/* Like fclose. However, remove any information associated with + FILE's file descriptor from the asset table as well. */ + +int +android_fclose (FILE *stream) +{ + int fd; + struct android_afs_open_fd *tem, **next, *temp; + + fd = fileno (stream); + + if (fd == -1) + goto skip; + + for (next = &afs_file_descriptors; (tem = *next);) + { + if (tem->fd == fd) + { + temp = tem->next; + xfree (*next); + *next = temp; + + break; + } + else + next = &(*next)->next; + } + + skip: + return fclose (stream); +} + + + +/* External asset management interface. By using functions here + to read and write from files, Emacs can avoid opening a + shared memory file descriptor for each ``asset'' file. */ + +/* Like android_open. However, return a structure that can + either directly hold an AAsset or a file descriptor. + + Value is the structure upon success. Upon failure, value + consists of an uninitialized file descriptor, but its asset + field is set to -1, and errno is set accordingly. */ + +struct android_fd_or_asset +android_open_asset (const char *filename, int oflag, mode_t mode) +{ + struct android_fd_or_asset fd; + AAsset *asset; + int rc; + struct android_vnode *vp; + + /* Now name this file. */ + vp = android_name_file (filename); + if (!vp) + goto failure; + + rc = (*vp->ops->open) (vp, oflag, mode, true, &fd.fd, + &asset); + (*vp->ops->close) (vp); + + /* Upon failure, return fd with its asset field set to (void *) + -1. */ + + if (rc < 0) + { + failure: + fd.asset = (void *) -1; + fd.fd = -1; + return fd; + } + + if (rc == 1) + { + /* An asset file was returned. Return the structure containing + an asset. */ + fd.asset = asset; + fd.fd = -1; + return fd; + } + + /* Otherwise, a file descriptor has been returned. Set fd.asset to + NULL, signifying that it is a file descriptor. */ + fd.asset = NULL; + return fd; +} + +/* Like android_close. However, it takes a ``file descriptor'' + opened using android_open_asset. */ + +int +android_close_asset (struct android_fd_or_asset asset) +{ + if (!asset.asset) + return android_close (asset.fd); + + AAsset_close (asset.asset); + return 0; +} + +/* Like `emacs_read_quit'. However, it handles file descriptors + opened using `android_open_asset' as well. */ + +ssize_t +android_asset_read_quit (struct android_fd_or_asset asset, + void *buffer, size_t size) +{ + if (!asset.asset) + return emacs_read_quit (asset.fd, buffer, size); + + /* It doesn't seem possible to quit from inside AAsset_read, + sadly. */ + return AAsset_read (asset.asset, buffer, size); +} + +/* Like `read'. However, it handles file descriptors opened + using `android_open_asset' as well. */ + +ssize_t +android_asset_read (struct android_fd_or_asset asset, + void *buffer, size_t size) +{ + if (!asset.asset) + return read (asset.fd, buffer, size); + + /* It doesn't seem possible to quit from inside AAsset_read, + sadly. */ + return AAsset_read (asset.asset, buffer, size); +} + +/* Like `lseek', but it handles ``file descriptors'' opened with + android_open_asset. */ + +off_t +android_asset_lseek (struct android_fd_or_asset asset, off_t off, + int whence) +{ + if (!asset.asset) + return lseek (asset.fd, off, whence); + + return AAsset_seek (asset.asset, off, whence); +} + +/* Like `fstat'. */ + +int +android_asset_fstat (struct android_fd_or_asset asset, + struct stat *statb) +{ + if (!asset.asset) + return android_fstat (asset.fd, statb); + + /* Clear statb. */ + memset (statb, 0, sizeof *statb); + + /* Set the mode. */ + statb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + + /* Concoct a nonexistent device and an inode number. */ + statb->st_dev = -1; + statb->st_ino = 0; + + /* Owned by root. */ + statb->st_uid = 0; + statb->st_gid = 0; + + /* Size of the file. */ + statb->st_size = AAsset_getLength (asset.asset); + return 0; +} + + + +/* Directory listing emulation. */ + +/* Open a directory stream from the VFS node designated by NAME. + Value is NULL upon failure with errno set accordingly. + + The directory stream returned holds local references to JNI objects + and shouldn't be used after the current local reference frame is + popped. */ + +struct android_vdir * +android_opendir (const char *name) +{ + struct android_vnode *vp; + struct android_vdir *dir; + + vp = android_name_file (name); + if (!vp) + return NULL; + + dir = (*vp->ops->opendir) (vp); + (*vp->ops->close) (vp); + return dir; +} + +/* Like dirfd. However, value is not a real directory file descriptor + if DIR is an asset directory. */ + +int +android_dirfd (struct android_vdir *dirp) +{ + return (*dirp->dirfd) (dirp); +} + +/* Like readdir, but for VFS directory streams instead. */ + +struct dirent * +android_readdir (struct android_vdir *dirp) +{ + return (*dirp->readdir) (dirp); +} + +/* Like closedir, but for VFS directory streams instead. */ + +void +android_closedir (struct android_vdir *dirp) +{ + return (*dirp->closedir) (dirp); +} diff --git a/src/callproc.c b/src/callproc.c index ee5195385de..0645c2c3e18 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -214,7 +214,7 @@ record_kill_process (struct Lisp_Process *p, Lisp_Object tempfile) static void delete_temp_file (Lisp_Object name) { - unlink (SSDATA (name)); + emacs_unlink (SSDATA (name)); } static void diff --git a/src/charset.c b/src/charset.c index c532f79d282..d5e42d038df 100644 --- a/src/charset.c +++ b/src/charset.c @@ -488,7 +488,7 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile, specbind (Qfile_name_handler_alist, Qnil); fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false, NULL); - fp = fd < 0 ? 0 : fdopen (fd, "r"); + fp = fd < 0 ? 0 : emacs_fdopen (fd, "r"); if (!fp) { int open_errno = errno; diff --git a/src/dired.c b/src/dired.c index 93487d552e2..f2a123dc168 100644 --- a/src/dired.c +++ b/src/dired.c @@ -54,7 +54,7 @@ #define emacs_closedir closedir /* The Android emulation of dirent stuff is required to be able to list the /assets special directory. */ -typedef struct android_dir emacs_dir; +typedef struct android_vdir emacs_dir; #define emacs_readdir android_readdir #define emacs_closedir android_closedir #endif diff --git a/src/emacs.c b/src/emacs.c index d75a83ab9d8..72f75d7890d 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -3012,7 +3012,7 @@ DEFUN ("kill-emacs", Fkill_emacs, Skill_emacs, 0, 2, "P", { Lisp_Object listfile; listfile = Fexpand_file_name (Vauto_save_list_file_name, Qnil); - unlink (SSDATA (listfile)); + emacs_unlink (SSDATA (listfile)); } #ifdef HAVE_NATIVE_COMP diff --git a/src/fileio.c b/src/fileio.c index d24f25c2e06..5ce933ec45b 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -182,15 +182,12 @@ #define emacs_fd_to_int(fds) ((fds).asset ? -1 : (fds).fd) -/* Check that ENCODED does not lie on any special directory whose - contents are read only. Signal a `file-error' if it does. - - If WRITE, then don't check that the file lies on `/content' on - Android. This special exception allows writing to content - provider-supplied files. */ +/* Establish that ENCODED is not contained within a special directory + whose contents are not eligible for Unix VFS operations. Signal a + `file-error' with REASON if it does. */ static void -check_mutable_filename (Lisp_Object encoded, bool write) +check_vfs_filename (Lisp_Object encoded, const char *reason) { #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY const char *name; @@ -198,17 +195,10 @@ check_mutable_filename (Lisp_Object encoded, bool write) name = SSDATA (encoded); if (android_is_special_directory (name, "/assets")) - xsignal2 (Qfile_error, - build_string ("File lies on read-only directory"), - encoded); - - if (write) - return; + xsignal2 (Qfile_error, build_string (reason), encoded); if (android_is_special_directory (name, "/content")) - xsignal2 (Qfile_error, - build_string ("File lies on read-only directory"), - encoded); + xsignal2 (Qfile_error, build_string (reason), encoded); #endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ } @@ -2287,7 +2277,6 @@ DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, encoded_file = ENCODE_FILE (file); encoded_newname = ENCODE_FILE (newname); - check_mutable_filename (encoded_newname, true); #ifdef WINDOWSNT if (NILP (ok_if_already_exists) @@ -2553,7 +2542,7 @@ DEFUN ("make-directory-internal", Fmake_directory_internal, dir = SSDATA (encoded_dir); - if (mkdir (dir, 0777 & ~auto_saving_dir_umask) != 0) + if (emacs_mkdir (dir, 0777 & ~auto_saving_dir_umask) != 0) report_file_error ("Creating directory", directory); return Qnil; @@ -2572,9 +2561,7 @@ DEFUN ("delete-directory-internal", Fdelete_directory_internal, encoded_dir = ENCODE_FILE (directory); dir = SSDATA (encoded_dir); - check_mutable_filename (encoded_dir, false); - - if (rmdir (dir) != 0) + if (emacs_rmdir (dir) != 0) report_file_error ("Removing directory", directory); return Qnil; @@ -2613,9 +2600,9 @@ DEFUN ("delete-file", Fdelete_file, Sdelete_file, 1, 2, return call1 (Qmove_file_to_trash, filename); encoded_file = ENCODE_FILE (filename); - check_mutable_filename (encoded_file, false); - if (unlink (SSDATA (encoded_file)) != 0 && errno != ENOENT) + if (emacs_unlink (SSDATA (encoded_file)) != 0 + && errno != ENOENT) report_file_error ("Removing old name", filename); return Qnil; } @@ -2771,8 +2758,6 @@ DEFUN ("rename-file", Frename_file, Srename_file, 2, 3, encoded_file = ENCODE_FILE (file); encoded_newname = ENCODE_FILE (newname); - check_mutable_filename (encoded_file, false); - check_mutable_filename (encoded_newname, false); bool plain_rename = (case_only_rename || (!NILP (ok_if_already_exists) @@ -2780,8 +2765,10 @@ DEFUN ("rename-file", Frename_file, Srename_file, 2, 3, int rename_errno UNINIT; if (!plain_rename) { - if (renameat_noreplace (AT_FDCWD, SSDATA (encoded_file), - AT_FDCWD, SSDATA (encoded_newname)) + if (emacs_renameat_noreplace (AT_FDCWD, + SSDATA (encoded_file), + AT_FDCWD, + SSDATA (encoded_newname)) == 0) return Qnil; @@ -2803,7 +2790,8 @@ DEFUN ("rename-file", Frename_file, Srename_file, 2, 3, if (plain_rename) { - if (rename (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0) + if (emacs_rename (SSDATA (encoded_file), + SSDATA (encoded_newname)) == 0) return Qnil; rename_errno = errno; /* Don't prompt again. */ @@ -2884,8 +2872,10 @@ DEFUN ("add-name-to-file", Fadd_name_to_file, Sadd_name_to_file, 2, 3, encoded_file = ENCODE_FILE (file); encoded_newname = ENCODE_FILE (newname); - check_mutable_filename (encoded_file, false); - check_mutable_filename (encoded_newname, false); + check_vfs_filename (encoded_file, "Trying to create hard link to " + "file within special directory"); + check_vfs_filename (encoded_newname, "Trying to create hard link" + " within special directory"); if (link (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0) return Qnil; @@ -2896,7 +2886,7 @@ DEFUN ("add-name-to-file", Fadd_name_to_file, Sadd_name_to_file, 2, 3, || FIXNUMP (ok_if_already_exists)) barf_or_query_if_file_exists (newname, true, "make it a new name", FIXNUMP (ok_if_already_exists), false); - unlink (SSDATA (newname)); + emacs_unlink (SSDATA (newname)); if (link (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0) return Qnil; } @@ -2939,10 +2929,9 @@ DEFUN ("make-symbolic-link", Fmake_symbolic_link, Smake_symbolic_link, 2, 3, encoded_target = ENCODE_FILE (target); encoded_linkname = ENCODE_FILE (linkname); - check_mutable_filename (encoded_target, false); - check_mutable_filename (encoded_linkname, false); - if (symlink (SSDATA (encoded_target), SSDATA (encoded_linkname)) == 0) + if (emacs_symlink (SSDATA (encoded_target), + SSDATA (encoded_linkname)) == 0) return Qnil; if (errno == ENOSYS) @@ -2955,8 +2944,9 @@ DEFUN ("make-symbolic-link", Fmake_symbolic_link, Smake_symbolic_link, 2, 3, || FIXNUMP (ok_if_already_exists)) barf_or_query_if_file_exists (linkname, true, "make it a link", FIXNUMP (ok_if_already_exists), false); - unlink (SSDATA (encoded_linkname)); - if (symlink (SSDATA (encoded_target), SSDATA (encoded_linkname)) == 0) + emacs_unlink (SSDATA (encoded_linkname)); + if (emacs_symlink (SSDATA (encoded_target), + SSDATA (encoded_linkname)) == 0) return Qnil; } @@ -3328,27 +3318,12 @@ file_accessible_directory_p (Lisp_Object file) special cases "/" and "//", and it's a safe optimization here. After appending '.', append another '/' to work around a macOS bug (Bug#30350). */ -#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY - if (!strncmp ("/assets/", data, - sizeof "/assets" - 1)) - { - static char const appended[] = "/"; - char *buf = SAFE_ALLOCA (len + sizeof appended); - memcpy (buf, data, len); - strcpy (buf + len, &appended[data[len - 1] == '/']); - dir = buf; - } - else - { -#endif - static char const appended[] = "/./"; - char *buf = SAFE_ALLOCA (len + sizeof appended); - memcpy (buf, data, len); - strcpy (buf + len, &appended[data[len - 1] == '/']); - dir = buf; -#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY - } -#endif + + static char const appended[] = "/./"; + char *buf = SAFE_ALLOCA (len + sizeof appended); + memcpy (buf, data, len); + strcpy (buf + len, &appended[data[len - 1] == '/']); + dir = buf; } ok = file_access_p (dir, F_OK); @@ -3682,7 +3657,8 @@ DEFUN ("set-file-modes", Fset_file_modes, Sset_file_modes, 2, 3, return call4 (handler, Qset_file_modes, absname, mode, flag); encoded = ENCODE_FILE (absname); - check_mutable_filename (encoded, false); + check_vfs_filename (encoded, "Trying to change access modes of file" + " within special directory"); char *fname = SSDATA (encoded); mode_t imode = XFIXNUM (mode) & 07777; if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0) @@ -3755,7 +3731,8 @@ DEFUN ("set-file-times", Fset_file_times, Sset_file_times, 1, 3, 0, return call4 (handler, Qset_file_times, absname, timestamp, flag); Lisp_Object encoded_absname = ENCODE_FILE (absname); - check_mutable_filename (encoded_absname, false); + check_vfs_filename (encoded_absname, "Trying to set access times of" + " file within special directory"); if (utimensat (AT_FDCWD, SSDATA (encoded_absname), ts, nofollow) != 0) { @@ -5456,7 +5433,6 @@ write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename, } encoded_filename = ENCODE_FILE (filename); - check_mutable_filename (encoded_filename, false); fn = SSDATA (encoded_filename); open_flags = O_WRONLY | O_CREAT; diff --git a/src/filelock.c b/src/filelock.c index cbbcc016b27..fea0044e219 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -228,7 +228,7 @@ get_boot_time (void) { get_boot_time_1 (SSDATA (filename), 1); if (delete_flag) - unlink (SSDATA (filename)); + emacs_unlink (SSDATA (filename)); } } @@ -323,11 +323,12 @@ rename_lock_file (char const *old, char const *new, bool force) { struct stat st; - int r = renameat_noreplace (AT_FDCWD, old, AT_FDCWD, new); + int r = emacs_renameat_noreplace (AT_FDCWD, old, + AT_FDCWD, new); if (! (r < 0 && errno == ENOSYS)) return r; if (link (old, new) == 0) - return unlink (old) == 0 || errno == ENOENT ? 0 : -1; + return emacs_unlink (old) == 0 || errno == ENOENT ? 0 : -1; if (errno != ENOSYS && errno != LINKS_MIGHT_NOT_WORK) return -1; @@ -347,7 +348,7 @@ rename_lock_file (char const *old, char const *new, bool force) return -1; } - return rename (old, new); + return emacs_rename (old, new); #endif } @@ -365,13 +366,13 @@ create_lock_file (char *lfname, char *lock_info_str, bool force) pretending that 'symlink' does not work. */ int err = ENOSYS; #else - int err = symlink (lock_info_str, lfname) == 0 ? 0 : errno; + int err = emacs_symlink (lock_info_str, lfname) == 0 ? 0 : errno; #endif if (err == EEXIST && force) { - unlink (lfname); - err = symlink (lock_info_str, lfname) == 0 ? 0 : errno; + emacs_unlink (lfname); + err = emacs_symlink (lock_info_str, lfname) == 0 ? 0 : errno; } if (err == ENOSYS || err == LINKS_MIGHT_NOT_WORK || err == ENAMETOOLONG) @@ -409,7 +410,7 @@ create_lock_file (char *lfname, char *lock_info_str, bool force) if (!err && rename_lock_file (nonce, lfname, force) != 0) err = errno; if (err) - unlink (nonce); + emacs_unlink (nonce); } SAFE_FREE (); @@ -622,7 +623,7 @@ current_lock_owner (lock_info_type *owner, Lisp_Object lfname) /* The owner process is dead or has a strange pid, so try to zap the lockfile. */ else - return unlink (SSDATA (lfname)) < 0 ? errno : 0; + return emacs_unlink (SSDATA (lfname)) < 0 ? errno : 0; } else { /* If we wanted to support the check for stale locks on remote machines, @@ -770,7 +771,8 @@ unlock_file (Lisp_Object fn) int err = current_lock_owner (0, lfname); if (! (err == 0 || err == ANOTHER_OWNS_IT || (err == I_OWN_IT - && (unlink (SSDATA (lfname)) == 0 || (err = errno) == ENOENT)))) + && (emacs_unlink (SSDATA (lfname)) == 0 + || (err = errno) == ENOENT)))) report_file_errno ("Unlocking file", fn, err); return Qnil; diff --git a/src/image.c b/src/image.c index 7501838d8c4..8a8f18754ac 100644 --- a/src/image.c +++ b/src/image.c @@ -4234,7 +4234,7 @@ close_android_fd (void *ptr) slurp_file (image_fd fd, ptrdiff_t *size) { #if !defined HAVE_ANDROID || defined ANDROID_STUBIFY - FILE *fp = fdopen (fd, "rb"); + FILE *fp = emacs_fdopen (fd, "rb"); char *buf = NULL; struct stat st; @@ -8021,7 +8021,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) } /* Open the image file. */ - fp = fdopen (fd, "rb"); + fp = emacs_fdopen (fd, "rb"); if (!fp) { image_error ("Cannot open image file `%s'", file); @@ -8750,7 +8750,7 @@ jpeg_load_body (struct frame *f, struct image *img, return 0; } - fp = fdopen (fd, "rb"); + fp = emacs_fdopen (fd, "rb"); if (fp == NULL) { image_error ("Cannot open `%s'", file); diff --git a/src/keyboard.c b/src/keyboard.c index ee42b821dfa..445f39ea140 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -11740,9 +11740,9 @@ DEFUN ("open-dribble-file", Fopen_dribble_file, Sopen_dribble_file, 1, 1, encfile = ENCODE_FILE (file); fd = emacs_open (SSDATA (encfile), O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd < 0 && errno == EEXIST - && (unlink (SSDATA (encfile)) == 0 || errno == ENOENT)) + && (emacs_unlink (SSDATA (encfile)) == 0 || errno == ENOENT)) fd = emacs_open (SSDATA (encfile), O_WRONLY | O_CREAT | O_EXCL, 0600); - dribble = fd < 0 ? 0 : fdopen (fd, "w"); + dribble = fd < 0 ? 0 : emacs_fdopen (fd, "w"); if (dribble == 0) report_file_error ("Opening dribble", file); } diff --git a/src/lisp.h b/src/lisp.h index 4be0002e9fc..5802984a5e7 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -5086,7 +5086,15 @@ maybe_disable_address_randomization (int argc, char **argv) extern int emacs_open_noquit (const char *, int, int); extern int emacs_pipe (int[2]); extern int emacs_close (int); +extern FILE *emacs_fdopen (int, const char *); extern int emacs_fclose (FILE *); +extern int emacs_unlink (const char *); +extern int emacs_symlink (const char *, const char *); +extern int emacs_rmdir (const char *); +extern int emacs_mkdir (const char *, mode_t); +extern int emacs_renameat_noreplace (int, const char *, int, + const char *); +extern int emacs_rename (const char *, const char *); extern ptrdiff_t emacs_read (int, void *, ptrdiff_t); extern ptrdiff_t emacs_read_quit (int, void *, ptrdiff_t); extern ptrdiff_t emacs_write (int, void const *, ptrdiff_t); diff --git a/src/lread.c b/src/lread.c index 11bfc52a83f..251da5670d0 100644 --- a/src/lread.c +++ b/src/lread.c @@ -1702,7 +1702,7 @@ DEFUN ("load", Fload, Sload, 1, 5, 0, stream = emacs_fopen (SSDATA (efound), fmode); #else #if !defined USE_ANDROID_ASSETS - stream = fdopen (fd, fmode); + stream = emacs_fdopen (fd, fmode); #else /* Android systems use special file descriptors which can point into compressed data and double as file streams. FMODE is diff --git a/src/process.c b/src/process.c index 8b7b8d8566e..90885ad318a 100644 --- a/src/process.c +++ b/src/process.c @@ -7472,6 +7472,16 @@ handle_child_signal (int sig) { changed = true; if (STRINGP (XCDR (head))) + /* handle_child_signal is called in an async signal + handler but needs to unlink temporary files which + might've been created in an Android content + provider. + + emacs_unlink is not async signal safe because + deleting files from content providers must proceed + through Java code. Consequentially, if XCDR (head) + lies on a content provider it will not be removed, + which is a bug. */ unlink (SSDATA (XCDR (head))); XSETCAR (tail, Qnil); } diff --git a/src/sysdep.c b/src/sysdep.c index bec2c00d3e5..88938d15b91 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -260,7 +260,7 @@ init_standard_fds (void) /* Set buferr if possible on platforms defining _PC_PIPE_BUF, as they support the notion of atomic writes to pipes. */ #ifdef _PC_PIPE_BUF - buferr = fdopen (STDERR_FILENO, "w"); + buferr = emacs_fdopen (STDERR_FILENO, "w"); if (buferr) setvbuf (buferr, NULL, _IOLBF, 0); #endif @@ -2545,7 +2545,7 @@ emacs_fopen (char const *file, char const *mode) } fd = emacs_open (file, omode | oflags | bflag, 0666); - return fd < 0 ? 0 : fdopen (fd, mode); + return fd < 0 ? 0 : emacs_fdopen (fd, mode); } /* Create a pipe for Emacs use. */ @@ -2627,6 +2627,19 @@ #define POSIX_CLOSE_RESTART 1 } } +/* Wrapper around fdopen. On Android, this calls `android_fclose' to + clear information associated with FD if necessary. */ + +FILE * +emacs_fdopen (int fd, const char *mode) +{ +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) + return fdopen (fd, mode); +#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ + return android_fdopen (fd, mode); +#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */ +} + /* Wrapper around fclose. On Android, this calls `android_fclose' to clear information associated with the FILE's file descriptor if necessary. */ @@ -2641,6 +2654,71 @@ emacs_fclose (FILE *stream) #endif } +/* Wrappers around unlink, symlink, rename, renameat_noreplace, and + rmdir. These operations handle asset and content directories on + Android. */ + +int +emacs_unlink (const char *name) +{ +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) + return unlink (name); +#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ + return android_unlink (name); +#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */ +} + +int +emacs_symlink (const char *target, const char *linkname) +{ +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) + return symlink (target, linkname); +#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ + return android_symlink (target, linkname); +#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */ +} + +int +emacs_rmdir (const char *dirname) +{ +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) + return rmdir (dirname); +#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ + return android_rmdir (dirname); +#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */ +} + +int +emacs_mkdir (const char *dirname, mode_t mode) +{ +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) + return mkdir (dirname, mode); +#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ + return android_mkdir (dirname, mode); +#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */ +} + +int +emacs_renameat_noreplace (int srcfd, const char *src, + int dstfd, const char *dst) +{ +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) + return renameat_noreplace (srcfd, src, dstfd, dst); +#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ + return android_renameat_noreplace (srcfd, src, dstfd, dst); +#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */ +} + +int +emacs_rename (const char *src, const char *dst) +{ +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) + return rename (src, dst); +#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ + return android_rename (src, dst); +#endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */ +} + /* Maximum number of bytes to read or write in a single system call. This works around a serious bug in Linux kernels before 2.6.16; see . diff --git a/src/term.c b/src/term.c index 4de57ca1afe..9bcb2cb1386 100644 --- a/src/term.c +++ b/src/term.c @@ -2415,7 +2415,7 @@ DEFUN ("resume-tty", Fresume_tty, Sresume_tty, 0, 1, 0, #else /* !MSDOS */ fd = emacs_open (t->display_info.tty->name, O_RDWR | O_NOCTTY, 0); t->display_info.tty->input = t->display_info.tty->output - = fd < 0 ? 0 : fdopen (fd, "w+"); + = fd < 0 ? 0 : emacs_fdopen (fd, "w+"); if (! t->display_info.tty->input) { @@ -4128,7 +4128,7 @@ init_tty (const char *name, const char *terminal_type, bool must_succeed) tty->input = tty->output = ((fd < 0 || ! isatty (fd)) ? NULL - : fdopen (fd, "w+")); + : emacs_fdopen (fd, "w+")); if (! tty->input) { commit ce63f592f579e8963a3e7f44b31c7df50fb0cdba Merge: b7de14b56fa 65834b8f8d5 Author: Po Lu Date: Wed Jul 26 08:08:11 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit b7de14b56fac658411baeaede56416ad322fecfd Merge: 44910e26f32 acebaa793f1 Author: Po Lu Date: Mon Jul 24 08:09:36 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 44910e26f32edbad9a5c063ecdfbb6a32a759752 Author: Po Lu Date: Sun Jul 23 09:52:19 2023 +0800 Facilitate locating the app library directory * doc/emacs/android.texi (Android File System): Document where the app library directory can probably be found. * src/android.c (android_create_lib_link): New function. Try to symlink `lib' in the directory holding the files directory to the app library directory. (setEmacsParams): Call that function if Emacs is being initialized from an application context. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index df73ee60c5d..86af851efd2 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -212,9 +212,27 @@ Android File System @end itemize The external storage directory is found at @file{/sdcard}. The -other directories are not found at any fixed location, although the -app data directory is typically symlinked to -@file{/data/data/org.gnu.emacs}. +other directories are not found at any fixed location (but see below), +although the app data directory is typically symlinked to +@file{/data/data/org.gnu.emacs/files}. + +@cindex app library directory, android +@cindex where is emacsclient under android + Older versions of Android used to place the app library directory +under the name @file{lib} in the parent of the app data directory. +Today, this directory is often placed in a directory with a randomly +generated name under @file{/data/app}. + + For the convenience of scripts running within applications sharing +the same user ID as Emacs (which have no access to the +@code{exec-directory} variable), a fairly considerable effort is made +at startup to symlink the application library directory to its +traditional location within the parent of the app data directory. + + If Emacs is reinstalled and the location of the app library +directory consequentially changes, that symlink will also be updated +to point to its new location the next time Emacs is started by the +system. @cindex temp~unlinked.NNNN files, Android On Android devices running very old (2.6.29) versions of the Linux diff --git a/src/android.c b/src/android.c index 048773a511d..6fcaa40b2a9 100644 --- a/src/android.c +++ b/src/android.c @@ -30,6 +30,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include #include +#include #include #include @@ -1995,6 +1996,65 @@ android_proc_name (int fd, char *buffer, size_t size) return 0; } +/* Try to guarantee the existence of the `lib' directory within the + parent directory of the application files directory. + + If `/data/data/org.gnu.emacs/lib' (or + `/data/user/N/org.gnu.emacs/lib') does not exist or is a dangling + symbolic link, create a symlink from it to the library + directory. + + Newer versions of Android don't create this link by default, making + it difficult to locate the directory containing Emacs library + files, particularly from scripts in other programs sharing the same + user ID as Emacs that don't have access to `exec-path'. */ + +static void +android_create_lib_link (void) +{ + char *filename; + char lib_directory[PATH_MAX]; + int fd; + struct stat statb; + + /* Find the directory containing the files directory. */ + filename = dirname (android_files_dir); + if (!filename) + goto failure; + + /* Now make `lib_directory' the name of the library directory + within. */ + snprintf (lib_directory, PATH_MAX, "%s/lib", filename); + + /* Try to open this directory. */ + fd = open (lib_directory, O_DIRECTORY); + + /* If the directory can be opened normally, close it and return + now. */ + if (fd >= 0) + goto success; + + /* Try to unlink the directory in case it's a dangling symbolic + link. */ + unlink (lib_directory); + + /* Otherwise, try to symlink lib_directory to the actual library + directory. */ + + if (symlink (android_lib_dir, lib_directory)) + /* Print a warning message if creating the link fails. */ + __android_log_print (ANDROID_LOG_WARN, __func__, + "Failed to create symbolic link from" + " application library directory `%s'" + " to its actual location at `%s'", + lib_directory, android_files_dir); + + success: + close (fd); + failure: + return; +} + /* JNI functions called by Java. */ @@ -2229,6 +2289,16 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, if (!emacs_service) emacs_abort (); + + /* If the service is set this Emacs is being initialized as part + of the Emacs application itself. + + Try to create a symlink from where scripts expect Emacs to + place its library files to the directory that actually holds + them; earlier versions of Android used to do this + automatically, but that feature has been removed. */ + + android_create_lib_link (); } /* Set up events. */ commit be70caa68f448438cd8170534220ca284d6a6930 Merge: 3ab81aa5fb1 f37c65b402f Author: Po Lu Date: Sun Jul 23 08:53:22 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 3ab81aa5fb18b2532bd5f7b5e2220a46caa65b55 Author: Po Lu Date: Sat Jul 22 20:09:54 2023 +0800 Try harder to keep the initial word selected * lisp/touch-screen.el (touch-screen-drag): If touch-screen-word-select, also keep the initial word within the region while scrolling. diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index a4337d2f009..0914071f2a0 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -524,6 +524,11 @@ touch-screen-drag ;; The currently selected window. Used to redisplay within ;; the correct window while scrolling. (old-window (selected-window)) + ;; Whether or not text should be selected word-by-word. + (word-select touch-screen-word-select) + ;; Cons containing the confines of the word initially + ;; selected when the touchpoint was first held down. + (initial touch-screen-word-select-initial-word) initial-point) ;; Keep dragging. (with-selected-window window @@ -539,12 +544,11 @@ touch-screen-drag ;; the window must be scrolled. (pos-visible-in-window-p point)) (let* ((bounds touch-screen-word-select-bounds) - (initial touch-screen-word-select-initial-word) (maybe-select-word (or (not touch-screen-word-select) (or (not bounds) (> point (cdr bounds)) (< point (car bounds)))))) - (if (and touch-screen-word-select + (if (and word-select ;; point is now outside the last word selected. maybe-select-word (not (posn-object posn)) @@ -644,6 +648,22 @@ touch-screen-drag ;; If there's no buffer position at that column, go ;; to the window start. (goto-char (window-start))) + ;; If word selection is enabled, now try to keep the + ;; initially selected word within the active region. + (when word-select + (when initial + ;; If point is less than mark, which is is less + ;; than the end of the word that was originally + ;; selected, try to keep it selected by moving + ;; mark there. + (when (and (<= (point) (mark)) + (< (mark) (cdr initial))) + (set-mark (cdr initial))) + ;; Do the opposite when the converse is true. + (when (and (>= (point) (mark)) + (> (mark) (car initial))) + (set-mark (car initial)))) + (setq touch-screen-word-select-bounds nil)) ;; Display a preview of the line now around point if ;; requested by the user. (when touch-screen-preview-select @@ -669,6 +689,23 @@ touch-screen-drag ;; If there's no buffer position at that ;; column, go to the window start. (goto-char (window-start))) + ;; If word selection is enabled, now try to keep + ;; the initially selected word within the active + ;; region. + (when word-select + (when initial + ;; If point is less than mark, which is is + ;; less than the end of the word that was + ;; originally selected, try to keep it + ;; selected by moving mark there. + (when (and (<= (point) (mark)) + (< (mark) (cdr initial))) + (set-mark (cdr initial))) + ;; Do the opposite when the converse is true. + (when (and (>= (point) (mark)) + (> (mark) (car initial))) + (set-mark (car initial)))) + (setq touch-screen-word-select-bounds nil)) ;; Display a preview of the line now around ;; point if requested by the user. (when touch-screen-preview-select @@ -692,6 +729,23 @@ touch-screen-drag ;; If there's no buffer position at that column, go ;; to the window start. (goto-char (window-end nil t))) + ;; If word selection is enabled, now try to keep + ;; the initially selected word within the active + ;; region. + (when word-select + (when initial + ;; If point is less than mark, which is is less + ;; than the end of the word that was originally + ;; selected, try to keep it selected by moving + ;; mark there. + (when (and (<= (point) (mark)) + (< (mark) (cdr initial))) + (set-mark (cdr initial))) + ;; Do the opposite when the converse is true. + (when (and (>= (point) (mark)) + (> (mark) (car initial))) + (set-mark (car initial)))) + (setq touch-screen-word-select-bounds nil)) ;; Display a preview of the line now around point if ;; requested by the user. (when touch-screen-preview-select @@ -717,6 +771,23 @@ touch-screen-drag ;; If there's no buffer position at that ;; column, go to the window start. (goto-char (window-end nil t))) + ;; If word selection is enabled, now try to keep + ;; the initially selected word within the active + ;; region. + (when word-select + (when initial + ;; If point is less than mark, which is is less + ;; than the end of the word that was originally + ;; selected, try to keep it selected by moving + ;; mark there. + (when (and (<= (point) (mark)) + (< (mark) (cdr initial))) + (set-mark (cdr initial))) + ;; Do the opposite when the converse is true. + (when (and (>= (point) (mark)) + (> (mark) (car initial))) + (set-mark (car initial)))) + (setq touch-screen-word-select-bounds nil)) ;; Display a preview of the line now around ;; point if requested by the user. (when touch-screen-preview-select commit 60797e52a64da2d4403a7dfaf333b13b7687ba79 Author: Po Lu Date: Sat Jul 22 14:06:08 2023 +0800 Fix window box computation for menu bar windows * src/window.h (WINDOW_MENU_BAR_P): Check for external menu bars using HAVE_WINDOW_SYSTEM && HAVE_EXT_MENU_BAR instead of hard coding X without Xt or GTK. diff --git a/src/window.h b/src/window.h index 2a86d27d1d4..413293420fd 100644 --- a/src/window.h +++ b/src/window.h @@ -759,14 +759,14 @@ #define WINDOW_RIGHT_EDGE_X(W) \ + WINDOW_RIGHT_PIXEL_EDGE (W)) /* True if W is a menu bar window. */ -#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK) +#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR #define WINDOW_MENU_BAR_P(W) \ (WINDOWP (WINDOW_XFRAME (W)->menu_bar_window) \ && (W) == XWINDOW (WINDOW_XFRAME (W)->menu_bar_window)) -#else +#else /* !HAVE_WINDOW_SYSTEM || HAVE_EXT_MENU_BAR */ /* No menu bar windows if X toolkit is in use. */ #define WINDOW_MENU_BAR_P(W) false -#endif +#endif /* HAVE_WINDOW_SYSTEM && !HAVE_EXT_MENU_BAR */ /* True if W is a tab bar window. */ #if defined (HAVE_WINDOW_SYSTEM) commit c81bd0f60b5b2989d23da5a8ee5d17f40985c083 Author: Po Lu Date: Sat Jul 22 10:45:36 2023 +0800 Update Android port * doc/lispref/commands.texi (Key Sequence Input): Describe which events receive imaginary prefix keys. * lisp/touch-screen.el (touch-screen-translate-touch): Consider `vertical-line' a virtual function key. (function-key-map): Translate events on vertical window borders. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index df86e7e7a62..217c8cae1f5 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -3275,20 +3275,21 @@ Key Sequence Input @cindex @code{bottom-divider}, prefix key @cindex mouse events, in special parts of window or frame @cindex touch screen events, in special parts of window or frame -When mouse or @code{touch-screen-begin} events occur in special parts -of a window or frame, such as a mode line or a scroll bar, the event -type shows nothing special---it is the same symbol that would normally -represent that combination of mouse button and modifier keys. The -information about the window part is kept elsewhere in the event---in -the coordinates. But @code{read-key-sequence} translates this -information into imaginary prefix keys, all of which are symbols: -@code{tab-line}, @code{header-line}, @code{horizontal-scroll-bar}, -@code{menu-bar}, @code{tab-bar}, @code{mode-line}, -@code{vertical-line}, @code{vertical-scroll-bar}, @code{left-margin}, -@code{right-margin}, @code{left-fringe}, @code{right-fringe}, -@code{right-divider}, and @code{bottom-divider}. You can define -meanings for mouse clicks in special window parts by defining key -sequences using these imaginary prefix keys. +When mouse or @code{touchscreen-begin} and @code{touchscreen-end} +events occur in special parts of a window or frame, such as a mode +line or a scroll bar, the event type shows nothing special---it is the +same symbol that would normally represent that combination of mouse +button and modifier keys. The information about the window part is +kept elsewhere in the event---in the coordinates. But +@code{read-key-sequence} translates this information into imaginary +prefix keys, all of which are symbols: @code{tab-line}, +@code{header-line}, @code{horizontal-scroll-bar}, @code{menu-bar}, +@code{tab-bar}, @code{mode-line}, @code{vertical-line}, +@code{vertical-scroll-bar}, @code{left-margin}, @code{right-margin}, +@code{left-fringe}, @code{right-fringe}, @code{right-divider}, and +@code{bottom-divider}. You can define meanings for mouse clicks in +special window parts by defining key sequences using these imaginary +prefix keys. For example, if you call @code{read-key-sequence} and then click the mouse on the window's mode line, you get two events, like this: diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 5bd920ccc89..a4337d2f009 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -1287,6 +1287,7 @@ touch-screen-translate-touch ;; If event1 is a virtual function key, make ;; it the new prefix. (if (memq event1 '(mode-line tab-line nil + vertical-line header-line tool-bar tab-bar left-fringe right-fringe left-margin right-margin @@ -1397,6 +1398,11 @@ function-key-map (define-key function-key-map [tab-line touchscreen-end] #'touch-screen-translate-touch) +(define-key function-key-map [vertical-line touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [vertical-line touchscreen-end] + #'touch-screen-translate-touch) + (define-key function-key-map [nil touchscreen-begin] #'touch-screen-translate-touch) (define-key function-key-map [nil touchscreen-end] commit 3a9fca84f27b4188033ee8ce2417d2f213766069 Author: Po Lu Date: Sat Jul 22 10:44:34 2023 +0800 ; * etc/NEWS: Announce `current-key-remap-sequence'. diff --git a/etc/NEWS b/etc/NEWS index 0793bac0e9c..de2e0b6bd89 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -644,6 +644,11 @@ provokes an error if used numerically. * Lisp Changes in Emacs 30.1 ++++ +** New variable 'current-key-remap-sequence'. +It is bound to the key sequence that caused a call to a function bound +within `function-key-map' or `input-decode-map' around those calls. + +++ ** New variables describing the names of built in programs. The new variables 'ctags-program-name', 'ebrowse-program-name', commit 0d73b18c589394b6c3545cf73b015e7bc09575e5 Author: Po Lu Date: Sat Jul 22 08:32:59 2023 +0800 Fix default value of scroll bar frame parameters on Android * src/androidfns.c (Fx_create_frame): Default Qvertical_scroll_bars to Qnil, but set scroll-bar-width and scroll-bar-height. diff --git a/src/androidfns.c b/src/androidfns.c index 11ed2e6c00a..dcc9ab83427 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -916,8 +916,10 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0), NULL, NULL, RES_TYPE_NUMBER); - gui_default_parameter (f, parms, Qvertical_scroll_bars, - Qleft, + /* `vertical-scroll-bars' defaults to nil on Android as a + consequence of scroll bars not being supported at all. */ + + gui_default_parameter (f, parms, Qvertical_scroll_bars, Qnil, "verticalScrollBars", "ScrollBars", RES_TYPE_SYMBOL); gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil, @@ -1034,14 +1036,12 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, "cursorType", "CursorType", RES_TYPE_SYMBOL); /* Scroll bars are not supported on Android, as they are near useless. */ -#if 0 gui_default_parameter (f, parms, Qscroll_bar_width, Qnil, "scrollBarWidth", "ScrollBarWidth", RES_TYPE_NUMBER); gui_default_parameter (f, parms, Qscroll_bar_height, Qnil, "scrollBarHeight", "ScrollBarHeight", RES_TYPE_NUMBER); -#endif gui_default_parameter (f, parms, Qalpha, Qnil, "alpha", "Alpha", RES_TYPE_NUMBER); gui_default_parameter (f, parms, Qalpha_background, Qnil, commit 95db5042d5e636097bfb0f9af42f65cd5ffaa9bb Merge: ae174f266d7 ca4bc9baf9d Author: Po Lu Date: Sat Jul 22 07:59:17 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit ae174f266d70f2d722545e77d3f85dd7d3f09a68 Author: Po Lu Date: Fri Jul 21 21:23:35 2023 +0800 Improve touch screen and text conversion behavior of many commands * doc/lispref/commands.texi (Key Sequence Input): Document new argument to `read-key-sequence' etc. * lisp/help-macro.el (make-help-screen): * lisp/subr.el (read-key, read-char-choice-with-read-key): Disable text conversion and display the OSK before reading a key sequence. * lisp/touch-screen.el (touch-screen-window-selection-changed): Only cancel the minibuffer OSK timer. (touch-screen-handle-point-up): Update comment accordingly. * src/keyboard.c (command_loop_1, read_menu_command) (read_key_sequence, read_key_sequence_vs, Fread_key_sequence) (Fread_key_sequence_vector): New arg DISABLE_TEXT_CONVERSION. All callers changed. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 52f7bcd302f..df86e7e7a62 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -3160,7 +3160,7 @@ Key Sequence Input @code{read-key-sequence}. Lisp programs can also call this function; for example, @code{describe-key} uses it to read the key to describe. -@defun read-key-sequence prompt &optional continue-echo dont-downcase-last switch-frame-ok command-loop +@defun read-key-sequence prompt &optional continue-echo dont-downcase-last switch-frame-ok command-loop disable-text-conversion This function reads a key sequence and returns it as a string or vector. It keeps reading events until it has accumulated a complete key sequence; that is, enough to specify a non-prefix command using the @@ -3200,6 +3200,12 @@ Key Sequence Input after another. It should be @code{nil} if the caller will read just one key sequence. +The argument @var{disable-text-conversion}, if non-@code{nil}, means +that system input methods will not directly perform edits to buffer +text while this key sequence is being read; user input will always +generated individual key events instead. @xref{Misc Events} for more +about text conversion. + In the following example, Emacs displays the prompt @samp{?} in the echo area, and then the user types @kbd{C-x C-f}. @@ -3220,7 +3226,7 @@ Key Sequence Input and does not set @code{quit-flag}. @xref{Quitting}. @end defun -@defun read-key-sequence-vector prompt &optional continue-echo dont-downcase-last switch-frame-ok command-loop +@defun read-key-sequence-vector prompt &optional continue-echo dont-downcase-last switch-frame-ok command-loop disable-text-conversion This is like @code{read-key-sequence} except that it always returns the key sequence as a vector, never as a string. @xref{Strings of Events}. diff --git a/lisp/help-macro.el b/lisp/help-macro.el index 6fd0ca3bf98..0905d1946f3 100644 --- a/lisp/help-macro.el +++ b/lisp/help-macro.el @@ -166,6 +166,7 @@ make-help-screen (error nil)) (let ((cursor-in-echo-area t) (overriding-local-map local-map)) + (frame-toggle-on-screen-keyboard nil nil) (setq key (read-key-sequence (format "Type one of listed options%s: " (if (pos-visible-in-window-p @@ -179,7 +180,13 @@ make-help-screen (help--key-description-fontified (kbd "SPC")) "/" (help--key-description-fontified (kbd "DEL")) - " to scroll")))) + " to scroll"))) + nil nil nil nil + ;; Disable ``text conversion''. OS + ;; input methods might otherwise chose + ;; to insert user input directly into + ;; a buffer. + t) char (aref key 0))) ;; If this is a scroll bar command, just run it. diff --git a/lisp/subr.el b/lisp/subr.el index 04f89f77d17..4346f99fa38 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3097,6 +3097,11 @@ read-key When there's an ambiguity because the key looks like the prefix of some sort of escape sequence, the ambiguity is resolved via `read-key-delay'. +Also in contrast to `read-event', input method text conversion +will be disabled while the key sequence is read, so that +character input events will always be generated for keyboard +input. + If the optional argument PROMPT is non-nil, display that as a prompt. @@ -3155,7 +3160,8 @@ read-key (lookup-key global-map [tool-bar]))) map)) (let* ((keys - (catch 'read-key (read-key-sequence-vector prompt nil t))) + (catch 'read-key (read-key-sequence-vector prompt nil t + nil nil t))) (key (aref keys 0))) (if (and (> (length keys) 1) (memq key '(mode-line header-line @@ -3341,6 +3347,8 @@ read-char-choice-with-read-key (while (not done) (unless (get-text-property 0 'face prompt) (setq prompt (propertize prompt 'face 'minibuffer-prompt))) + ;; Display the on screen keyboard if it exists. + (frame-toggle-on-screen-keyboard nil t) (setq char (let ((inhibit-quit inhibit-keyboard-quit)) (read-key prompt))) (and show-help (buffer-live-p (get-buffer helpbuf)) diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 687722f4792..5bd920ccc89 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -926,16 +926,14 @@ touch-screen-handle-point-update (defun touch-screen-window-selection-changed (frame) "Notice that FRAME's selected window has changed. -If point is now on read only text, hide the on screen keyboard. -Otherwise, cancel any timer that is supposed to hide the keyboard -in response to the minibuffer being closed." +Cancel any timer that is supposed to hide the keyboard in +response to the minibuffer being closed." (with-selected-frame frame - (if (and (or buffer-read-only - (get-text-property (point) 'read-only)) - ;; Don't hide the on-screen keyboard if it's always - ;; supposed to be displayed. - (not touch-screen-display-keyboard)) - (frame-toggle-on-screen-keyboard (selected-frame) t) + (unless (and (or buffer-read-only + (get-text-property (point) 'read-only)) + ;; Don't hide the on-screen keyboard if it's always + ;; supposed to be displayed. + (not touch-screen-display-keyboard)) ;; Prevent hiding the minibuffer from hiding the on screen ;; keyboard. (when minibuffer-on-screen-keyboard-timer @@ -1027,10 +1025,8 @@ touch-screen-handle-point-up ;; opened, add ;; `touch-screen-window-selection-changed' ;; as a window selection change function - ;; This allows the on screen keyboard to be - ;; hidden if the selected window's point - ;; becomes read only at some point in the - ;; future. + ;; This then prevents it from being hidden + ;; after exiting the minibuffer. (progn (add-hook 'window-selection-change-functions #'touch-screen-window-selection-changed) diff --git a/src/callint.c b/src/callint.c index d8d2b278458..00e9a080654 100644 --- a/src/callint.c +++ b/src/callint.c @@ -537,7 +537,8 @@ DEFUN ("call-interactively", Fcall_interactively, Scall_interactively, 1, 3, 0, make_fixnum (SCHARS (callint_message)), Qface, Qminibuffer_prompt, callint_message); args[i] = Fread_key_sequence (callint_message, - Qnil, Qnil, Qnil, Qnil); + Qnil, Qnil, Qnil, Qnil, + Qnil); unbind_to (speccount1, Qnil); visargs[i] = Fkey_description (args[i], Qnil); @@ -567,7 +568,8 @@ DEFUN ("call-interactively", Fcall_interactively, Scall_interactively, 1, 3, 0, make_fixnum (SCHARS (callint_message)), Qface, Qminibuffer_prompt, callint_message); args[i] = Fread_key_sequence_vector (callint_message, - Qnil, Qt, Qnil, Qnil); + Qnil, Qt, Qnil, Qnil, + Qnil); visargs[i] = Fkey_description (args[i], Qnil); unbind_to (speccount1, Qnil); diff --git a/src/keyboard.c b/src/keyboard.c index 97172be8152..f60b5c95654 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -1294,7 +1294,7 @@ some_mouse_moved (void) enum { READ_KEY_ELTS = 30 }; static int read_key_sequence (Lisp_Object *, Lisp_Object, - bool, bool, bool, bool); + bool, bool, bool, bool, bool); static void adjust_point_for_property (ptrdiff_t, bool); static Lisp_Object @@ -1405,7 +1405,8 @@ command_loop_1 (void) /* Read next key sequence; i gets its length. */ raw_keybuf_count = 0; Lisp_Object keybuf[READ_KEY_ELTS]; - int i = read_key_sequence (keybuf, Qnil, false, true, true, false); + int i = read_key_sequence (keybuf, Qnil, false, true, true, false, + false); /* A filter may have run while we were reading the input. */ if (! FRAME_LIVE_P (XFRAME (selected_frame))) @@ -1680,7 +1681,8 @@ read_menu_command (void) specbind (Qecho_keystrokes, make_fixnum (0)); Lisp_Object keybuf[READ_KEY_ELTS]; - int i = read_key_sequence (keybuf, Qnil, false, true, true, true); + int i = read_key_sequence (keybuf, Qnil, false, true, true, true, + false); unbind_to (count, Qnil); @@ -10276,12 +10278,16 @@ restore_reading_key_sequence (int old_reading_key_sequence) read_char will return it. If FIX_CURRENT_BUFFER, we restore current_buffer - from the selected window's buffer. */ + from the selected window's buffer. + + If DISABLE_TEXT_CONVERSION_P, disable text conversion so the input + method will always send key events. */ static int read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt, bool dont_downcase_last, bool can_return_switch_frame, - bool fix_current_buffer, bool prevent_redisplay) + bool fix_current_buffer, bool prevent_redisplay, + bool disable_text_conversion_p) { specpdl_ref count = SPECPDL_INDEX (); @@ -10351,7 +10357,7 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt, /* Whether or not text conversion has already been disabled. */ disabled_conversion = false; -#endif +#endif /* HAVE_TEXT_CONVERSION */ struct buffer *starting_buffer; @@ -10445,6 +10451,18 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt, if (INTERACTIVE && t < mock_input) echo_truncate (echo_start); + /* If text conversion is supposed to be disabled immediately, do it + now. */ + +#ifdef HAVE_TEXT_CONVERSION + if (disable_text_conversion_p) + { + disable_text_conversion (); + record_unwind_protect_void (resume_text_conversion); + disabled_conversion = true; + } +#endif /* HAVE_TEXT_CONVERSION */ + /* If the best binding for the current key sequence is a keymap, or we may be looking at a function key's escape sequence, keep on reading. */ @@ -11279,7 +11297,8 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt, read_key_sequence_vs (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object dont_downcase_last, Lisp_Object can_return_switch_frame, - Lisp_Object cmd_loop, bool allow_string) + Lisp_Object cmd_loop, bool allow_string, + bool disable_text_conversion) { specpdl_ref count = SPECPDL_INDEX (); @@ -11306,7 +11325,8 @@ read_key_sequence_vs (Lisp_Object prompt, Lisp_Object continue_echo, raw_keybuf_count = 0; Lisp_Object keybuf[READ_KEY_ELTS]; int i = read_key_sequence (keybuf, prompt, ! NILP (dont_downcase_last), - ! NILP (can_return_switch_frame), false, false); + ! NILP (can_return_switch_frame), false, false, + disable_text_conversion); #if 0 /* The following is fine for code reading a key sequence and then proceeding with a lengthy computation, but it's not good @@ -11328,7 +11348,7 @@ read_key_sequence_vs (Lisp_Object prompt, Lisp_Object continue_echo, (i, keybuf))); } -DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 5, 0, +DEFUN ("read-key-sequence", Fread_key_sequence, Sread_key_sequence, 1, 6, 0, doc: /* Read a sequence of keystrokes and return as a string or vector. The sequence is sufficient to specify a non-prefix command in the current local and global maps. @@ -11374,20 +11394,31 @@ Second (optional) arg CONTINUE-ECHO, if non-nil, means this key echos The optional fifth argument CMD-LOOP, if non-nil, means that this key sequence is being read by something that will read commands one after another. It should be nil if the caller -will read just one key sequence. */) - (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object dont_downcase_last, Lisp_Object can_return_switch_frame, Lisp_Object cmd_loop) +will read just one key sequence. + +The optional sixth argument DISABLE-TEXT-CONVERSION, if non-nil, means +disable input method text conversion for the duration of reading this +key sequence, and that keyboard input will always result in key events +being sent. */) + (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object dont_downcase_last, + Lisp_Object can_return_switch_frame, Lisp_Object cmd_loop, + Lisp_Object disable_text_conversion) { return read_key_sequence_vs (prompt, continue_echo, dont_downcase_last, - can_return_switch_frame, cmd_loop, true); + can_return_switch_frame, cmd_loop, true, + !NILP (disable_text_conversion)); } DEFUN ("read-key-sequence-vector", Fread_key_sequence_vector, - Sread_key_sequence_vector, 1, 5, 0, + Sread_key_sequence_vector, 1, 6, 0, doc: /* Like `read-key-sequence' but always return a vector. */) - (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object dont_downcase_last, Lisp_Object can_return_switch_frame, Lisp_Object cmd_loop) + (Lisp_Object prompt, Lisp_Object continue_echo, Lisp_Object dont_downcase_last, + Lisp_Object can_return_switch_frame, Lisp_Object cmd_loop, + Lisp_Object disable_text_conversion) { return read_key_sequence_vs (prompt, continue_echo, dont_downcase_last, - can_return_switch_frame, cmd_loop, false); + can_return_switch_frame, cmd_loop, false, + !NILP (disable_text_conversion)); } /* Return true if input events are pending. */ commit 2df3f89014a39342c402637296ee88e3749f4918 Author: Po Lu Date: Fri Jul 21 15:20:45 2023 +0800 Correctly translate touchscreen-up events outside a frame * lisp/touch-screen.el (touch-screen-translate-touch): Check if a prefix is specified separately from prefix being non-nil. Accept `nil' as an imaginary prefix key. (function-key-map): Register translation functions on the tab bar, tab lines and internal border. diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 4f930704869..687722f4792 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -1271,7 +1271,11 @@ touch-screen-translate-touch if POSN is on a link or a button, or `mouse-1' otherwise." (unwind-protect ;; Save the virtual function key if this is a mode line event. - (let* ((prefix (and (> (length current-key-remap-sequence) 1) + (let* ((prefix-specified + ;; Virtual prefix keys can be nil for events that fall + ;; outside a frame or within its internal border. + (> (length current-key-remap-sequence) 1)) + (prefix (and prefix-specified (aref current-key-remap-sequence 0))) (touch-screen-translate-prompt prompt) (event (catch 'input-event @@ -1279,14 +1283,14 @@ touch-screen-translate-touch ;; `current-key-remap-sequence'. (touch-screen-handle-touch (aref current-key-remap-sequence - (if prefix 1 0)) + (if prefix-specified 1 0)) prefix) ;; Next, continue reading input events. (while t (let ((event1 (read-event))) ;; If event1 is a virtual function key, make ;; it the new prefix. - (if (memq event1 '(mode-line tab-line + (if (memq event1 '(mode-line tab-line nil header-line tool-bar tab-bar left-fringe right-fringe left-margin right-margin @@ -1387,6 +1391,21 @@ function-key-map (define-key function-key-map [tool-bar touchscreen-end] #'touch-screen-translate-touch) +(define-key function-key-map [tab-bar touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [tab-bar touchscreen-end] + #'touch-screen-translate-touch) + +(define-key function-key-map [tab-line touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [tab-line touchscreen-end] + #'touch-screen-translate-touch) + +(define-key function-key-map [nil touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [nil touchscreen-end] + #'touch-screen-translate-touch) + ;; Exports. These functions are intended for use externally. commit 5d89602e290770d699d2dba860e4b5119fe0a30c Author: Po Lu Date: Fri Jul 21 14:22:54 2023 +0800 Improve touch screen scrolling support * lisp/touch-screen.el (touch-screen-preview-select): Avoid unnecessary redisplays. (touch-screen-drag): Scroll at window margins using window scrolling functions instead of relying on redisplay to recenter the window around point. diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index f9611e269f4..4f930704869 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -405,114 +405,110 @@ touch-screen-preview-select (long-line-optimizations-p) (let ((window-line-height (window-line-height)) (maximum-height (* 2 (frame-char-height)))) - (or (and window-line-height - (> (car window-line-height) - maximum-height)) - ;; `window-line-height' isn't available. - ;; Redisplay first and try to ascertain the height - ;; of the line again. - (prog1 nil (redisplay t)) - ;; Likewise if the line height still isn't - ;; available. - (not (setq window-line-height - (window-line-height))) - ;; Actually check the height now. - (> (car window-line-height) - maximum-height)))) - (if (catch 'hscrolled-away - (let ((beg nil) end string y) - ;; Detect whether or not the window is hscrolled. If it - ;; is, set beg to the location of the first column - ;; instead. - (when (> (window-hscroll) 0) - (setq y (+ (or (cdr (posn-x-y (posn-at-point))) - (throw 'hscrolled-away t)) - (window-header-line-height) - (window-tab-line-height))) - (let* ((posn (posn-at-x-y 0 y)) - (point (posn-point posn))) - (setq beg point))) - ;; Check if lines are being truncated; if so, use the - ;; character at the end of the window as the end of the - ;; text to be displayed, as the visual line may extend - ;; past the window. - (when (or truncate-lines beg) ; truncate-lines or hscroll. - (setq y (or y (+ (or (cdr (posn-x-y (posn-at-point))) - (throw 'hscrolled-away t)) - (window-header-line-height) - (window-tab-line-height)))) - (let* ((posn (posn-at-x-y (1- (window-width nil t)) y)) - (point (posn-point posn))) - (setq end point))) - ;; Now find the rest of the visual line. - (save-excursion - (unless beg - (beginning-of-visual-line) - (setq beg (point))) - (unless end - (end-of-visual-line) - (setq end (point)))) - ;; Obtain a substring containing the beginning of the - ;; visual line and the end. - (setq string (buffer-substring beg end)) - ;; Hack `invisible' properties within the new string. - ;; Look for each change of the property that is a variable - ;; name and replace it with its actual value according to - ;; `buffer-invisibility-spec'. - (when (listp buffer-invisibility-spec) - (let ((index 0) - (property (get-text-property 0 - 'invisible - string)) - index1 invisible) - (while index - ;; Find the end of this text property. - (setq index1 (next-single-property-change index - 'invisible - string)) - ;; Replace the property with whether or not it is - ;; non-nil. - (when property - (setq invisible nil) - (catch 'invisible - (dolist (spec buffer-invisibility-spec) - ;; Process one element of the buffer - ;; invisibility specification. - (if (consp spec) - (when (eq (cdr spec) 't) - ;; (ATOM . t) makes N invisible if N is - ;; equal to ATOM or a list containing - ;; ATOM. - (when (or (eq (car spec) property) - (and (listp spec) - (memq (car spec) invisible))) - (throw 'invisible (setq invisible t)))) - ;; Otherwise, N is invisible if SPEC is - ;; equal to N. - (when (eq spec property) - (throw 'invisible (setq invisible t)))))) - (put-text-property index (or index1 - (- end beg)) - 'invisible invisible string)) - ;; Set index to that of the next text property and - ;; continue. - (setq index index1 - property (and index1 - (get-text-property index1 - 'invisible - string)))))) - (let ((resize-mini-windows t) difference width - (message-log-max nil)) - ;; Find the offset of point from beg and display a cursor - ;; below. - (setq difference (- (point) beg) - width (string-pixel-width - (substring string 0 difference))) - (message "%s\n%s^" string - (propertize " " - 'display (list 'space - :width (list width))))) - nil))))) + (unless window-line-height + ;; `window-line-height' isn't available. + ;; Redisplay first and try to ascertain the height + ;; of the line again. + (redisplay t) + (setq window-line-height (window-line-height))) + ;; `window-line-height' might still be unavailable. + (and window-line-height + (> (car window-line-height) + maximum-height)))) + (catch 'hscrolled-away + (let ((beg nil) end string y) + ;; Detect whether or not the window is hscrolled. If it + ;; is, set beg to the location of the first column + ;; instead. + (when (> (window-hscroll) 0) + (setq y (+ (or (cdr (posn-x-y (posn-at-point))) + (throw 'hscrolled-away t)) + (window-header-line-height) + (window-tab-line-height))) + (let* ((posn (posn-at-x-y 0 y)) + (point (posn-point posn))) + (setq beg point))) + ;; Check if lines are being truncated; if so, use the + ;; character at the end of the window as the end of the + ;; text to be displayed, as the visual line may extend + ;; past the window. + (when (or truncate-lines beg) ; truncate-lines or hscroll. + (setq y (or y (+ (or (cdr (posn-x-y (posn-at-point))) + (throw 'hscrolled-away t)) + (window-header-line-height) + (window-tab-line-height)))) + (let* ((posn (posn-at-x-y (1- (window-width nil t)) y)) + (point (posn-point posn))) + (setq end point))) + ;; Now find the rest of the visual line. + (save-excursion + (unless beg + (beginning-of-visual-line) + (setq beg (point))) + (unless end + (end-of-visual-line) + (setq end (point)))) + ;; Obtain a substring containing the beginning of the + ;; visual line and the end. + (setq string (buffer-substring beg end)) + ;; Hack `invisible' properties within the new string. + ;; Look for each change of the property that is a variable + ;; name and replace it with its actual value according to + ;; `buffer-invisibility-spec'. + (when (listp buffer-invisibility-spec) + (let ((index 0) + (property (get-text-property 0 + 'invisible + string)) + index1 invisible) + (while index + ;; Find the end of this text property. + (setq index1 (next-single-property-change index + 'invisible + string)) + ;; Replace the property with whether or not it is + ;; non-nil. + (when property + (setq invisible nil) + (catch 'invisible + (dolist (spec buffer-invisibility-spec) + ;; Process one element of the buffer + ;; invisibility specification. + (if (consp spec) + (when (eq (cdr spec) 't) + ;; (ATOM . t) makes N invisible if N is + ;; equal to ATOM or a list containing + ;; ATOM. + (when (or (eq (car spec) property) + (and (listp spec) + (memq (car spec) invisible))) + (throw 'invisible (setq invisible t)))) + ;; Otherwise, N is invisible if SPEC is + ;; equal to N. + (when (eq spec property) + (throw 'invisible (setq invisible t)))))) + (put-text-property index (or index1 + (- end beg)) + 'invisible invisible string)) + ;; Set index to that of the next text property and + ;; continue. + (setq index index1 + property (and index1 + (get-text-property index1 + 'invisible + string)))))) + (let ((resize-mini-windows t) difference width + (message-log-max nil)) + ;; Find the offset of point from beg and display a cursor + ;; below. + (setq difference (- (point) beg) + width (string-pixel-width + (substring string 0 difference))) + (message "%s\n%s^" string + (propertize " " + 'display (list 'space + :width (list width))))) + nil)))) (defun touch-screen-drag (event) "Handle a drag EVENT by setting the region to its new point. @@ -523,113 +519,208 @@ touch-screen-drag (interactive "e") (let* ((posn (cadr event)) ; Position of the tool. (point (posn-point posn)) ; Point of the event. - ; Window where the tap originated. + ;; Window where the tap originated. (window (nth 1 touch-screen-current-tool)) + ;; The currently selected window. Used to redisplay within + ;; the correct window while scrolling. + (old-window (selected-window)) initial-point) ;; Keep dragging. (with-selected-window window - ;; Figure out what character to go to. If this posn is - ;; in the window, go to (posn-point posn). If not, - ;; then go to the line before either window start or - ;; window end. + ;; Figure out what character to go to. If this posn is in the + ;; window, go to (posn-point posn). If not, then go to the line + ;; before either window start or window end. (setq initial-point (point)) - (if (and (eq (posn-window posn) window) - point (not (eq point initial-point))) - (let* ((bounds touch-screen-word-select-bounds) - (initial touch-screen-word-select-initial-word) - (maybe-select-word (or (not touch-screen-word-select) - (or (not bounds) - (> point (cdr bounds)) - (< point (car bounds)))))) - (if (and touch-screen-word-select - ;; point is now outside the last word selected. - maybe-select-word - (not (posn-object posn)) - (when-let* ((char (char-after point)) - (class (char-syntax char))) - ;; Don't select words if point isn't inside a - ;; word constituent or similar. - (or (eq class ?w) (eq class ?_)))) - ;; Determine the confines of the word containing - ;; POINT. - (let (word-start word-end) - (save-excursion - (goto-char point) - (forward-word-strictly) - ;; Set word-end to ZV if there is no word after - ;; this one. - (setq word-end (point)) - ;; Now try to move backwards. Set word-start to - ;; BEGV if this word is there. - (backward-word-strictly) - (setq word-start (point))) - (let ((mark (mark))) - ;; Extend the region to cover either word-end or - ;; word-start; whether to goto word-end or - ;; word-start is subject to the position of the - ;; mark relative to point. - (if (< word-start mark) - ;; The start of the word is behind mark. - ;; Extend the region towards the start. - (goto-char word-start) - ;; Else, go to the end of the word. - (goto-char word-end)) + (when (or (not point) + (not (eq point initial-point))) + (if (and (eq (posn-window posn) window) + point + ;; point must be visible in the window. If it isn't, + ;; the window must be scrolled. + (pos-visible-in-window-p point)) + (let* ((bounds touch-screen-word-select-bounds) + (initial touch-screen-word-select-initial-word) + (maybe-select-word (or (not touch-screen-word-select) + (or (not bounds) + (> point (cdr bounds)) + (< point (car bounds)))))) + (if (and touch-screen-word-select + ;; point is now outside the last word selected. + maybe-select-word + (not (posn-object posn)) + (when-let* ((char (char-after point)) + (class (char-syntax char))) + ;; Don't select words if point isn't inside a + ;; word constituent or similar. + (or (eq class ?w) (eq class ?_)))) + ;; Determine the confines of the word containing + ;; POINT. + (let (word-start word-end) + (save-excursion + (goto-char point) + (forward-word-strictly) + ;; Set word-end to ZV if there is no word after + ;; this one. + (setq word-end (point)) + ;; Now try to move backwards. Set word-start to + ;; BEGV if this word is there. + (backward-word-strictly) + (setq word-start (point))) + (let ((mark (mark))) + ;; Extend the region to cover either word-end or + ;; word-start; whether to goto word-end or + ;; word-start is subject to the position of the + ;; mark relative to point. + (if (< word-start mark) + ;; The start of the word is behind mark. + ;; Extend the region towards the start. + (goto-char word-start) + ;; Else, go to the end of the word. + (goto-char word-end)) + ;; If point is less than mark, which is is less + ;; than the end of the word that was originally + ;; selected, try to keep it selected by moving + ;; mark there. + (when (and initial (<= (point) mark) + (< mark (cdr initial))) + (set-mark (cdr initial))) + ;; Do the opposite when the converse is true. + (when (and initial (>= (point) mark) + (> mark (car initial))) + (set-mark (car initial)))) + (if bounds + (progn (setcar bounds word-start) + (setcdr bounds word-end)) + (setq touch-screen-word-select-bounds + (cons word-start word-end)))) + (when maybe-select-word + (goto-char (posn-point posn)) + (when initial ;; If point is less than mark, which is is less ;; than the end of the word that was originally ;; selected, try to keep it selected by moving ;; mark there. - (when (and initial (<= (point) mark) - (< mark (cdr initial))) + (when (and (<= (point) (mark)) + (< (mark) (cdr initial))) (set-mark (cdr initial))) ;; Do the opposite when the converse is true. - (when (and initial (>= (point) mark) - (> mark (car initial))) + (when (and (>= (point) (mark)) + (> (mark) (car initial))) (set-mark (car initial)))) - (if bounds - (progn (setcar bounds word-start) - (setcdr bounds word-end)) - (setq touch-screen-word-select-bounds - (cons word-start word-end)))) - (when maybe-select-word - (goto-char (posn-point posn)) - (when initial - ;; If point is less than mark, which is is less than - ;; the end of the word that was originally selected, - ;; try to keep it selected by moving mark there. - (when (and (<= (point) (mark)) - (< (mark) (cdr initial))) - (set-mark (cdr initial))) - ;; Do the opposite when the converse is true. - (when (and (>= (point) (mark)) - (> (mark) (car initial))) - (set-mark (car initial)))) - (setq touch-screen-word-select-bounds nil))) - ;; Finally, display a preview of the line around point if - ;; requested by the user. - (when (and touch-screen-preview-select - (not (eq (point) initial-point))) - (touch-screen-preview-select))) - ;; POSN is outside the window. Scroll accordingly. - (let ((relative-xy - (touch-screen-relative-xy posn window))) - (let ((scroll-conservatively 101)) + (setq touch-screen-word-select-bounds nil))) + ;; Finally, display a preview of the line around point + ;; if requested by the user. + (when (and touch-screen-preview-select + (not (eq (point) initial-point))) + (touch-screen-preview-select))) + ;; POSN is outside the window. Scroll accordingly. + (let* ((relative-xy + (touch-screen-relative-xy posn window)) + (xy (posn-x-y posn)) + ;; The height of the window's text area. + (body-height (window-body-height nil t)) + ;; This is used to find the character closest to + ;; POSN's column at the bottom of the window. + (height (- body-height + ;; Use the last row of the window, not its + ;; last pixel. + (frame-char-height))) + (midpoint (/ body-height 2)) + (scroll-conservatively 101)) (cond - ((< (cdr relative-xy) 0) + ((< (cdr relative-xy) midpoint) + ;; POSN is before half the window, yet POINT does not + ;; exist or is not completely visible within. Scroll + ;; downwards. (ignore-errors - (goto-char (1- (window-start))) - (setq touch-screen-word-select-bounds nil)) - (redisplay)) - ((> (cdr relative-xy) - (let ((edges (window-inside-pixel-edges))) - (- (nth 3 edges) (cadr edges)))) + ;; Scroll down by a single line. + (scroll-down 1) + ;; After scrolling, look up the new posn at EVENT's + ;; column and go there. + (setq posn (posn-at-x-y (car xy) 0) + point (posn-point posn)) + (if point + (goto-char point) + ;; If there's no buffer position at that column, go + ;; to the window start. + (goto-char (window-start))) + ;; Display a preview of the line now around point if + ;; requested by the user. + (when touch-screen-preview-select + (touch-screen-preview-select)) + ;; Select old-window, so that redisplay doesn't + ;; display WINDOW as selected if it isn't already. + (with-selected-window old-window + ;; Now repeat this every `mouse-scroll-delay' until + ;; input becomes available, but scroll down a few + ;; more lines. + (while (sit-for mouse-scroll-delay) + ;; Select WINDOW again. + (with-selected-window window + ;; Keep scrolling down until input becomes + ;; available. + (scroll-down 4) + ;; After scrolling, look up the new posn at + ;; EVENT's column and go there. + (setq posn (posn-at-x-y (car xy) 0) + point (posn-point posn)) + (if point + (goto-char point) + ;; If there's no buffer position at that + ;; column, go to the window start. + (goto-char (window-start))) + ;; Display a preview of the line now around + ;; point if requested by the user. + (when touch-screen-preview-select + (touch-screen-preview-select)))))) + (setq touch-screen-word-select-bounds nil)) + ((>= (cdr relative-xy) midpoint) + ;; Default to scrolling upwards even if POSN is still + ;; within the confines of the window. If POINT is + ;; partially visible, and the branch above hasn't been + ;; taken it must be somewhere at the bottom of the + ;; window, so scroll downwards. (ignore-errors - (goto-char (1+ (window-end nil t))) - (setq touch-screen-word-select-bounds nil)) - (redisplay))) - ;; Finally, display a preview of the line now around point - ;; if requested by the user. - (when touch-screen-preview-select - (touch-screen-preview-select)))))))) + ;; Scroll up by a single line. + (scroll-up 1) + ;; After scrolling, look up the new posn at EVENT's + ;; column and go there. + (setq posn (posn-at-x-y (car xy) height) + point (posn-point posn)) + (if point + (goto-char point) + ;; If there's no buffer position at that column, go + ;; to the window start. + (goto-char (window-end nil t))) + ;; Display a preview of the line now around point if + ;; requested by the user. + (when touch-screen-preview-select + (touch-screen-preview-select)) + ;; Select old-window, so that redisplay doesn't + ;; display WINDOW as selected if it isn't already. + (with-selected-window old-window + ;; Now repeat this every `mouse-scroll-delay' until + ;; input becomes available, but scroll down a few + ;; more lines. + (while (sit-for mouse-scroll-delay) + ;; Select WINDOW again. + (with-selected-window window + ;; Keep scrolling down until input becomes + ;; available. + (scroll-up 4) + ;; After scrolling, look up the new posn at + ;; EVENT's column and go there. + (setq posn (posn-at-x-y (car xy) height) + point (posn-point posn)) + (if point + (goto-char point) + ;; If there's no buffer position at that + ;; column, go to the window start. + (goto-char (window-end nil t))) + ;; Display a preview of the line now around + ;; point if requested by the user. + (when touch-screen-preview-select + (touch-screen-preview-select)))))))))))))) (defun touch-screen-restart-drag (event) "Restart dragging to select text. commit e1761019a99f80b22f63e94be10ab1a5722d01b2 Author: Po Lu Date: Fri Jul 21 12:23:08 2023 +0800 Update Android port * doc/emacs/input.texi (Touchscreens): Document `touch-screen-preview-select'. * doc/lispref/commands.texi (Touchscreen Events): Fix typo in the descriptions of two touch screen events. * lisp/dired.el (dired-insert-set-properties): Adjust for changes to file end computation. * lisp/minibuffer.el (clear-minibuffer-message): Don't clear minibuffer message if dragging. * lisp/touch-screen.el (touch-screen-current-tool): Fix doc string. (touch-screen-preview-select): New function. (touch-screen-drag): Call it if point changes. diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi index f5b0d0570e1..671901fea88 100644 --- a/doc/emacs/input.texi +++ b/doc/emacs/input.texi @@ -77,6 +77,17 @@ Touchscreens of the point or the mark within a window will begin a new ``drag'' gesture, where the region will be extended in the direction of any subsequent movement. + +@vindex touch-screen-preview-select +@cindex previewing the region during selection, touchscreens + Difficulties in making accurate adjustments to the region can also +be alleviated by indicating the position of the point relative to its +containing line within the echo area, since the window cursor may be +physically obscured by the tool. If +@code{touch-screen-preview-select} is non-@code{nil}, the line +containing point is displayed in the echo area (@pxref{Echo Area}) +during the motion of the tool, followed by another line indicating the +position of point within the first line. @end itemize @vindex touch-screen-delay diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 9a7146d7eae..52f7bcd302f 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2011,7 +2011,7 @@ Touchscreen Events This event is sent when @var{point} is created by the user pressing a finger against the touchscreen. -These events also have imaginary prefixes keys added by +Imaginary prefix keys are also affixed to these events @code{read-key-sequence} when they originate on top of a special part of a frame or window. @xref{Key Sequence Input}. @@ -2032,7 +2032,7 @@ Touchscreen Events should undo or avoid any editing commands that would otherwise result from the touch sequence. -These events also have imaginary prefixes keys added by +Imaginary prefix keys are also affixed to these events @code{read-key-sequence} when they originate on top of a special part of a frame or window. @end table diff --git a/lisp/dired.el b/lisp/dired.el index 084ef063c4c..80aefd59771 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -1916,10 +1916,10 @@ dired-insert-set-properties (fboundp 'x-begin-drag)) "down-mouse-1: drag this file to another program mouse-2: visit this file in other window" - "mouse-2: visit this file in other window"))))) - (when (< (+ (point) 4) (line-end-position)) - (put-text-property (+ (point) 4) (line-end-position) - 'invisible 'dired-hide-details-link)))) + "mouse-2: visit this file in other window")))) + (when (< (+ end 5) (line-end-position)) + (put-text-property (+ end 5) (line-end-position) + 'invisible 'dired-hide-details-link))))) (forward-line 1)))) (defun dired--make-directory-clickable () diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 3cf679867b3..35b359a75e2 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -973,10 +973,16 @@ clear-minibuffer-message (when (overlayp minibuffer-message-overlay) (delete-overlay minibuffer-message-overlay) (setq minibuffer-message-overlay nil))) - - ;; Return nil telling the caller that the message - ;; should be also handled by the caller. - nil) + ;; Don't clear the message if touch screen drag-to-select is in + ;; progress, because a preview message might currently be displayed + ;; in the echo area. FIXME: find some way to place this in + ;; touch-screen.el. + (if (and touch-screen-preview-select + (eq (nth 3 touch-screen-current-tool) 'drag)) + 'dont-clear-message + ;; Return nil telling the caller that the message + ;; should be also handled by the caller. + nil)) (setq clear-message-function 'clear-minibuffer-message) diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 89dc1c61cb6..f9611e269f4 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -40,7 +40,7 @@ touch-screen-current-tool touch point, the initial position of the touchpoint, and another four fields to used store data while tracking the touch point. See `touch-screen-handle-point-update' and -`touch-screen-handle-point-up' for the meanings of the fifth +`touch-screen-handle-point-up' for the meanings of the fourth element.") (defvar touch-screen-set-point-commands '(mouse-set-point) @@ -96,6 +96,15 @@ touch-screen-extend-selection :group 'mouse :version "30.1") +(defcustom touch-screen-preview-select nil + "If non-nil, display a preview while selecting text. +When enabled, a preview of the visible line within the window +will be displayed in the echo area while dragging combined with +an indication of the position of point within that line." + :type 'boolean + :group 'mouse + :version "30.1") + (defvar-local touch-screen-word-select-bounds nil "The start and end positions of the word last selected. Normally a cons of those two positions or nil if no word was @@ -377,6 +386,134 @@ touch-screen-hold touch-screen-word-select-initial-word (cons word-start word-end))))))))) +(defun touch-screen-preview-select () + "Display a preview of the line around point in the echo area. +Unless the minibuffer is an active or the current line is +excessively tall, display an indication of the position of point +and the contents of the visible line around it within the echo +area. + +If the selected window is hscrolled or lines may be truncated, +attempt to find the extents of the text between column 0 and the +right most column of the window using `posn-at-x-y'." + (interactive) + ;; First, establish that the minibuffer isn't active and the line + ;; isn't taller than two times the frame character height. + (unless (or (> (minibuffer-depth) 0) + ;; The code below doesn't adapt well to buffers + ;; containing long lines. + (long-line-optimizations-p) + (let ((window-line-height (window-line-height)) + (maximum-height (* 2 (frame-char-height)))) + (or (and window-line-height + (> (car window-line-height) + maximum-height)) + ;; `window-line-height' isn't available. + ;; Redisplay first and try to ascertain the height + ;; of the line again. + (prog1 nil (redisplay t)) + ;; Likewise if the line height still isn't + ;; available. + (not (setq window-line-height + (window-line-height))) + ;; Actually check the height now. + (> (car window-line-height) + maximum-height)))) + (if (catch 'hscrolled-away + (let ((beg nil) end string y) + ;; Detect whether or not the window is hscrolled. If it + ;; is, set beg to the location of the first column + ;; instead. + (when (> (window-hscroll) 0) + (setq y (+ (or (cdr (posn-x-y (posn-at-point))) + (throw 'hscrolled-away t)) + (window-header-line-height) + (window-tab-line-height))) + (let* ((posn (posn-at-x-y 0 y)) + (point (posn-point posn))) + (setq beg point))) + ;; Check if lines are being truncated; if so, use the + ;; character at the end of the window as the end of the + ;; text to be displayed, as the visual line may extend + ;; past the window. + (when (or truncate-lines beg) ; truncate-lines or hscroll. + (setq y (or y (+ (or (cdr (posn-x-y (posn-at-point))) + (throw 'hscrolled-away t)) + (window-header-line-height) + (window-tab-line-height)))) + (let* ((posn (posn-at-x-y (1- (window-width nil t)) y)) + (point (posn-point posn))) + (setq end point))) + ;; Now find the rest of the visual line. + (save-excursion + (unless beg + (beginning-of-visual-line) + (setq beg (point))) + (unless end + (end-of-visual-line) + (setq end (point)))) + ;; Obtain a substring containing the beginning of the + ;; visual line and the end. + (setq string (buffer-substring beg end)) + ;; Hack `invisible' properties within the new string. + ;; Look for each change of the property that is a variable + ;; name and replace it with its actual value according to + ;; `buffer-invisibility-spec'. + (when (listp buffer-invisibility-spec) + (let ((index 0) + (property (get-text-property 0 + 'invisible + string)) + index1 invisible) + (while index + ;; Find the end of this text property. + (setq index1 (next-single-property-change index + 'invisible + string)) + ;; Replace the property with whether or not it is + ;; non-nil. + (when property + (setq invisible nil) + (catch 'invisible + (dolist (spec buffer-invisibility-spec) + ;; Process one element of the buffer + ;; invisibility specification. + (if (consp spec) + (when (eq (cdr spec) 't) + ;; (ATOM . t) makes N invisible if N is + ;; equal to ATOM or a list containing + ;; ATOM. + (when (or (eq (car spec) property) + (and (listp spec) + (memq (car spec) invisible))) + (throw 'invisible (setq invisible t)))) + ;; Otherwise, N is invisible if SPEC is + ;; equal to N. + (when (eq spec property) + (throw 'invisible (setq invisible t)))))) + (put-text-property index (or index1 + (- end beg)) + 'invisible invisible string)) + ;; Set index to that of the next text property and + ;; continue. + (setq index index1 + property (and index1 + (get-text-property index1 + 'invisible + string)))))) + (let ((resize-mini-windows t) difference width + (message-log-max nil)) + ;; Find the offset of point from beg and display a cursor + ;; below. + (setq difference (- (point) beg) + width (string-pixel-width + (substring string 0 difference))) + (message "%s\n%s^" string + (propertize " " + 'display (list 'space + :width (list width))))) + nil))))) + (defun touch-screen-drag (event) "Handle a drag EVENT by setting the region to its new point. If `touch-screen-word-select' and EVENT lies outside the last @@ -387,15 +524,17 @@ touch-screen-drag (let* ((posn (cadr event)) ; Position of the tool. (point (posn-point posn)) ; Point of the event. ; Window where the tap originated. - (window (nth 1 touch-screen-current-tool))) + (window (nth 1 touch-screen-current-tool)) + initial-point) ;; Keep dragging. (with-selected-window window ;; Figure out what character to go to. If this posn is ;; in the window, go to (posn-point posn). If not, ;; then go to the line before either window start or ;; window end. + (setq initial-point (point)) (if (and (eq (posn-window posn) window) - point (not (eq point (point)))) + point (not (eq point initial-point))) (let* ((bounds touch-screen-word-select-bounds) (initial touch-screen-word-select-initial-word) (maybe-select-word (or (not touch-screen-word-select) @@ -464,7 +603,12 @@ touch-screen-drag (when (and (>= (point) (mark)) (> (mark) (car initial))) (set-mark (car initial)))) - (setq touch-screen-word-select-bounds nil)))) + (setq touch-screen-word-select-bounds nil))) + ;; Finally, display a preview of the line around point if + ;; requested by the user. + (when (and touch-screen-preview-select + (not (eq (point) initial-point))) + (touch-screen-preview-select))) ;; POSN is outside the window. Scroll accordingly. (let ((relative-xy (touch-screen-relative-xy posn window))) @@ -481,7 +625,11 @@ touch-screen-drag (ignore-errors (goto-char (1+ (window-end nil t))) (setq touch-screen-word-select-bounds nil)) - (redisplay))))))))) + (redisplay))) + ;; Finally, display a preview of the line now around point + ;; if requested by the user. + (when touch-screen-preview-select + (touch-screen-preview-select)))))))) (defun touch-screen-restart-drag (event) "Restart dragging to select text. commit 0ff70f12a5e29a0e90637bd063e1725f0e4e4ab2 Merge: 916ef574899 c55e67081e9 Author: Po Lu Date: Fri Jul 21 07:58:30 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 916ef5748992216710f01d91c330e52042b279f1 Author: Po Lu Date: Thu Jul 20 20:50:50 2023 +0800 Update Android port * exec/trace.c (handle_readlinkat): Adjust commentary to match behavior. * src/android.c (android_get_keysym_name): NULL terminate *name_return. diff --git a/exec/trace.c b/exec/trace.c index 23db8ebcbc7..3b384792d0a 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -899,10 +899,10 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) CALLNO is the system call number, and REGS are the current user registers of the TRACEE. - If the first argument of a `readlinkat' system call is AT_FDCWD, - and the file name specified in either a `readlink' or `readlinkat' + If the file name specified in either a `readlink' or `readlinkat' system call is `/proc/self/exe', write the name of the executable - being run into the buffer specified in the system call. + being run into the buffer specified in the system call. Do not + handle relative file names at the moment. Return the number of bytes written to the tracee's buffer in *RESULT. diff --git a/src/android.c b/src/android.c index 1662160bdd9..048773a511d 100644 --- a/src/android.c +++ b/src/android.c @@ -6151,6 +6151,7 @@ android_get_keysym_name (int keysym, char *name_return, size_t size) NULL); android_exception_check_nonnull ((void *) buffer, string); strncpy (name_return, buffer, size - 1); + name_return[size] = '\0'; (*android_java_env)->ReleaseStringUTFChars (android_java_env, (jstring) string, commit 1a9402b3c39313ed10729f9faa14e95a8682eaf0 Author: Po Lu Date: Thu Jul 20 19:58:32 2023 +0800 Update some menu definitions for Android * lisp/international/mule-cmds.el (set-coding-system-map): Don't display `set-terminal-coding-system' on Android. diff --git a/lisp/international/mule-cmds.el b/lisp/international/mule-cmds.el index 3d6d66970d3..e3811d81ec2 100644 --- a/lisp/international/mule-cmds.el +++ b/lisp/international/mule-cmds.el @@ -88,7 +88,8 @@ set-coding-system-map (bindings--define-key map [separator-3] menu-bar-separator) (bindings--define-key map [set-terminal-coding-system] '(menu-item "For Terminal" set-terminal-coding-system - :enable (null (memq initial-window-system '(x w32 ns haiku pgtk))) + :enable (null (memq initial-window-system '(x w32 ns haiku pgtk + android))) :help "How to encode terminal output")) (bindings--define-key map [set-keyboard-coding-system] '(menu-item "For Keyboard" set-keyboard-coding-system commit 9032d2178dc46e8963d0c6d8f7252b6d2cd64e08 Author: Po Lu Date: Thu Jul 20 19:52:44 2023 +0800 * lisp/cus-edit.el (custom-display): Add `android' display type. diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el index fda44ff6a3b..c4328d60093 100644 --- a/lisp/cus-edit.el +++ b/lisp/cus-edit.el @@ -3538,6 +3538,10 @@ 'custom-display :sibling-args (:help-echo "\ Haiku interface.") haiku) + (const :format "Android " + :sibling-args (:help-echo "\ +Android interface.") + android) (const :format "DOS " :sibling-args (:help-echo "\ Plain MS-DOS.") commit 7196d2d18e307ce2a07d09696bbb51f59a7d6b1b Merge: 353f90c7584 f9bbe3189b0 Author: Po Lu Date: Thu Jul 20 19:52:01 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 353f90c7584497ff6525c44d31263896f02a43d4 Author: Po Lu Date: Thu Jul 20 19:49:47 2023 +0800 Update Android port * src/android.c (struct android_event_queue): Don't make unnecessarily volatile. diff --git a/src/android.c b/src/android.c index f2e5e75d35e..1662160bdd9 100644 --- a/src/android.c +++ b/src/android.c @@ -306,7 +306,7 @@ #define ANDROID_MAX_ASSET_FD 65535 /* The number of events in the queue. If this is greater than 1024, writing will block. */ - volatile int num_events; + int num_events; /* Circular queue of events. */ struct android_event_container events; commit b5121503e4d27e1d0b18f1e115708c37437d60e9 Author: Po Lu Date: Thu Jul 20 12:48:37 2023 +0800 Update Android port * lisp/touch-screen.el (touch-screen-handle-touch): Don't restart dragging if point is at ZV and the tap falls on a different row. diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index df9a5a454fc..89dc1c61cb6 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -223,8 +223,8 @@ touch-screen-handle-scroll (scroll-right 1) (setq lines-hscrolled (1+ lines-hscrolled)) (when (not (zerop accumulator)) - ;; If there is still an outstanding amount to - ;; scroll, do this again. + ;; If there is still an outstanding amount + ;; to scroll, do this again. (throw 'again t))) (when (and (> accumulator 0) (>= accumulator column-width)) @@ -236,7 +236,8 @@ touch-screen-handle-scroll ;; scroll, do this again. (throw 'again t))))) ;; Scrolling is done. Move the accumulator back to - ;; touch-screen-current-tool and break out of the loop. + ;; touch-screen-current-tool and break out of the + ;; loop. (setcar (nthcdr 6 touch-screen-current-tool) accumulator) (setcar (nthcdr 8 touch-screen-current-tool) lines-hscrolled) nil))))) @@ -321,7 +322,8 @@ touch-screen-hold (progn ;; If so, clear the bounds and set and activate the ;; mark. - (setq touch-screen-word-select-bounds nil) + (setq touch-screen-word-select-bounds nil + touch-screen-word-select-initial-word nil) (push-mark point) (goto-char point) (activate-mark)) @@ -893,7 +895,16 @@ touch-screen-handle-touch (when (and touch-screen-extend-selection (or (eq point (point)) (eq point (mark))) - (region-active-p)) + (region-active-p) + ;; Only restart drag-to-select if the tap falls + ;; on the same row as the selection. This + ;; prevents dragging from starting if the tap + ;; is below the last window line with text and + ;; `point' is at ZV, as the user most likely + ;; meant to scroll the window instead. + (when-let* ((posn-point (posn-at-point point)) + (posn-row (cdr (posn-col-row posn-point)))) + (eq (cdr (posn-col-row position)) posn-row))) ;; Indicate that a drag is about to restart. (setcar (nthcdr 3 tool-list) 'restart-drag) ;; Generate the `restart-drag' event. commit 7e8904e796807e3b8bfc20ed45135c53d8a86f50 Author: Po Lu Date: Thu Jul 20 11:21:25 2023 +0800 Use context menu header titles on Android * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): New field `title'. (addSubmenu): New arg TITLE. Set that field. (expandTo): Set MENU's header title if it's a context menu. * src/androidmenu.c (android_init_emacs_context_menu): Adjust signature of `createContextMenu'. (android_menu_show): Use TITLE instead of pane titles if there's only one pane. diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index eb83016c849..46eddeeda3d 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -27,6 +27,7 @@ import android.os.Build; +import android.view.ContextMenu; import android.view.Menu; import android.view.MenuItem; import android.view.View; @@ -130,18 +131,27 @@ private static final class Item implements MenuItem.OnMenuItemClickListener } }; + /* List of menu items contained in this menu. */ public List menuItems; + + /* The parent context menu, or NULL if none. */ private EmacsContextMenu parent; + /* The title of this context menu, or NULL if none. */ + private String title; + + + /* Create a context menu with no items inside and the title TITLE, which may be NULL. */ public static EmacsContextMenu - createContextMenu () + createContextMenu (String title) { EmacsContextMenu menu; menu = new EmacsContextMenu (); + menu.title = title; menu.menuItems = new ArrayList (); return menu; @@ -204,7 +214,7 @@ private static final class Item implements MenuItem.OnMenuItemClickListener item.itemID = 0; item.itemName = itemName; item.tooltip = tooltip; - item.subMenu = createContextMenu (); + item.subMenu = createContextMenu (itemName); item.subMenu.parent = this; menuItems.add (item); @@ -287,12 +297,22 @@ private static final class Item implements MenuItem.OnMenuItemClickListener /* Enter the items in this context menu to MENU. Assume that MENU will be displayed in VIEW; this may lead to - popupMenu being called on VIEW if a submenu is selected. */ + popupMenu being called on VIEW if a submenu is selected. + + If MENU is a ContextMenu, set its header title to the one + contained in this object. */ public void expandTo (Menu menu, EmacsView view) { inflateMenuItems (menu, view); + + /* See if menu is a ContextMenu and a title is set. */ + if (title == null || !(menu instanceof ContextMenu)) + return; + + /* Set its title to this.title. */ + ((ContextMenu) menu).setHeaderTitle (title); } /* Return the parent or NULL. */ diff --git a/src/androidmenu.c b/src/androidmenu.c index 75710486c75..94e3f646b44 100644 --- a/src/androidmenu.c +++ b/src/androidmenu.c @@ -101,7 +101,8 @@ #define FIND_METHOD_STATIC(c_name, name, signature) \ eassert (menu_class.c_name); FIND_METHOD_STATIC (create_context_menu, "createContextMenu", - "()Lorg/gnu/emacs/EmacsContextMenu;"); + "(Ljava/lang/String;)" + "Lorg/gnu/emacs/EmacsContextMenu;"); FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ" "Ljava/lang/String;Z)V"); @@ -269,12 +270,26 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, /* Push the first local frame. */ android_push_local_frame (); + /* Set title_string to a Java string containing TITLE if non-nil. + If the menu consists of more than one pane, replace the title + with the pane header item so that the menu looks consistent. */ + + title_string = NULL; + if (STRINGP (title) && menu_items_n_panes < 2) + title_string = android_build_string (title); + /* Push the first local frame for the context menu. */ method = menu_class.create_context_menu; current_context_menu = context_menu = (*android_java_env)->CallStaticObjectMethod (android_java_env, menu_class.class, - method); + method, + title_string); + + /* Delete the unused title reference. */ + + if (title_string) + ANDROID_DELETE_LOCAL_REF (title_string); /* Push the second local frame for temporaries. */ count1 = SPECPDL_INDEX (); @@ -322,6 +337,13 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, i += 1; else if (EQ (AREF (menu_items, i), Qt)) { + /* If the menu contains a single pane, then the pane is + actually TITLE. Don't duplicate the text within the + context menu title. */ + + if (menu_items_n_panes < 2) + goto next_item; + /* This is a new pane. Switch back to the topmost context menu. */ if (current_context_menu != context_menu) @@ -348,6 +370,7 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, android_exception_check (); ANDROID_DELETE_LOCAL_REF (temp); + next_item: i += MENU_ITEMS_PANE_LENGTH; } else commit 4d3442ebad5cb1e1005cd5eca7e91c95e767ed65 Merge: 882e1d659fe e2cc16fbd0d Author: Po Lu Date: Thu Jul 20 09:23:06 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 882e1d659fec8062e96cfb614e73954840c6ecfe Author: Po Lu Date: Thu Jul 20 09:22:41 2023 +0800 Introduce a `dired-click-select' mode * doc/emacs/dired.texi (Marks vs Flags): Document command bound to `touchscreen-hold'. * doc/lispref/commands.texi (Touchscreen Events): Describe `touch-screen-inhibit-drag'. * etc/NEWS: Improve description of changes to touch screen support. * lisp/dired-aux.el (dired-do-chxxx, dired-do-chmod) (dired-do-print, dired-do-shell-command, dired-do-compress-to) (dired-do-create-files, dired-do-rename, dired-do-isearch) (dired-do-isearch-regexp, dired-do-search) (dired-do-query-replace-regexp, dired-do-find-regexp) (dired-vc-next-action): Disable ``click to select'' after running this command. * lisp/dired.el (dired-insert-set-properties): Attach click-to-select keymap to file names if necessary. (dired-mode-map): Bind `touchscreen-hold' to click to select mode. (dired-post-do-command): New function. (dired-do-delete): Call it. (dired-mark-for-click, dired-enable-click-to-select-mode): New functions. (dired-click-to-select-mode): New minor mode. * lisp/touch-screen.el (touch-screen-current-tool): Fix doc string. (touch-screen-inhibit-drag): New function. diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi index 77c4e09c826..244dd7eb525 100644 --- a/doc/emacs/dired.texi +++ b/doc/emacs/dired.texi @@ -684,6 +684,19 @@ Marks vs Flags @code{dired-undo} restores the original names in the Dired buffer, which gets the Dired buffer out of sync with the actual contents of the directory. + +@item touchscreen-hold +@kindex touchscreen-hold @r{(Dired)} +@findex dired-click-to-select-mode +@findex dired-enable-click-to-select-mode +Enter a ``click to select'' mode, where using the mouse button +@kbd{mouse-2} on a file name will cause its mark to be toggled. This +mode is useful when performing file management using a touch screen +device. + +It is enabled when a ``hold'' gesture (@pxref{Touchscreens}) is +detected over a file name, and is automatically disabled once a Dired +command operates on the marked files. @end table @node Operating on Files diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index d53e45a73de..9a7146d7eae 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2145,10 +2145,11 @@ Touchscreen Events @cindex handling touch screen events @cindex tap and drag, touch screen gestures -Emacs provides two functions to handle touch screen events independent -of gesture recognition or mouse event translation. They are intended -to be used by commands bound to @code{touchscreen-begin}, to recognize -and handle common gestures. +Several functions are provided for Lisp programs that handle touch +screen events. The intended use of the first two functions described +below is from commands bound directly to @code{touchscreen-begin} +events; they allow responding to commonly used touch screen gestures +separately from mouse event translation. @defun touch-screen-track-tap event &optional update data This function is used to track a single ``tap'' gesture originating @@ -2178,6 +2179,24 @@ Touchscreen Events drag. @end defun +In addition to those two functions, a function is provided for +commands bound to some types of events generated through mouse event +translation to prevent unwanted events from being generated after it +is called. + +@defun touch-screen-inhibit-drag +This function inhibits the generation of @code{touchscreen-drag} +events during mouse event translation for the duration of the touch +sequence being translated after it is called. It must be called from +a command which is bound to a @code{touchscreen-hold} or +@code{touchscreen-drag} event, and signals an error otherwise. + +Since this function can only be called after a gesture is already +recognized during mouse event translation, no mouse events will be +generated from touch events constituting the previously mentioned +touch sequence after it is called. +@end defun + @node Focus Events @subsection Focus Events @cindex focus event diff --git a/etc/NEWS b/etc/NEWS index c0cec91e77f..b59624e0df8 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -126,9 +126,11 @@ right-aligned to is controlled by the new user option * Editing Changes in Emacs 30.1 +++ -** Emacs now has better support for touchscreen events. -Many touch screen gestures are now implemented, as is support for -tapping buttons and opening menus. +** Emacs now has better support for touchscreen devices. +Many touch screen gestures are now implemented and translated into +mouse or gesture events, and support for tapping tool bar buttons and +opening menus has been written. Countless packages, such as Dired and +Custom have been adjusted to better understand touch screen input. --- ** On X, Emacs now supports input methods which perform "string conversion". diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el index a07406e4c0d..3e8b4c3c8fc 100644 --- a/lisp/dired-aux.el +++ b/lisp/dired-aux.el @@ -480,7 +480,8 @@ dired-do-chxxx (if failures (dired-log-summary (format "%s: error" operation) - nil)))) + nil))) + (dired-post-do-command)) ;;;###autoload (defun dired-do-chmod (&optional arg) @@ -531,7 +532,8 @@ dired-do-chmod (if num-modes num-modes (file-modes-symbolic-to-number modes (file-modes file 'nofollow))) 'nofollow)) - (dired-do-redisplay arg))) + (dired-do-redisplay arg)) + (dired-post-do-command)) ;;;###autoload (defun dired-do-chgrp (&optional arg) @@ -634,7 +636,8 @@ dired-do-print lpr-switches)) " ") 'print arg file-list))) - (dired-run-shell-command (dired-shell-stuff-it command file-list nil)))) + (dired-run-shell-command (dired-shell-stuff-it command file-list nil))) + (dired-post-do-command)) (defun dired-mark-read-string (prompt initial op-symbol arg files &optional default-value collection) @@ -918,7 +921,8 @@ dired-do-shell-command nil file-list) ;; execute the shell command (dired-run-shell-command - (dired-shell-stuff-it command file-list nil arg))))))) + (dired-shell-stuff-it command file-list nil arg)))))) + (dired-post-do-command)) ;; Might use {,} for bash or csh: (defvar dired-mark-prefix "" @@ -1547,7 +1551,8 @@ dired-do-compress-to "Compressed %d files to %s" (length in-files)) (length in-files) - (file-name-nondirectory out-file))))))) + (file-name-nondirectory out-file)))))) + (dired-post-do-command)) ;;;###autoload (defun dired-compress-file (file) @@ -2554,7 +2559,8 @@ dired-do-create-files (and (functionp dired-do-revert-buffer) (funcall dired-do-revert-buffer target))) (dired-fun-in-all-buffers (file-name-directory target) nil - #'revert-buffer)))))) + #'revert-buffer))))) + (dired-post-do-command)) ;; Read arguments for a marked-files command that wants a file name, ;; perhaps popping up the list of marked files. @@ -2887,7 +2893,8 @@ dired-do-rename (dired-get-marked-files nil arg)) (user-error "Can't rename \".\" or \"..\" files")) (dired-do-create-files 'move #'dired-rename-file - "Move" arg dired-keep-marker-rename "Rename")) + "Move" arg dired-keep-marker-rename "Rename") + (dired-post-do-command)) ;;; Operate on files matched by regexp @@ -3579,14 +3586,18 @@ dired-do-isearch "Search for a string through all marked files using Isearch." (interactive) (multi-isearch-files - (dired-get-marked-files nil nil #'dired-nondirectory-p nil t))) + (prog1 (dired-get-marked-files nil nil + #'dired-nondirectory-p nil t) + (dired-post-do-command)))) ;;;###autoload (defun dired-do-isearch-regexp () "Search for a regexp through all marked files using Isearch." (interactive) - (multi-isearch-files-regexp - (dired-get-marked-files nil nil 'dired-nondirectory-p nil t))) + (prog1 (multi-isearch-files-regexp + (dired-get-marked-files nil nil + 'dired-nondirectory-p nil t)) + (dired-post-do-command))) (declare-function fileloop-continue "fileloop" ()) @@ -3603,6 +3614,7 @@ dired-do-search regexp (dired-get-marked-files nil nil #'dired-nondirectory-p) 'default) + (dired-post-do-command) (fileloop-continue)) ;;;###autoload @@ -3626,6 +3638,7 @@ dired-do-query-replace-regexp (if (and buffer (with-current-buffer buffer buffer-read-only)) (error "File `%s' is visited read-only" file)))) + (dired-post-do-command) (fileloop-initialize-replace from to (dired-get-marked-files nil nil #'dired-nondirectory-p) (if (equal from (downcase from)) nil 'default) @@ -3675,6 +3688,7 @@ dired-do-find-regexp (user-error "No matches for: %s" regexp)) (message "Searching...done") xrefs)))) + (dired-post-do-command) (xref-show-xrefs fetcher nil))) ;;;###autoload @@ -3767,6 +3781,7 @@ dired-vc-next-action (file-name-as-directory file) file)) marked-files)))) + (dired-post-do-command) (if mark-files (let ((transient-hook (make-symbol "vc-dir-mark-files"))) (fset transient-hook diff --git a/lisp/dired.el b/lisp/dired.el index 90342069154..084ef063c4c 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -1872,6 +1872,9 @@ dired-mouse-drag-files-map keymap) "Keymap applied to file names when `dired-mouse-drag-files' is enabled.") +(defvar dired-click-to-select-mode) +(defvar dired-click-to-select-map) + (defun dired-insert-set-properties (beg end) "Add various text properties to the lines in the region, from BEG to END." (save-excursion @@ -1893,27 +1896,27 @@ dired-insert-set-properties (when (member (cl-incf i) dired-hide-details-preserved-columns) (put-text-property opoint (point) 'invisible nil)) (setq opoint (point))))) - (when (and dired-mouse-drag-files (fboundp 'x-begin-drag)) - (put-text-property (point) - (save-excursion - (dired-move-to-end-of-filename) - (backward-char) - (point)) - 'keymap - dired-mouse-drag-files-map)) - (add-text-properties - (point) - (progn - (dired-move-to-end-of-filename) - (point)) - `(mouse-face - highlight - dired-filename t - help-echo ,(if (and dired-mouse-drag-files - (fboundp 'x-begin-drag)) - "down-mouse-1: drag this file to another program + (let ((beg (point)) (end (save-excursion + (dired-move-to-end-of-filename) + (1- (point))))) + (if dired-click-to-select-mode + (put-text-property beg end 'keymap + dired-click-to-select-map) + (when (and dired-mouse-drag-files (fboundp 'x-begin-drag)) + (put-text-property beg end 'keymap + dired-mouse-drag-files-map))) + (add-text-properties + beg (1+ end) + `(mouse-face + highlight + dired-filename t + help-echo ,(if dired-click-to-select-mode + "mouse-2: mark or unmark this file" + (if (and dired-mouse-drag-files + (fboundp 'x-begin-drag)) + "down-mouse-1: drag this file to another program mouse-2: visit this file in other window" - "mouse-2: visit this file in other window"))) + "mouse-2: visit this file in other window"))))) (when (< (+ (point) 4) (line-end-position)) (put-text-property (+ (point) 4) (line-end-position) 'invisible 'dired-hide-details-link)))) @@ -2287,7 +2290,9 @@ dired-mode-map ": d" #'epa-dired-do-decrypt ": v" #'epa-dired-do-verify ": s" #'epa-dired-do-sign - ": e" #'epa-dired-do-encrypt) + ": e" #'epa-dired-do-encrypt + ;; Click-to-select. + "" #'dired-enable-click-to-select-mode) (put 'dired-find-file :advertised-binding (kbd "RET")) @@ -3700,6 +3705,11 @@ dired-do-flagged-delete (or nomessage (message "(No deletions requested)"))))) +(defun dired-post-do-command () + "Disable `dired-click-to-select-mode' after an operation." + (when dired-click-to-select-mode + (dired-click-to-select-mode -1))) + (defun dired-do-delete (&optional arg) "Delete all marked (or next ARG) files. `dired-recursive-deletes' controls whether deletion of @@ -3717,7 +3727,8 @@ dired-do-delete m)) arg)) arg t) - (dolist (m markers) (set-marker m nil)))) + (dolist (m markers) (set-marker m nil))) + (dired-post-do-command)) (defvar dired-deletion-confirmer 'yes-or-no-p) ; or y-or-n-p? @@ -4938,6 +4949,97 @@ dired-do-eww (interactive nil dired-mode) (eww-open-file (dired-get-file-for-visit))) + +;;; Click-To-Select mode + +(defvar dired-click-to-select-map (make-sparse-keymap) + "Keymap placed on files under `dired-click-to-select' mode.") + +(define-key dired-click-to-select-map [mouse-2] + #'dired-mark-for-click) + +(defun dired-mark-for-click (event) + "Mark or unmark the file underneath the mouse click at EVENT. +See `dired-click-to-select-mode' for more details." + (interactive "e") + (let ((posn (event-start event)) + (inhibit-read-only t)) + (with-selected-window (posn-window posn) + (goto-char (posn-point posn)) + (save-excursion + (dired-repeat-over-lines + 1 (lambda () + (let ((char (char-after))) + (when (or (not (looking-at-p dired-re-dot)) + (not (equal dired-marker-char dired-del-marker))) + (delete-char 1) + (insert (if (eq char dired-marker-char) + ;; Insert a space to unmark the file if + ;; it's already marked. + ?\s + ;; Otherwise mark the file. + dired-marker-char)))))))))) + +(defun dired-enable-click-to-select-mode (event) + "Enable `dired-click-to-select-mode' and mark the file under EVENT. +If there is no file under EVENT, call `touch-screen-hold' with +EVENT instead." + (interactive "e") + (let* ((posn (event-start event)) + (window (posn-window posn)) + (point (posn-point posn))) + (if (and window point + (get-text-property point 'dired-filename + (window-buffer window))) + (progn (beep) + (touch-screen-inhibit-drag) + (with-selected-window window + (goto-char point) + (save-excursion (dired-mark 1)) + (dired-click-to-select-mode 1))) + (touch-screen-hold event)))) + +(define-minor-mode dired-click-to-select-mode + "Toggle click-to-select inside this Dired buffer. +When this minor mode is enabled, using `mouse-2' on a file name +within a Dired buffer will toggle its mark instead of going to it +within another window. + +Disabling this minor mode will unmark all files within the Dired +buffer. + +`dired-click-to-select-mode' is automatically disabled after any +Dired operation (command whose name starts with `dired-do') +completes." + :group 'dired + :lighter " Click-To-Select" + (unless (derived-mode-p 'dired-mode 'wdired-mode) + (error "Not a Dired buffer")) + (if dired-click-to-select-mode + (setq-local tool-bar-map + `(keymap (exit-click-to-select menu-item + "Exit Click To Select Mode" + dired-click-to-select-mode + :help "Exit `dired-click-to-select-mode'." + :image ,(tool-bar--image-expression "close") + :enable t))) + ;; Reset the default tool bar. + (kill-local-variable 'tool-bar-map) + (dired-unmark-all-marks)) + ;; Repropertize this Dired buffer. + (let ((inhibit-read-only t)) + (remove-text-properties (point-min) (point-max) + '(invisible nil + keymap nil + dired-filename nil + help-echo nil + mouse-face nil)) + (when dired-make-directory-clickable + (dired--make-directory-clickable)) + (dired-insert-set-properties (point-min) (point-max))) + ;; Redisplay the tool bar. + (force-mode-line-update)) + (provide 'dired) (run-hooks 'dired-load-hook) ; for your customizations diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 19b3d3b2cf4..df9a5a454fc 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -23,11 +23,11 @@ ;;; Commentary: ;; This file provides code to recognize simple touch screen gestures. -;; It is used on X and Android, where the platform cannot recognize -;; them for us. +;; It is used on X and Android, currently the only systems where Emacs +;; supports touch input. ;; -;; See (elisp)Touchscreen Events for a description of the details of touch -;; events. +;; See (elisp)Touchscreen Events for a description of the details of +;; touch events. ;;; Code: @@ -39,8 +39,9 @@ touch-screen-current-tool to that window, a field used to store data while tracking the touch point, the initial position of the touchpoint, and another four fields to used store data while tracking the touch point. -See `touch-screen-handle-point-update' for the meanings of the -fourth element.") +See `touch-screen-handle-point-update' and +`touch-screen-handle-point-up' for the meanings of the fifth +element.") (defvar touch-screen-set-point-commands '(mouse-set-point) "List of commands known to set the point. @@ -1211,6 +1212,26 @@ touch-screen-track-drag +;;; Event handling exports. These functions are intended for use by +;;; Lisp commands bound to touch screen gesture events. + +(defun touch-screen-inhibit-drag () + "Inhibit subsequent `touchscreen-drag' events from being sent. +Prevent `touchscreen-drag' and translated mouse events from being +sent until the touch sequence currently being translated ends. +Must be called from a command bound to a `touchscreen-hold' or +`touchscreen-drag' event." + (let* ((tool touch-screen-current-tool) + (current-what (nth 4 tool))) + ;; Signal an error if no hold or drag is in progress. + (when (and (not (eq current-what 'hold) + (eq current-what 'drag))) + (error "Calling `touch-screen-inhibit-drag' outside hold or drag")) + ;; Now set the fourth element of tool to `command-inhibit'. + (setcar (nthcdr 3 tool) 'command-inhibit))) + + + (provide 'touch-screen) ;;; touch-screen ends here commit 1eb24c6c3812833864dce42a09ac497db1946758 Author: Po Lu Date: Wed Jul 19 15:00:39 2023 +0800 ; * src/sfnt.c (sfnt_infer_deltas): Improve commentary. diff --git a/src/sfnt.c b/src/sfnt.c index dc190aa950b..10321a09c8b 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -14210,8 +14210,9 @@ sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start, - For each point that lies between the first point and the last on the axis currently being considered, interpolate its - position in that axis so that the ratio between the first - point and the last in the original outline still holds. + position in that axis so that the ratio formed by its position + relative to the first and last points of the pair in the + original outline still holds. - For each point that lies to the left or top of the first point on the axis being considered, use the delta of the first point. @@ -14220,9 +14221,9 @@ sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start, the last point on that axis, use the delta of the last point. - X and Y contain the original positions positions of each point. - TOUCHED contains whether or not each point has been changed by - an explicitly specified delta. + X and Y contain the original positions of each point. + TOUCHED contains whether or not each point within GLYPH has been + changed through variation. Apply the inferred deltas back to GLYPH. */ commit b3054d69d88b9977052e889ed9134db4d69b8e97 Author: Po Lu Date: Wed Jul 19 14:42:02 2023 +0800 Improve behavior of `restart-drag' * lisp/touch-screen.el (touch-screen-handle-point-up): If what is `restart-drag' (meaning that a drag has been restarted but the touchpoint has not moved), translate the release into a regular mouse click to deactivate the region. diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index a9815dfc623..19b3d3b2cf4 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -707,11 +707,11 @@ touch-screen-handle-point-up PREFIX should be a virtual function key used to look up key bindings. -If the fourth element of `touch-screen-current-tool' is nil, move -point to the position of POINT, selecting the window under POINT -as well, and deactivate the mark; if there is a button or link at -POINT, call the command bound to `mouse-2' there. Otherwise, -call the command bound to `mouse-1'. +If the fourth element of `touch-screen-current-tool' is nil or +`restart-drag', move point to the position of POINT, selecting +the window under POINT as well, and deactivate the mark; if there +is a button or link at POINT, call the command bound to `mouse-2' +there. Otherwise, call the command bound to `mouse-1'. If the fourth element of `touch-screen-current-tool' is `mouse-drag', then generate either a `mouse-1' or a @@ -729,7 +729,11 @@ touch-screen-handle-point-up is not read-only." (let ((what (nth 3 touch-screen-current-tool)) (posn (cdr point)) window point) - (cond ((null what) + (cond ((or (null what) + ;; If dragging has been restarted but the touch point + ;; hasn't been moved, translate the sequence into a + ;; regular mouse click. + (eq what 'restart-drag)) (when (windowp (posn-window posn)) (setq point (posn-point posn) window (posn-window posn)) commit db48c88e7ec4608e16255550c1141386c51663c4 Author: Po Lu Date: Wed Jul 19 12:46:02 2023 +0800 Update Android port * build-aux/makecounter.sh (curcount): Rename `counter' to `emacs_shortlisp_counter'. * doc/emacs/input.texi (Touchscreens): Document `touch-screen-extend-selection'. * doc/lispref/commands.texi (Touchscreen Events): Document `touchscreen-restart-drag'. * lisp/touch-screen.el (touch-screen-extend-selection): New user option. (touch-screen-restart-drag): New function. (touch-screen-handle-point-update): Handle `restart-drag' gestures. (touch-screen-handle-touch): Check if the prerequisites for extending a previous drag gesture are met, and generate such events if so. (touch-screen-translate-touch): Update doc string. * src/Makefile.in (otherobj): Remove BUILD_COUNTER_OBJ. ($(lispsource)/international/charprop.el): (%.elc): Don't depend on bootstrap-emacs if cross configuring for Android. (libemacs.so): Directly depend on and link with BUILD_COUNTER_OBJ. diff --git a/build-aux/makecounter.sh b/build-aux/makecounter.sh index 13ad5f485a2..3bebd288031 100755 --- a/build-aux/makecounter.sh +++ b/build-aux/makecounter.sh @@ -39,5 +39,5 @@ curcount= #ifdef EXPORT EXPORT #endif /* EXPORT */ -int counter = $curcount; +int emacs_shortlisp_counter = $curcount; EOF diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi index eccb3e5e243..f5b0d0570e1 100644 --- a/doc/emacs/input.texi +++ b/doc/emacs/input.texi @@ -68,6 +68,15 @@ Touchscreens option @code{touch-screen-word-select} enables ``word selection mode'', causing dragging to select the complete word, not only the character containing the position of the tool. + +@vindex touch-screen-extend-selection +@cindex extending the selection, touchscreens + Similarly, it may be difficult to select all of the text intended +within a single gesture. If the user option +@code{touch-screen-extend-selection} is enabled, taps on the locations +of the point or the mark within a window will begin a new ``drag'' +gesture, where the region will be extended in the direction of any +subsequent movement. @end itemize @vindex touch-screen-delay diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 55fecdce2d7..d53e45a73de 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2129,9 +2129,18 @@ Touchscreen Events @cindex @code{touchscreen-drag} event @item (touchscreen-drag @var{posn}) If a ``long-press'' gesture is detected while translating the current -touch sequence, a @code{touchscreen-drag} event is sent upon each -subsequent @code{touchscreen-update} event, with @var{posn} set to the -new position of the touchpoint. +touch sequence or ``drag-to-select'' is being resumed as a result of +the @code{touch-screen-extend-selection} user option, a +@code{touchscreen-drag} event is sent upon each subsequent +@code{touchscreen-update} event with @var{posn} set to the new +position of the touchpoint. + +@cindex @code{touchscreen-restart-drag} event +@item (touchscreen-restart-drag @var{posn}) +This event is sent upon the start of a touch sequence resulting in the +continuation of a ``drag-to-select'' gesture (subject to the +aformentioned user option) with @var{posn} set to the position list of +the initial @code{touchscreen-begin} event within that touch sequence. @end table @cindex handling touch screen events diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 4c43ec4b63f..a9815dfc623 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -86,6 +86,15 @@ touch-screen-word-select :group 'mouse :version "30.1") +(defcustom touch-screen-extend-selection nil + "If non-nil, restart drag-to-select upon a tap on point or mark. +When enabled, tapping on the character containing the point or +mark will resume dragging where it left off while the region is +active." + :type 'boolean + :group 'mouse + :version "30.1") + (defvar-local touch-screen-word-select-bounds nil "The start and end positions of the word last selected. Normally a cons of those two positions or nil if no word was @@ -471,8 +480,35 @@ touch-screen-drag (setq touch-screen-word-select-bounds nil)) (redisplay))))))))) +(defun touch-screen-restart-drag (event) + "Restart dragging to select text. +Set point to the location of EVENT within its window while +keeping the bounds of the region intact, and set up state for +`touch-screen-drag'." + (interactive "e") + (let* ((posn (event-start event)) + (window (posn-window posn)) + (point (posn-point posn))) + (with-selected-window window + (let ((current-point (point)) + (current-mark (mark))) + ;; Ensure that mark and point haven't changed since EVENT was + ;; generated, and the region is still active. + (when (or (eq point current-point) + (eq point current-mark) + (region-active-p)) + (when (eq point current-mark) + ;; Exchange point and mark. + (exchange-point-and-mark)) + ;; Clear the state necessary to set up dragging. Don't try + ;; to select entire words immediately after dragging starts, + ;; to allow for fine grained selection inside a word. + (setq touch-screen-word-select-bounds nil + touch-screen-word-select-initial-word nil)))))) + (global-set-key [touchscreen-hold] #'touch-screen-hold) (global-set-key [touchscreen-drag] #'touch-screen-drag) +(global-set-key [touchscreen-restart-drag] #'touch-screen-restart-drag) @@ -541,8 +577,12 @@ touch-screen-handle-point-update If the fourth element of `touch-screen-current-tool' is `held', then the touch has been held down for some time. If motion -happens, cancel `touch-screen-current-timer', and set the field -to `drag'. Then, generate a `touchscreen-drag' event. +happens, set the field to `drag'. Then, generate a +`touchscreen-drag' event. + +If the fourth element of `touch-screen-current-tool' is +`restart-drag', set the field to `drag' and generate a +`touchscreen-drag'. If the fourth element of `touch-screen-current-tool' is `drag', then move point to the position of POINT." @@ -628,6 +668,15 @@ touch-screen-handle-point-update ;; `touchscreen-hold' was generated when the timeout ;; fired. (throw 'input-event (list 'touchscreen-drag posn)))) + ((eq what 'restart-drag) + (let* ((posn (cdr point))) + ;; Now start dragging. + (setcar (nthcdr 3 touch-screen-current-tool) + 'drag) + ;; Generate a (touchscreen-drag POSN) event. + ;; `touchscreen-restart-drag' was generated when the + ;; timeout fired. + (throw 'input-event (list 'touchscreen-drag posn)))) ((eq what 'drag) (let* ((posn (cdr point))) ;; Generate a (touchscreen-drag POSN) event. @@ -809,7 +858,7 @@ touch-screen-handle-touch (position (cdadr event)) (window (posn-window position)) (point (posn-point position)) - binding) + binding tool-list) ;; Cancel the touch screen timer, if it is still there by any ;; chance. (when touch-screen-current-timer @@ -817,24 +866,46 @@ touch-screen-handle-touch (setq touch-screen-current-timer nil)) ;; Replace any previously ongoing gesture. If POSITION has no ;; window or position, make it nil instead. - (setq touch-screen-current-tool (and (windowp window) - (list touchpoint window - (posn-x-y position) - nil position - nil nil nil nil))) - ;; Determine if there is a command bound to `down-mouse-1' at - ;; the position of the tap and that command is not a command - ;; whose functionality is replaced by the long-press - ;; mechanism. If so, set the fourth element of - ;; `touch-screen-current-tool' to `mouse-drag' and generate an - ;; emulated `mouse-1' event. - ;; - ;; If the command in question is a keymap, use `mouse-1-menu' - ;; instead of `mouse-drag', and don't generate a - ;; `down-mouse-1' event immediately. Instead, wait for the - ;; touch point to be released. - (if (and touch-screen-current-tool - (with-selected-window window + (setq tool-list (and (windowp window) + (list touchpoint window + (posn-x-y position) + nil position + nil nil nil nil)) + touch-screen-current-tool tool-list) + + ;; Select the window underneath the event as the checks below + ;; will look up keymaps and markers inside its buffer. + (save-selected-window + ;; Check if `touch-screen-extend-selection' is enabled, the + ;; tap lies on the point or the mark, and the region is + ;; active. If that's the case, set the fourth element of + ;; `touch-screen-current-tool' to `restart-drag', then + ;; generate a `touchscreen-restart-drag' event. + (when tool-list + ;; tool-list is always non-nil where the selected window + ;; matters. + (select-window window) + (when (and touch-screen-extend-selection + (or (eq point (point)) + (eq point (mark))) + (region-active-p)) + ;; Indicate that a drag is about to restart. + (setcar (nthcdr 3 tool-list) 'restart-drag) + ;; Generate the `restart-drag' event. + (throw 'input-event (list 'touchscreen-restart-drag + position)))) + ;; Determine if there is a command bound to `down-mouse-1' + ;; at the position of the tap and that command is not a + ;; command whose functionality is replaced by the long-press + ;; mechanism. If so, set the fourth element of + ;; `touch-screen-current-tool' to `mouse-drag' and generate + ;; an emulated `mouse-1' event. + ;; + ;; If the command in question is a keymap, set that element + ;; to `mouse-1-menu' instead of `mouse-drag', and don't + ;; generate a `down-mouse-1' event immediately. Instead, + ;; wait for the touch point to be released. + (if (and tool-list (and (setq binding (key-binding (if prefix (vector prefix @@ -842,24 +913,23 @@ touch-screen-handle-touch [down-mouse-1]) t nil position)) (not (and (symbolp binding) - (get binding 'ignored-mouse-command)))))) - (if (or (keymapp binding) - (and (symbolp binding) - (get binding 'mouse-1-menu-command))) - ;; binding is a keymap, or a command that does almost - ;; the same thing. If a `mouse-1' event is generated - ;; after the keyboard command loop displays it as a - ;; menu, that event could cause unwanted commands to - ;; be run. Set what to `mouse-1-menu' instead and - ;; wait for the up event to display the menu. - (setcar (nthcdr 3 touch-screen-current-tool) - 'mouse-1-menu) - (progn (setcar (nthcdr 3 touch-screen-current-tool) - 'mouse-drag) - (throw 'input-event (list 'down-mouse-1 position)))) - (and point - ;; Start the long-press timer. - (touch-screen-handle-timeout nil))))) + (get binding 'ignored-mouse-command))))) + (if (or (keymapp binding) + (and (symbolp binding) + (get binding 'mouse-1-menu-command))) + ;; binding is a keymap, or a command that does + ;; almost the same thing. If a `mouse-1' event is + ;; generated after the keyboard command loop + ;; displays it as a menu, that event could cause + ;; unwanted commands to be run. Set what to + ;; `mouse-1-menu' instead and wait for the up event + ;; to display the menu. + (setcar (nthcdr 3 tool-list) 'mouse-1-menu) + (progn (setcar (nthcdr 3 tool-list) 'mouse-drag) + (throw 'input-event (list 'down-mouse-1 position)))) + (and point + ;; Start the long-press timer. + (touch-screen-handle-timeout nil)))))) ((eq (car event) 'touchscreen-update) (unless touch-screen-current-tool ;; If a stray touchscreen-update event arrives (most likely @@ -930,6 +1000,10 @@ touch-screen-translate-touch where POSN is the position of the long-press or touchpoint motion, + (touchscreen-restart-drag POSN) + +where POSN is the position of the tap, + (down-mouse-1 POSN) (drag-mouse-1 POSN) diff --git a/src/Makefile.in b/src/Makefile.in index 36f3876f659..e818b6c1655 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -514,22 +514,9 @@ PRE_ALLOC_OBJ= ## lastfile.o on Cygwin and MinGW, empty elsewhere. POST_ALLOC_OBJ=@POST_ALLOC_OBJ@ -## Builds using libemacs.so (Android) don't dump Emacs within this -## Makefile, but on device. Make sure the library hash changes for -## each change in shortlisp by linking an object that changes -## accordingly to it. -BUILD_COUNTER_OBJ = -ifeq ($(XCONFIGURE),android) -BUILD_COUNTER_OBJ = build-counter.o - -# This file is then compiled into build-counter.so -build-counter.c: $(abs_top_builddir)/src/lisp.mk $(shortlisp) - $(AM_V_GEN) $(top_srcdir)/build-aux/makecounter.sh $@ -endif - ## List of object files that make-docfile should not be told about. otherobj= $(TERMCAP_OBJ) $(PRE_ALLOC_OBJ) $(GMALLOC_OBJ) $(RALLOC_OBJ) \ - $(POST_ALLOC_OBJ) $(WIDGET_OBJ) $(LIBOBJS) $(BUILD_COUNTER_OBJ) + $(POST_ALLOC_OBJ) $(WIDGET_OBJ) $(LIBOBJS) ## All object files linked into temacs. $(VMLIMIT_OBJ) should be first. ## (On MinGW, firstfile.o should be before vm-limit.o.) @@ -625,9 +612,11 @@ LIBES = ## up-to-date. Although since charprop depends on bootstrap-emacs, ## and emacs depends on charprop, in practice this rule was always run ## anyway. +ifneq ($(XCONFIGURE),android) $(lispsource)/international/charprop.el: \ FORCE | bootstrap-emacs$(EXEEXT) $(bootstrap_pdmp) $(MAKE) -C ../admin/unidata all EMACS="../$(bootstrap_exe)" +endif ## We require charprop.el to exist before ucs-normalize.el is ## byte-compiled, because ucs-normalize.el needs to load 2 uni-*.el files. @@ -782,12 +771,22 @@ top_builddir := include $(old_top_builddir)/ndk-build/ndk-build.mk top_builddir := $(old_top_builddir) -libemacs.so: $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(EMACSRES) \ - $(MAKE_PDUMPER_FINGERPRINT) $(NDK_BUILD_SHARED) \ - $(NDK_BUILD_STATIC) $(etc)/DOC +## Builds using libemacs.so (Android) don't dump Emacs within this +## Makefile, but on device. Make sure the library hash changes for +## each change in shortlisp by linking an object that changes +## accordingly to it. +BUILD_COUNTER_OBJ = build-counter.o + +# This file is then compiled into libemacs.so +build-counter.c: $(abs_top_builddir)/src/lisp.mk $(lisp) + $(AM_V_GEN) $(top_srcdir)/build-aux/makecounter.sh $@ + +libemacs.so: $(ALLOBJS) $(BUILD_COUNTER_OBJ) $(LIBEGNU_ARCHIVE) \ + $(EMACSRES) $(MAKE_PDUMPER_FINGERPRINT) \ + $(NDK_BUILD_SHARED) $(NDK_BUILD_STATIC) $(etc)/DOC $(AM_V_CCLD)$(CC) -o $@ $(ALL_CFLAGS) $(TEMACS_LDFLAGS) \ $(ANDROID_LDFLAGS) $(LDFLAGS) -shared $(ALLOBJS) \ - $(LIBEGNU_ARCHIVE) $(LIBES) + $(BUILD_COUNTER_OBJ) $(LIBEGNU_ARCHIVE) $(LIBES) $(AM_V_at)$(MAKE_PDUMPER_FINGERPRINT) $@ # There is also a binary named `android-emacs' which simply calls @@ -918,10 +917,16 @@ .PHONY: ## To solve the freshness issue, in the past we tried various clever tricks, ## but now that we require GNU make, we can simply specify ## bootstrap-emacs$(EXEEXT) as an order-only prerequisite. +## +## bootstrap-emacs doesn't have to be built when cross-compiling +## libemacs.so for Android, however, as the Lisp files have already +## been compiled by the top level `src' Makefile. +ifneq ($(XCONFIGURE),android) %.elc: %.el | bootstrap-emacs$(EXEEXT) $(bootstrap_pdmp) @$(MAKE) $(AM_V_NO_PD) -C ../lisp EMACS="$(bootstrap_exe)"\ THEFILE=$< $ Date: Wed Jul 19 09:53:58 2023 +0800 Make sure Android builds are redumped upon changes to shortlisp * build-aux/makecounter.sh: New script. * src/Makefile.in (abs_top_builddir): New variable. (BUILD_COUNTER_OBJ): Define to build-counter.o if compiling for Android. (build-counter.c): New target. Generate this file using makecounter.sh upon changes to lisp.mk or shortlisp. (lisp.mk): Make and load relative to abs_top_builddir. (emacs$(EXEEXT)): Adjust acordingly. (mostlyclean): Remove build-counter.c. diff --git a/build-aux/makecounter.sh b/build-aux/makecounter.sh new file mode 100755 index 00000000000..13ad5f485a2 --- /dev/null +++ b/build-aux/makecounter.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# Generate or update a C file containing an increasing counter +# variable. +# +# Copyright (C) 2023 Free Software Foundation, Inc. +# +# This file is part of GNU Emacs. GNU Emacs 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 3 of the License, or (at your option) any later version. +# +# GNU Emacs 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 GNU Emacs. If not, see . + +set -e + +curcount= +if test -f "$1"; then + curcount=`cat "$1" | grep = | cut -d= -f2 \ + | sed -e 's/;//' -e 's/ //'` +fi + +curcount=`expr 1 + $curcount 2>/dev/null || echo 0` + +cat > $1 < + +#ifdef HAVE_ANDROID +#define EXPORT __attribute__ ((visibility ("default"))) +#endif /* HAVE_ANDROID */ + +#ifdef EXPORT +EXPORT +#endif /* EXPORT */ +int counter = $curcount; +EOF diff --git a/src/Makefile.in b/src/Makefile.in index 8cbdd67378c..36f3876f659 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -43,6 +43,10 @@ vpath %.c : vpath %.h := $(srcdir) endif +# abs_top_builddir is the name of the toplevel build directory under +# cross-compiled builds. +abs_top_builddir = @abs_top_builddir@ + CC = @CC@ CXX = @CXX@ CFLAGS = @CFLAGS@ @@ -510,9 +514,22 @@ PRE_ALLOC_OBJ= ## lastfile.o on Cygwin and MinGW, empty elsewhere. POST_ALLOC_OBJ=@POST_ALLOC_OBJ@ +## Builds using libemacs.so (Android) don't dump Emacs within this +## Makefile, but on device. Make sure the library hash changes for +## each change in shortlisp by linking an object that changes +## accordingly to it. +BUILD_COUNTER_OBJ = +ifeq ($(XCONFIGURE),android) +BUILD_COUNTER_OBJ = build-counter.o + +# This file is then compiled into build-counter.so +build-counter.c: $(abs_top_builddir)/src/lisp.mk $(shortlisp) + $(AM_V_GEN) $(top_srcdir)/build-aux/makecounter.sh $@ +endif + ## List of object files that make-docfile should not be told about. otherobj= $(TERMCAP_OBJ) $(PRE_ALLOC_OBJ) $(GMALLOC_OBJ) $(RALLOC_OBJ) \ - $(POST_ALLOC_OBJ) $(WIDGET_OBJ) $(LIBOBJS) + $(POST_ALLOC_OBJ) $(WIDGET_OBJ) $(LIBOBJS) $(BUILD_COUNTER_OBJ) ## All object files linked into temacs. $(VMLIMIT_OBJ) should be first. ## (On MinGW, firstfile.o should be before vm-limit.o.) @@ -565,15 +582,19 @@ DEPDIR = ## we need to remove leim-list, site-init, and site-load by hand. ## There's not much to choose between these two approaches, ## but the second one seems like it could be more future-proof. +## +## This list is placed in the toplevel build directory to prevent it +## from being unnecessarily regenerated by the successive use of this +## Makefile within cross/Makefile. shortlisp = -lisp.mk: $(lispsource)/loadup.el +$(abs_top_builddir)/src/lisp.mk: $(lispsource)/loadup.el ${AM_V_GEN}( printf 'shortlisp = \\\n'; \ sed -n 's/^[ \t]*(load "\([^"]*\)".*/\1/p' $< | \ sed -e 's/$$/.elc \\/' -e 's/\.el\.elc/.el/'; \ echo "" ) > $@.tmp $(AM_V_at)mv -f $@.tmp $@ --include lisp.mk +-include $(abs_top_builddir)/src/lisp.mk shortlisp_filter = leim/leim-list.el site-load.elc site-init.elc shortlisp := $(filter-out ${shortlisp_filter},${shortlisp}) ## Place loaddefs.el first, so it gets generated first, since it is on @@ -640,7 +661,7 @@ SYSTEM_TYPE = ## since not all pieces are used on all platforms. But DOC depends ## on all of $lisp, and emacs depends on DOC, so it is ok to use $lisp here. emacs$(EXEEXT): temacs$(EXEEXT) \ - lisp.mk $(etc)/DOC $(lisp) \ + $(abs_top_builddir)/src/lisp.mk $(etc)/DOC $(lisp) \ $(lispsource)/international/charprop.el ${charsets} ifeq ($(SYSTEM_TYPE),cygwin) find ${top_builddir} -name '*.eln' | rebase -v -O -T - @@ -808,7 +829,7 @@ .PHONY: mostlyclean: rm -f android-emacs libemacs.so - rm -f temacs$(EXEEXT) core ./*.core \#* ./*.o + rm -f temacs$(EXEEXT) core ./*.core \#* ./*.o build-counter.c rm -f dmpstruct.h rm -f emacs.pdmp rm -f ../etc/DOC commit 5a3de76532056e03b674b1cb7d4e0d9e15a2a971 Merge: a177e8fd189 a4087f59558 Author: Po Lu Date: Wed Jul 19 08:23:06 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit a177e8fd189d52a1275df1bf1f3aff5a7bed78bb Merge: f13c0464ccf dec15620d8c Author: Po Lu Date: Wed Jul 19 07:54:08 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit f13c0464ccf3ab08db3cfd719fd86319dc1f3f95 Author: Po Lu Date: Tue Jul 18 22:01:17 2023 +0800 Fix typos in touch-screen.el * lisp/touch-screen.el (touch-screen-handle-point-update) (touch-screen-handle-point-up): Fix typos. diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index ad27c28d5f9..4c43ec4b63f 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -573,7 +573,7 @@ touch-screen-handle-point-update (> diff-x (frame-char-width))) (< diff-y -10) (and diff-x-eligible - (< diff-x (frame-char-width)))) + (< diff-x (- (frame-char-width))))) (setcar (nthcdr 3 touch-screen-current-tool) 'scroll) (setcar (nthcdr 2 touch-screen-current-tool) @@ -686,8 +686,9 @@ touch-screen-handle-point-up window (posn-window posn)) ;; Select the window that was tapped given that it isn't ;; an inactive minibuffer window. - (when (or (not (eq window) - (minibuffer-window (window-frame window))) + (when (or (not (eq window + (minibuffer-window + (window-frame window)))) (minibuffer-window-active-p window)) (select-window window)) ;; Now simulate a mouse click there. If there is a link commit a636a6607084944d675e0ecc32203f9546ab5120 Merge: ef497028bd7 607f826edd4 Author: Po Lu Date: Tue Jul 18 21:26:56 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit ef497028bd7ede3ab27843fae6d3fbe12cb8e9e2 Merge: cf50cd99ce7 c4f8ead08d1 Author: Po Lu Date: Tue Jul 18 16:17:28 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit cf50cd99ce7a17e6ee400b0a6d58981064785c9e Author: Po Lu Date: Tue Jul 18 15:46:07 2023 +0800 * lisp/touch-screen.el (touch-screen-handle-point-update): Fix typo. diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index cb9bfa06b7d..ad27c28d5f9 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -556,7 +556,7 @@ touch-screen-handle-point-update ;; WINDOW. (relative-xy (touch-screen-relative-xy posn window)) - (col (and (posn-area original-posn) + (col (and (not (posn-area original-posn)) (car (posn-col-row original-posn (posn-window posn))))) ;; Don't start horizontal scrolling if the touch @@ -564,8 +564,8 @@ touch-screen-handle-point-update ;; edges, as systems like Android use those two ;; columns to implement gesture navigation. (diff-x-eligible - (and col (> (car col) 2) - (< (car col) (- (window-width window) 2)))) + (and col (> col 2) + (< col (- (window-width window) 2)))) (diff-x (- (car last-posn) (car relative-xy))) (diff-y (- (cdr last-posn) (cdr relative-xy)))) (when (or (> diff-y 10) commit 7cd11a71fa8d02b9f7115d76b440905b778c398b Author: Po Lu Date: Tue Jul 18 13:24:55 2023 +0800 Avoid splurious menu-bar nil events * src/keyboard.c (make_lispy_event): Return nil if no menu item is found. diff --git a/src/keyboard.c b/src/keyboard.c index a65d706ee2d..97172be8152 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -6222,6 +6222,11 @@ make_lispy_event (struct input_event *event) } } + /* Don't generate a menu bar event if ITEM is + nil. */ + if (NILP (item)) + return Qnil; + /* ELisp manual 2.4b says (x y) are window relative but code says they are frame-relative. */ @@ -6566,6 +6571,10 @@ make_lispy_event (struct input_event *event) #endif /* HAVE_WINDOW_SYSTEM */ f = XFRAME (event->frame_or_window); + + if (!FRAME_LIVE_P (f)) + return Qnil; + id = event->arg; x = event->x; y = event->y; @@ -6641,6 +6650,9 @@ make_lispy_event (struct input_event *event) bool close; #endif /* HAVE_WINDOW_SYSTEM */ + if (!FRAME_LIVE_P (f)) + return Qnil; + id = event->arg; x = event->x; y = event->y; @@ -6680,6 +6692,11 @@ make_lispy_event (struct input_event *event) } } + /* Don't generate a menu bar event if ITEM is + nil. */ + if (NILP (item)) + return Qnil; + /* ELisp manual 2.4b says (x y) are window relative but code says they are frame-relative. */ @@ -6769,6 +6786,9 @@ make_lispy_event (struct input_event *event) struct frame *f = XFRAME (event->frame_or_window); evt = Qnil; + if (!FRAME_LIVE_P (f)) + return Qnil; + for (tem = event->arg; CONSP (tem); tem = XCDR (tem)) { it = XCAR (tem); @@ -6777,10 +6797,19 @@ make_lispy_event (struct input_event *event) y = XCAR (XCDR (it)); id = XCAR (XCDR (XCDR (it))); + /* Don't report touches to the menu bar. */ + if (EQ (id, menu_bar_touch_id)) + continue; + position = make_lispy_position (f, x, y, event->timestamp); evt = Fcons (Fcons (id, position), evt); } + if (NILP (evt)) + /* Don't return an event if the touchpoint list is + empty. */ + return Qnil; + return list2 (Qtouchscreen_update, evt); } commit eaf13bc739c37ad0eff12c95e1cba44ead47e102 Author: Po Lu Date: Tue Jul 18 13:24:36 2023 +0800 Update Android port * lisp/touch-screen.el (touch-screen-hold) (touch-screen-handle-point-up): Don't select inactive minibuffer windows. (touch-screen-handle-point-update): Improve detection of left and right edges. diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 5606f752042..cb9bfa06b7d 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -262,10 +262,17 @@ touch-screen-hold word around EVENT; otherwise, set point to the location of EVENT." (interactive "e") (let* ((posn (cadr event)) - (point (posn-point posn))) - (when point + (point (posn-point posn)) + (window (posn-window posn))) + (when (and point + ;; Make sure WINDOW is not an inactive minibuffer + ;; window. + (or (not (eq window + (minibuffer-window + (window-frame window)))) + (minibuffer-window-active-p window))) (beep) - (select-window (posn-window posn)) + (select-window window) (if (or (not touch-screen-word-select) (when-let* ((char (char-after point)) (class (char-syntax char))) @@ -544,12 +551,13 @@ touch-screen-handle-point-update (cond ((null what) (let* ((posn (cdr point)) (last-posn (nth 2 touch-screen-current-tool)) + (original-posn (nth 4 touch-screen-current-tool)) ;; Now get the position of X and Y relative to ;; WINDOW. (relative-xy (touch-screen-relative-xy posn window)) - (col (and (eq (posn-area posn) 'text-area) - (car (posn-col-row posn + (col (and (posn-area original-posn) + (car (posn-col-row original-posn (posn-window posn))))) ;; Don't start horizontal scrolling if the touch ;; point originated within two columns of the window @@ -560,14 +568,12 @@ touch-screen-handle-point-update (< (car col) (- (window-width window) 2)))) (diff-x (- (car last-posn) (car relative-xy))) (diff-y (- (cdr last-posn) (cdr relative-xy)))) - ;; Decide whether or not to start scrolling. Make the - ;; hscrolling threshold slightly larger than the vertical - ;; scrolling threshold, to compensate better for - ;; Android-style gesture navigation. - (when (or (> diff-y 10) (and diff-x-eligible - (> diff-x 20)) - (< diff-y -10) (and diff-x-eligible - (< diff-x -20))) + (when (or (> diff-y 10) + (and diff-x-eligible + (> diff-x (frame-char-width))) + (< diff-y -10) + (and diff-x-eligible + (< diff-x (frame-char-width)))) (setcar (nthcdr 3 touch-screen-current-tool) 'scroll) (setcar (nthcdr 2 touch-screen-current-tool) @@ -678,8 +684,12 @@ touch-screen-handle-point-up (when (windowp (posn-window posn)) (setq point (posn-point posn) window (posn-window posn)) - ;; Select the window that was tapped. - (select-window window) + ;; Select the window that was tapped given that it isn't + ;; an inactive minibuffer window. + (when (or (not (eq window) + (minibuffer-window (window-frame window))) + (minibuffer-window-active-p window)) + (select-window window)) ;; Now simulate a mouse click there. If there is a link ;; or a button, use mouse-2 to push it. (let* ((event (list (if (or (mouse-on-link-p posn) commit 48329b79148d09ff7d0951fce97e3966fd504b13 Author: Po Lu Date: Tue Jul 18 12:16:05 2023 +0800 Update Android port * lisp/touch-screen.el (touch-screen-handle-touch): Fix treatment of stray update events. diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 8f10bc3e794..5606f752042 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -850,6 +850,10 @@ touch-screen-handle-touch ;; Start the long-press timer. (touch-screen-handle-timeout nil))))) ((eq (car event) 'touchscreen-update) + (unless touch-screen-current-tool + ;; If a stray touchscreen-update event arrives (most likely + ;; from the menu bar), stop translating this sequence. + (throw 'input-event nil)) ;; The positions of tools currently pressed against the screen ;; have changed. If there is a tool being tracked as part of a ;; gesture, look it up in the list of tools. commit 391c084355a66aa74f8f6864474bd595f3fd3461 Author: Po Lu Date: Tue Jul 18 12:08:04 2023 +0800 Don't enable scroll-bar-mode by default on Android * src/frame.c (syms_of_frame): Default to nil if HAVE_ANDROID. diff --git a/src/frame.c b/src/frame.c index 35881ce6de1..da00cbf4bce 100644 --- a/src/frame.c +++ b/src/frame.c @@ -6489,7 +6489,7 @@ syms_of_frame (void) DEFVAR_LISP ("default-frame-scroll-bars", Vdefault_frame_scroll_bars, doc: /* Default position of vertical scroll bars on this window-system. */); -#ifdef HAVE_WINDOW_SYSTEM +#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_ANDROID #if defined (HAVE_NTGUI) || defined (NS_IMPL_COCOA) || (defined (USE_GTK) && defined (USE_TOOLKIT_SCROLL_BARS)) /* MS-Windows, macOS, and GTK have scroll bars on the right by default. */ @@ -6497,9 +6497,9 @@ syms_of_frame (void) #else Vdefault_frame_scroll_bars = Qleft; #endif -#else +#else /* !HAVE_WINDOW_SYSTEM || HAVE_ANDROID */ Vdefault_frame_scroll_bars = Qnil; -#endif +#endif /* HAVE_WINDOW_SYSTEM && !HAVE_ANDROID */ DEFVAR_BOOL ("scroll-bar-adjust-thumb-portion", scroll_bar_adjust_thumb_portion_p, commit 07716075dbd96591b64098c4355550ef804a7b6c Author: Po Lu Date: Tue Jul 18 10:15:16 2023 +0800 ; * src/keyboard.c (make_lispy_event): Fix botched merge. diff --git a/src/keyboard.c b/src/keyboard.c index 73c4e3f2593..a65d706ee2d 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -6696,6 +6696,8 @@ make_lispy_event (struct input_event *event) } #endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */ + position = make_lispy_position (f, x, y, event->timestamp); + #ifdef HAVE_WINDOW_SYSTEM /* Now check if POSITION lies on the tab bar. If so, look up commit 5ff31bf36cf00f9d5a378ce139fd5c2ae8d3f25e Author: Po Lu Date: Tue Jul 18 10:12:40 2023 +0800 Update Android port * doc/lispref/commands.texi (Touchscreen Events): Describe treatment of canceled touch sequences during touch event translation. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): Update JNI prototypes. * java/org/gnu/emacs/EmacsWindow.java (motionEvent): Set cancelation flag in events sent where appropriate. * lisp/touch-screen.el (touch-screen-handle-point-update): Improve treatment of horizontal scrolling near window edges. (touch-screen-handle-touch): Don't handle point up if the touch sequence has been canceled. * src/android.c (sendTouchDown, sendTouchUp, sendTouchMove): New argument `flags'. * src/androidgui.h (enum android_touch_event_flags): New enum. (struct android_touch_event): New field `flags'. * src/androidterm.c (handle_one_android_event): Report cancelation in TOUCHSCREEN_END_EVENTs. * src/keyboard.c (make_lispy_event): Fix botched merge. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index ffb01254fc9..55fecdce2d7 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2061,9 +2061,11 @@ Touchscreen Events of a single @code{down-mouse-1} event, with subsequent @code{touchscreen-update} events translated to mouse motion events (@pxref{Motion Events}), and a final @code{touchscreen-end} event -translated to a @code{mouse-1} or @code{drag-mouse-1} event. This is -referred to ``simple translation'', and produces a simple -correspondence between touchpoint motion and mouse motion. +translated to a @code{mouse-1} or @code{drag-mouse-1} event (unless +the @code{touchscreen-end} event indicates that the touch sequence has +been intercepted by another program.) This is referred to ``simple +translation'', and produces a simple correspondence between touchpoint +motion and mouse motion. @cindex @code{ignored-mouse-command}, a symbol property However, some commands bound to @@ -2078,13 +2080,14 @@ Touchscreen Events (@pxref{Touchscreens,,, emacs, The GNU Emacs Manual}) first, and finally attempts to translate touch screen events into mouse events if no gesture was detected prior to a closing @code{touchscreen-end} -event and a command is bound to @code{mouse-1} at the location of that -event. Before generating the @code{mouse-1} event, point is also set -to the location of the @code{touchscreen-end} event, and the window -containing the position of that event is selected, as a compromise for -packages which assume @code{mouse-drag-region} has already set point -to the location of any mouse click and selected the window where it -took place. +event (with its @var{canceled} parameter @code{nil}, as with simple +translation) and a command is bound to @code{mouse-1} at the location +of that event. Before generating the @code{mouse-1} event, point is +also set to the location of the @code{touchscreen-end} event, and the +window containing the position of that event is selected, as a +compromise for packages which assume @code{mouse-drag-region} has +already set point to the location of any mouse click and selected the +window where it took place. To prevent unwanted @code{mouse-1} events arriving after a mouse menu is dismissed (@pxref{Mouse Menus}), Emacs also avoids simple diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 1331539879a..d4d502ede5a 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -142,15 +142,18 @@ public static native long sendButtonRelease (short window, int x, int y, /* Send an ANDROID_TOUCH_DOWN event. */ public static native long sendTouchDown (short window, int x, int y, - long time, int pointerID); + long time, int pointerID, + int flags); /* Send an ANDROID_TOUCH_UP event. */ public static native long sendTouchUp (short window, int x, int y, - long time, int pointerID); + long time, int pointerID, + int flags); /* Send an ANDROID_TOUCH_MOVE event. */ public static native long sendTouchMove (short window, int x, int y, - long time, int pointerID); + long time, int pointerID, + int flags); /* Send an ANDROID_WHEEL event. */ public static native long sendWheel (short window, int x, int y, diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 0e96a8382d0..8118479319e 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -1025,23 +1025,30 @@ private static class Coordinate /* Touch down event. */ EmacsNative.sendTouchDown (this.handle, coordinate.x, coordinate.y, time, - coordinate.id); + coordinate.id, 0); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: + /* Touch up event. */ + EmacsNative.sendTouchUp (this.handle, coordinate.x, + coordinate.y, time, + coordinate.id, 0); + break; + case MotionEvent.ACTION_CANCEL: - /* Touch up event. Android documentation says ACTION_CANCEL - should be treated as more or less equivalent to ACTION_UP, - so that is what is done here. */ + /* Touch sequence cancellation event. */ EmacsNative.sendTouchUp (this.handle, coordinate.x, - coordinate.y, time, coordinate.id); + coordinate.y, time, + coordinate.id, + 1 /* ANDROID_TOUCH_SEQUENCE_CANCELED */); break; case MotionEvent.ACTION_MOVE: /* Pointer motion event. */ EmacsNative.sendTouchMove (this.handle, coordinate.x, - coordinate.y, time, coordinate.id); + coordinate.y, time, + coordinate.id, 0); break; } } diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 1ef66d0043f..8f10bc3e794 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -548,11 +548,26 @@ touch-screen-handle-point-update ;; WINDOW. (relative-xy (touch-screen-relative-xy posn window)) + (col (and (eq (posn-area posn) 'text-area) + (car (posn-col-row posn + (posn-window posn))))) + ;; Don't start horizontal scrolling if the touch + ;; point originated within two columns of the window + ;; edges, as systems like Android use those two + ;; columns to implement gesture navigation. + (diff-x-eligible + (and col (> (car col) 2) + (< (car col) (- (window-width window) 2)))) (diff-x (- (car last-posn) (car relative-xy))) (diff-y (- (cdr last-posn) (cdr relative-xy)))) - ;; Decide whether or not to start scrolling. - (when (or (> diff-y 10) (> diff-x 10) - (< diff-y -10) (< diff-x -10)) + ;; Decide whether or not to start scrolling. Make the + ;; hscrolling threshold slightly larger than the vertical + ;; scrolling threshold, to compensate better for + ;; Android-style gesture navigation. + (when (or (> diff-y 10) (and diff-x-eligible + (> diff-x 20)) + (< diff-y -10) (and diff-x-eligible + (< diff-x -20))) (setcar (nthcdr 3 touch-screen-current-tool) 'scroll) (setcar (nthcdr 2 touch-screen-current-tool) @@ -852,7 +867,11 @@ touch-screen-handle-touch (cancel-timer touch-screen-current-timer) (setq touch-screen-current-timer nil)) (unwind-protect - (touch-screen-handle-point-up (cadr event) prefix) + ;; Don't perform any actions associated with releasing the + ;; tool if the touch sequence was intercepted by another + ;; program. + (unless (caddr event) + (touch-screen-handle-point-up (cadr event) prefix)) ;; Make sure the tool list is cleared even if ;; `touch-screen-handle-point-up' throws. (setq touch-screen-current-tool nil))) diff --git a/src/android.c b/src/android.c index 90288737c77..f2e5e75d35e 100644 --- a/src/android.c +++ b/src/android.c @@ -2870,7 +2870,8 @@ NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object, JNIEXPORT jlong JNICALL NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object, jshort window, jint x, jint y, - jlong time, jint pointer_id) + jlong time, jint pointer_id, + jint flags) { JNI_STACK_ALIGNMENT_PROLOGUE; @@ -2883,6 +2884,7 @@ NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object, event.touch.y = y; event.touch.time = time; event.touch.pointer_id = pointer_id; + event.touch.flags = flags; android_write_event (&event); return event_serial; @@ -2891,7 +2893,8 @@ NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object, JNIEXPORT jlong JNICALL NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object, jshort window, jint x, jint y, - jlong time, jint pointer_id) + jlong time, jint pointer_id, + jint flags) { JNI_STACK_ALIGNMENT_PROLOGUE; @@ -2904,6 +2907,7 @@ NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object, event.touch.y = y; event.touch.time = time; event.touch.pointer_id = pointer_id; + event.touch.flags = flags; android_write_event (&event); return event_serial; @@ -2912,7 +2916,8 @@ NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object, JNIEXPORT jlong JNICALL NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object, jshort window, jint x, jint y, - jlong time, jint pointer_id) + jlong time, jint pointer_id, + jint flags) { JNI_STACK_ALIGNMENT_PROLOGUE; @@ -2925,6 +2930,7 @@ NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object, event.touch.y = y; event.touch.time = time; event.touch.pointer_id = pointer_id; + event.touch.flags = flags; android_write_event (&event); return event_serial; diff --git a/src/androidgui.h b/src/androidgui.h index 9e604cdcb8c..265ec29b678 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -365,6 +365,13 @@ #define ANDROID_IS_MODIFIER_KEY(key) \ int width, height; }; +enum android_touch_event_flags + { + /* This touch sequence has been intercepted by the WM (probably + for back gesture navigation or some such.) */ + ANDROID_TOUCH_SEQUENCE_CANCELED = 1, + }; + struct android_touch_event { /* Type of the event. */ @@ -384,6 +391,9 @@ #define ANDROID_IS_MODIFIER_KEY(key) \ /* Index of the pointer being tracked. */ unsigned int pointer_id; + + /* Flags associated with this event. */ + int flags; }; struct android_wheel_event diff --git a/src/androidterm.c b/src/androidterm.c index 27800a61864..bcb6cd6db45 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -1511,6 +1511,11 @@ handle_one_android_event (struct android_display_info *dpyinfo, inev.ie.kind = TOUCHSCREEN_END_EVENT; inev.ie.timestamp = event->touch.time; + /* Report whether the sequence has been canceled. */ + + if (event->touch.flags & ANDROID_TOUCH_SEQUENCE_CANCELED) + inev.ie.modifiers = 1; + XSETFRAME (inev.ie.frame_or_window, any); XSETINT (inev.ie.x, event->touch.x); XSETINT (inev.ie.y, event->touch.y); diff --git a/src/keyboard.c b/src/keyboard.c index bd7433e584a..73c4e3f2593 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -6560,11 +6560,10 @@ make_lispy_event (struct input_event *event) { Lisp_Object x, y, id, position; struct frame *f; +#ifdef HAVE_WINDOW_SYSTEM int tab_bar_item; bool close; -#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR - int column, row, dummy; -#endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */ +#endif /* HAVE_WINDOW_SYSTEM */ f = XFRAME (event->frame_or_window); id = event->arg; @@ -6572,16 +6571,82 @@ make_lispy_event (struct input_event *event) y = event->y; #if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR - if (event->kind == TOUCHSCREEN_BEGIN_EVENT - && coords_in_menu_bar_window (f, XFIXNUM (x), XFIXNUM (y))) + if (coords_in_menu_bar_window (f, XFIXNUM (x), XFIXNUM (y))) { /* If the tap began in the menu bar window, then save the id. */ menu_bar_touch_id = id; return Qnil; } - else if (event->kind == TOUCHSCREEN_END_EVENT - && EQ (menu_bar_touch_id, id)) +#endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */ + + position = make_lispy_position (f, x, y, event->timestamp); + +#ifdef HAVE_WINDOW_SYSTEM + + /* Now check if POSITION lies on the tab bar. If so, look up + the corresponding tab bar item's propertized string as the + OBJECT. */ + + if (coords_in_tab_bar_window (f, XFIXNUM (event->x), + XFIXNUM (event->y)) + /* `get_tab_bar_item_kbd' returns 0 if the item was + previously highlighted, 1 otherwise, and -1 if there is + no tab bar item. */ + && get_tab_bar_item_kbd (f, XFIXNUM (event->x), + XFIXNUM (event->y), &tab_bar_item, + &close) >= 0) + { + /* First, obtain the propertized string. */ + x = Fcopy_sequence (AREF (f->tab_bar_items, + (tab_bar_item + + TAB_BAR_ITEM_CAPTION))); + + /* Next, add the key binding. */ + AUTO_LIST2 (y, Qmenu_item, list3 (AREF (f->tab_bar_items, + (tab_bar_item + + TAB_BAR_ITEM_KEY)), + AREF (f->tab_bar_items, + (tab_bar_item + + TAB_BAR_ITEM_BINDING)), + close ? Qt : Qnil)); + + /* And add the new properties to the propertized string. */ + Fadd_text_properties (make_fixnum (0), + make_fixnum (SCHARS (x)), + y, x); + + /* Set the position to 0. */ + x = Fcons (x, make_fixnum (0)); + + /* Finally, add the OBJECT. */ + position = nconc2 (position, Fcons (x, Qnil)); + } + +#endif /* HAVE_WINDOW_SYSTEM */ + + return list2 (Qtouchscreen_begin, + Fcons (id, position)); + } + + case TOUCHSCREEN_END_EVENT: + { + Lisp_Object x, y, id, position; + struct frame *f = XFRAME (event->frame_or_window); +#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR + int column, row, dummy; +#endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */ +#ifdef HAVE_WINDOW_SYSTEM + int tab_bar_item; + bool close; +#endif /* HAVE_WINDOW_SYSTEM */ + + id = event->arg; + x = event->x; + y = event->y; + +#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR + if (EQ (menu_bar_touch_id, id)) { /* This touch should activate the menu bar. Generate the menu bar event. */ @@ -6631,8 +6696,6 @@ make_lispy_event (struct input_event *event) } #endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */ - position = make_lispy_position (f, x, y, event->timestamp); - #ifdef HAVE_WINDOW_SYSTEM /* Now check if POSITION lies on the tab bar. If so, look up @@ -6676,29 +6739,9 @@ make_lispy_event (struct input_event *event) #endif /* HAVE_WINDOW_SYSTEM */ - return list2 (((event->kind - == TOUCHSCREEN_BEGIN_EVENT) - ? Qtouchscreen_begin - : Qtouchscreen_end), - Fcons (id, position)); - } - - case TOUCHSCREEN_END_EVENT: - { - Lisp_Object x, y, id, position; - struct frame *f = XFRAME (event->frame_or_window); - - id = event->arg; - x = event->x; - y = event->y; - position = make_lispy_position (f, x, y, event->timestamp); - return list3 (((event->kind - == TOUCHSCREEN_BEGIN_EVENT) - ? Qtouchscreen_begin - : Qtouchscreen_end), - Fcons (id, position), + return list3 (Qtouchscreen_end, Fcons (id, position), event->modifiers ? Qt : Qnil); } commit 46fd03a49617066fca87000378771caedfd150f3 Merge: 324d66e3901 9ad601e7d76 Author: Po Lu Date: Tue Jul 18 09:29:16 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 324d66e3901665f1efa82650ee03b13c9b9bcfe4 Merge: 53023eba94b bec707da4db Author: Po Lu Date: Tue Jul 18 08:09:01 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 53023eba94bd159b3f327ac6216c3463f59c2d7a Merge: 85e39e86b0b ac566bcdee3 Author: Po Lu Date: Mon Jul 17 20:44:00 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 85e39e86b0bfff1b49f07c9ef0bc5bdf03739753 Author: Po Lu Date: Mon Jul 17 14:28:20 2023 +0800 Update Android port * doc/lispref/commands.texi (Touchscreen Events): Document meaning of `mouse-1-menu-command'. * lisp/mouse.el (minor-mode-menu-from-indicator): New arg EVENT. Give it to popup-menu. (mouse-minor-mode-menu): Use posn specified within EVENT. * lisp/touch-screen.el (touch-screen-handle-touch): Fix interactive translation. Treat commands labeled `mouse-1-menu-command' like ordinary keymaps. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 74b57f58ee8..bcba3fe4026 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2066,19 +2066,20 @@ Touchscreen Events conflict with defined touch screen gestures (such as ``long-press to drag''), or with user expectations for touch input, and shouldn't subject the touch sequence to simple translation. If a command whose -name contains the property @code{ignored-mouse-command} is encountered -or there is no command bound to @code{down-mouse-1}, a more irregular -form of translation takes place: here, Emacs processes touch screen -gestures (@pxref{Touchscreens,,, emacs, The GNU Emacs Manual}) first, -and finally attempts to translate touch screen events into mouse -events if no gesture was detected prior to a closing -@code{touchscreen-end} event and a command is bound to @code{mouse-1} -at the location of that event. Before generating the @code{mouse-1} -event, point is also set to the location of the @code{touchscreen-end} -event, and the window containing the position of that event is -selected, as a compromise for packages which assume -@code{mouse-drag-region} has already set point to the location of any -mouse click and selected the window where it took place. +name contains the property (@pxref{Symbol Properties}) +@code{ignored-mouse-command} is encountered or there is no command +bound to @code{down-mouse-1}, a more irregular form of translation +takes place: here, Emacs processes touch screen gestures +(@pxref{Touchscreens,,, emacs, The GNU Emacs Manual}) first, and +finally attempts to translate touch screen events into mouse events if +no gesture was detected prior to a closing @code{touchscreen-end} +event and a command is bound to @code{mouse-1} at the location of that +event. Before generating the @code{mouse-1} event, point is also set +to the location of the @code{touchscreen-end} event, and the window +containing the position of that event is selected, as a compromise for +packages which assume @code{mouse-drag-region} has already set point +to the location of any mouse click and selected the window where it +took place. To prevent unwanted @code{mouse-1} events arriving after a mouse menu is dismissed (@pxref{Mouse Menus}), Emacs also avoids simple @@ -2088,6 +2089,13 @@ Touchscreen Events starting position of the touch sequence, consequentially displaying the mouse menu. +@cindex @code{mouse-1-menu-command}, a symbol property +Since certain commands are also bound to @code{down-mouse-1} for the +purpose of displaying pop-up menus, Emacs additionally behaves as +illustrated in the last paragraph if @code{down-mouse-1} is bound to a +command whose name has the property @code{mouse-1-menu-command}. + +@cindex touchscreen gesture events If touch gestures are detected during translation, one of the following input events may be generated: diff --git a/lisp/mouse.el b/lisp/mouse.el index 3c30361ad7d..50c10880da7 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -206,8 +206,11 @@ mouse-double-click-time ;; Provide a mode-specific menu on a mouse button. -(defun minor-mode-menu-from-indicator (indicator) +(defun minor-mode-menu-from-indicator (indicator &optional event) "Show menu for minor mode specified by INDICATOR. +EVENT may be the mouse event that is causing this menu to be +displayed. + Interactively, INDICATOR is read using completion. If there is no menu defined for the minor mode, then create one with items `Turn Off' and `Help'." @@ -234,14 +237,17 @@ minor-mode-menu-from-indicator ,(lambda () (interactive) (describe-function mm-fun))))))) (if menu - (popup-menu menu) + (popup-menu menu event) (message "No menu available"))))) (defun mouse-minor-mode-menu (event) "Show minor-mode menu for EVENT on minor modes area of the mode line." (interactive "@e") (let ((indicator (car (nth 4 (car (cdr event)))))) - (minor-mode-menu-from-indicator indicator))) + (minor-mode-menu-from-indicator indicator event))) + +;; See (elisp)Touchscreen Events. +(put 'mouse-minor-mode-menu 'mouse-1-menu-command t) (defun mouse-menu-major-mode-map () (run-hooks 'activate-menubar-hook 'menu-bar-update-hook) diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 7d733bdb77f..1ef66d0043f 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -767,12 +767,13 @@ touch-screen-handle-touch (if interactive ;; Called interactively (probably from wid-edit.el.) ;; Add any event generated to `unread-command-events'. - (let ((event (catch 'input-event - (touch-screen-translate-touch nil) nil))) - (when (vectorp event) + (let ((event1 + (let ((current-key-remap-sequence (vector event))) + (touch-screen-translate-touch nil)))) + (when (vectorp event1) (setq unread-command-events (nconc unread-command-events - (nreverse (append event nil)))))) + (nreverse (append event1 nil)))))) (cond ((eq (car event) 'touchscreen-begin) ;; A tool was just pressed against the screen. Figure out the @@ -816,13 +817,15 @@ touch-screen-handle-touch t nil position)) (not (and (symbolp binding) (get binding 'ignored-mouse-command)))))) - (if (keymapp binding) - ;; binding is a keymap. If a `mouse-1' event is - ;; generated after the keyboard command loop displays - ;; it as a menu, that event could cause unwanted - ;; commands to be run. Set what to `mouse-1-menu' - ;; instead and wait for the up event to display the - ;; menu. + (if (or (keymapp binding) + (and (symbolp binding) + (get binding 'mouse-1-menu-command))) + ;; binding is a keymap, or a command that does almost + ;; the same thing. If a `mouse-1' event is generated + ;; after the keyboard command loop displays it as a + ;; menu, that event could cause unwanted commands to + ;; be run. Set what to `mouse-1-menu' instead and + ;; wait for the up event to display the menu. (setcar (nthcdr 3 touch-screen-current-tool) 'mouse-1-menu) (progn (setcar (nthcdr 3 touch-screen-current-tool) commit 51a06388b79c8d42241455afeb5ca3ad0d8225d9 Author: Po Lu Date: Mon Jul 17 13:58:32 2023 +0800 Update Android port * doc/lispref/commands.texi (Touchscreen Events): Document changes to simple translation. * lisp/touch-screen.el (touch-screen-handle-point-up): Generate `down-mouse-1' if the current gesture is `mouse-1-menu'. (touch-screen-handle-touch): If binding is a keymap, set state to `mouse-1-menu' and wait for point to be released before generating down-mouse-1. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 025ac197feb..74b57f58ee8 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2080,6 +2080,14 @@ Touchscreen Events @code{mouse-drag-region} has already set point to the location of any mouse click and selected the window where it took place. +To prevent unwanted @code{mouse-1} events arriving after a mouse menu +is dismissed (@pxref{Mouse Menus}), Emacs also avoids simple +translation if @code{down-mouse-1} is bound to a keymap, making it a +prefix key. In lieu of simple translation, it translates the closing +@code{touchscreen-end} to a @code{down-mouse-1} event with the +starting position of the touch sequence, consequentially displaying +the mouse menu. + If touch gestures are detected during translation, one of the following input events may be generated: diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index f500076c78a..7d733bdb77f 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -648,6 +648,11 @@ touch-screen-handle-point-up `drag-mouse-1' event depending on how far the position of POINT is from the starting point of the touch. +If the fourth element of `touch-screen-current-tool' is +`mouse-1-menu', then generate a `down-mouse-1' event at the +original position of the tool to display its bound keymap as a +menu. + If the command being executed is listed in `touch-screen-set-point-commands' also display the on-screen keyboard if the current buffer and the character at the new point @@ -738,7 +743,13 @@ touch-screen-handle-point-up ;; ... otherwise, generate a drag-mouse-1 event. (list 'drag-mouse-1 (cons old-window old-posn) - (cons new-window posn))))))))) + (cons new-window posn)))))) + ((eq what 'mouse-1-menu) + ;; Generate a `down-mouse-1' event at the position the tap + ;; took place. + (throw 'input-event + (list 'down-mouse-1 + (nth 4 touch-screen-current-tool))))))) (defun touch-screen-handle-touch (event prefix &optional interactive) "Handle a single touch EVENT, and perform associated actions. @@ -757,11 +768,11 @@ touch-screen-handle-touch ;; Called interactively (probably from wid-edit.el.) ;; Add any event generated to `unread-command-events'. (let ((event (catch 'input-event - (touch-screen-handle-touch event prefix) nil))) - (when event + (touch-screen-translate-touch nil) nil))) + (when (vectorp event) (setq unread-command-events (nconc unread-command-events - (list event))))) + (nreverse (append event nil)))))) (cond ((eq (car event) 'touchscreen-begin) ;; A tool was just pressed against the screen. Figure out the @@ -770,7 +781,8 @@ touch-screen-handle-touch (let* ((touchpoint (caadr event)) (position (cdadr event)) (window (posn-window position)) - (point (posn-point position))) + (point (posn-point position)) + binding) ;; Cancel the touch screen timer, if it is still there by any ;; chance. (when touch-screen-current-timer @@ -785,22 +797,37 @@ touch-screen-handle-touch nil nil nil nil))) ;; Determine if there is a command bound to `down-mouse-1' at ;; the position of the tap and that command is not a command - ;; whose functionality is replaced by the long-press mechanism. - ;; If so, set the fourth element of `touch-screen-current-tool' - ;; to `mouse-drag' and generate an emulated `mouse-1' event. + ;; whose functionality is replaced by the long-press + ;; mechanism. If so, set the fourth element of + ;; `touch-screen-current-tool' to `mouse-drag' and generate an + ;; emulated `mouse-1' event. + ;; + ;; If the command in question is a keymap, use `mouse-1-menu' + ;; instead of `mouse-drag', and don't generate a + ;; `down-mouse-1' event immediately. Instead, wait for the + ;; touch point to be released. (if (and touch-screen-current-tool (with-selected-window window - (let ((binding (key-binding (if prefix - (vector prefix - 'down-mouse-1) - [down-mouse-1]) - t nil position))) - (and binding - (not (and (symbolp binding) - (get binding 'ignored-mouse-command))))))) - (progn (setcar (nthcdr 3 touch-screen-current-tool) - 'mouse-drag) - (throw 'input-event (list 'down-mouse-1 position))) + (and (setq binding + (key-binding (if prefix + (vector prefix + 'down-mouse-1) + [down-mouse-1]) + t nil position)) + (not (and (symbolp binding) + (get binding 'ignored-mouse-command)))))) + (if (keymapp binding) + ;; binding is a keymap. If a `mouse-1' event is + ;; generated after the keyboard command loop displays + ;; it as a menu, that event could cause unwanted + ;; commands to be run. Set what to `mouse-1-menu' + ;; instead and wait for the up event to display the + ;; menu. + (setcar (nthcdr 3 touch-screen-current-tool) + 'mouse-1-menu) + (progn (setcar (nthcdr 3 touch-screen-current-tool) + 'mouse-drag) + (throw 'input-event (list 'down-mouse-1 position)))) (and point ;; Start the long-press timer. (touch-screen-handle-timeout nil))))) commit 089a710ab0ede81f00e4f66c47836c65f0f588d1 Author: Po Lu Date: Mon Jul 17 12:58:29 2023 +0800 Improve word selection behavior * lisp/tab-bar.el (tab-bar-map): Don't bind touch-screen-drag. * lisp/touch-screen.el (touch-screen-drag): Extend the region based on the position of the mark, not the position of point relative to EVENT. (touch-screen-translate-touch): Don't generate virtual function keys for non-mouse events. (function-key-map): Remove redundant definitions. * src/keyboard.c (read_key_sequence): Don't generate *-bar prefix keys for mock input (such as input from function key translation.) diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index a81f42fc751..044337260ce 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -493,9 +493,7 @@ tab-bar-map "S-" #'tab-bar-move-tab "S-" #'tab-bar-move-tab-backward "S-" #'tab-bar-move-tab - "" #'tab-bar-touchscreen-begin - ;; Trying to set this in `touch-screen.el' runs afoul of the filter. - "" 'touchscreen-drag) + "" #'tab-bar-touchscreen-begin) (global-set-key [tab-bar] `(menu-item ,(purecopy "tab bar") ,(make-sparse-keymap) diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 4543dc5e8ce..f500076c78a 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -405,22 +405,28 @@ touch-screen-drag ;; BEGV if this word is there. (backward-word-strictly) (setq word-start (point))) - ;; If point is greater than the current point, set - ;; it to word-end. - (if (> point (point)) - (goto-char word-end) - ;; Else, go to the start of the word. - (goto-char word-start)) - ;; If point is less than mark, which is is less than - ;; the end of the word that was originally selected, - ;; try to keep it selected by moving mark there. - (when (and initial (<= (point) (mark)) - (< (mark) (cdr initial))) - (set-mark (cdr initial))) - ;; Do the opposite when the converse is true. - (when (and initial (>= (point) (mark)) - (> (mark) (car initial))) - (set-mark (car initial))) + (let ((mark (mark))) + ;; Extend the region to cover either word-end or + ;; word-start; whether to goto word-end or + ;; word-start is subject to the position of the + ;; mark relative to point. + (if (< word-start mark) + ;; The start of the word is behind mark. + ;; Extend the region towards the start. + (goto-char word-start) + ;; Else, go to the end of the word. + (goto-char word-end)) + ;; If point is less than mark, which is is less + ;; than the end of the word that was originally + ;; selected, try to keep it selected by moving + ;; mark there. + (when (and initial (<= (point) mark) + (< mark (cdr initial))) + (set-mark (cdr initial))) + ;; Do the opposite when the converse is true. + (when (and initial (>= (point) mark) + (> mark (car initial))) + (set-mark (car initial)))) (if bounds (progn (setcar bounds word-start) (setcdr bounds word-end)) @@ -439,33 +445,28 @@ touch-screen-drag (when (and (>= (point) (mark)) (> (mark) (car initial))) (set-mark (car initial)))) - (setq touch-screen-word-select-bounds nil))) - (let ((relative-xy - (touch-screen-relative-xy posn window))) - (let ((scroll-conservatively 101)) - (cond - ((< (cdr relative-xy) 0) - (ignore-errors - (goto-char (1- (window-start))) - (setq touch-screen-word-select-bounds nil)) - (redisplay)) - ((> (cdr relative-xy) - (let ((edges (window-inside-pixel-edges))) - (- (nth 3 edges) (cadr edges)))) - (ignore-errors - (goto-char (1+ (window-end nil t))) - (setq touch-screen-word-select-bounds nil)) - (redisplay)))))))))) + (setq touch-screen-word-select-bounds nil)))) + ;; POSN is outside the window. Scroll accordingly. + (let ((relative-xy + (touch-screen-relative-xy posn window))) + (let ((scroll-conservatively 101)) + (cond + ((< (cdr relative-xy) 0) + (ignore-errors + (goto-char (1- (window-start))) + (setq touch-screen-word-select-bounds nil)) + (redisplay)) + ((> (cdr relative-xy) + (let ((edges (window-inside-pixel-edges))) + (- (nth 3 edges) (cadr edges)))) + (ignore-errors + (goto-char (1+ (window-end nil t))) + (setq touch-screen-word-select-bounds nil)) + (redisplay))))))))) (global-set-key [touchscreen-hold] #'touch-screen-hold) (global-set-key [touchscreen-drag] #'touch-screen-drag) -;; Bind this to most of the virtual prefix keys as well. -(global-set-key [tool-bar touchscreen-drag] #'touch-screen-drag) -(global-set-key [header-line touchscreen-drag] #'touch-screen-drag) -(global-set-key [mode-line touchscreen-drag] #'touch-screen-drag) -(global-set-key [tab-line touchscreen-drag] #'touch-screen-drag) - ;; Touch screen event translation. The code here translates raw touch @@ -911,6 +912,11 @@ touch-screen-translate-touch ;; or an empty vector if it is nil, meaning that ;; no key events have been translated. (if event (or (and prefix (consp event) + ;; Only generate virtual function keys for + ;; mouse events. + (memq (car event) + '(down-mouse-1 mouse-1 + mouse-2 mouse-movement)) ;; If this is a mode line event, then ;; generate the appropriate function key. (vector prefix event)) @@ -932,8 +938,6 @@ function-key-map (define-key function-key-map [mode-line touchscreen-begin] #'touch-screen-translate-touch) -(define-key function-key-map [mode-line touchscreen-update] - #'touch-screen-translate-touch) (define-key function-key-map [mode-line touchscreen-end] #'touch-screen-translate-touch) @@ -942,67 +946,54 @@ function-key-map (define-key function-key-map [nil touchscreen-begin] #'touch-screen-translate-touch) -(define-key function-key-map [nil touchscreen-update] - #'touch-screen-translate-touch) (define-key function-key-map [nil touchscreen-end] #'touch-screen-translate-touch) (define-key function-key-map [header-line touchscreen-begin] #'touch-screen-translate-touch) -(define-key function-key-map [header-line touchscreen-update] - #'touch-screen-translate-touch) (define-key function-key-map [header-line touchscreen-end] #'touch-screen-translate-touch) (define-key function-key-map [bottom-divider touchscreen-begin] #'touch-screen-translate-touch) -(define-key function-key-map [bottom-divider touchscreen-update] - #'touch-screen-translate-touch) (define-key function-key-map [bottom-divider touchscreen-end] #'touch-screen-translate-touch) (define-key function-key-map [right-divider touchscreen-begin] #'touch-screen-translate-touch) -(define-key function-key-map [right-divider touchscreen-update] - #'touch-screen-translate-touch) (define-key function-key-map [right-divider touchscreen-end] #'touch-screen-translate-touch) (define-key function-key-map [right-divider touchscreen-begin] #'touch-screen-translate-touch) -(define-key function-key-map [right-divider touchscreen-update] - #'touch-screen-translate-touch) (define-key function-key-map [right-divider touchscreen-end] #'touch-screen-translate-touch) (define-key function-key-map [left-fringe touchscreen-begin] #'touch-screen-translate-touch) -(define-key function-key-map [left-fringe touchscreen-update] - #'touch-screen-translate-touch) (define-key function-key-map [left-fringe touchscreen-end] #'touch-screen-translate-touch) (define-key function-key-map [right-fringe touchscreen-begin] #'touch-screen-translate-touch) -(define-key function-key-map [right-fringe touchscreen-update] - #'touch-screen-translate-touch) (define-key function-key-map [right-fringe touchscreen-end] #'touch-screen-translate-touch) (define-key function-key-map [left-margin touchscreen-begin] #'touch-screen-translate-touch) -(define-key function-key-map [left-margin touchscreen-update] - #'touch-screen-translate-touch) (define-key function-key-map [left-margin touchscreen-end] #'touch-screen-translate-touch) (define-key function-key-map [right-margin touchscreen-begin] #'touch-screen-translate-touch) -(define-key function-key-map [right-margin touchscreen-update] - #'touch-screen-translate-touch) (define-key function-key-map [right-margin touchscreen-end] #'touch-screen-translate-touch) +(define-key function-key-map [tool-bar touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [tool-bar touchscreen-end] + #'touch-screen-translate-touch) + ;; Exports. These functions are intended for use externally. diff --git a/src/keyboard.c b/src/keyboard.c index fa5eea31c3b..78105fa9e05 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -10762,7 +10762,15 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt, posn = POSN_POSN (xevent_start (key)); /* Handle menu-bar events: insert the dummy prefix event `menu-bar'. */ - if (EQ (posn, Qmenu_bar) || EQ (posn, Qtab_bar) || EQ (posn, Qtool_bar)) + if ((EQ (posn, Qmenu_bar) || EQ (posn, Qtab_bar) + || EQ (posn, Qtool_bar)) + /* Only insert the prefix key if the event comes + directly from the keyboard buffer. Key + translation functions might return events with a + `posn-area' of tool-bar or tab-bar without + intending for these prefix events to be + generated. */ + && (mock_input <= t)) { if (READ_KEY_ELTS - t <= 1) error ("Key sequence too long"); commit 21c7024cf8b93f5fc8b7bccdcb8d9ba3b6dadf63 Author: Po Lu Date: Mon Jul 17 09:46:37 2023 +0800 Improve touch screen support * doc/emacs/input.texi (Touchscreens): Document the new feature for people who have trouble dragging to word boundaries. * lisp/touch-screen.el (touch-screen-word-select): New defcustom. (touch-screen-word-select-bounds) (touch-screen-word-select-initial-word): New variable definitions. (touch-screen-hold): If `touch-screen-word-select', select the word around EVENT. (touch-screen-drag): If `touch-screen-word-select', extend the region to the next word boundary if the character under point constitutes a word. (touch-screen-handle-point-update, touch-screen-handle-touch) (touch-screen-translate-touch): Fix doc strings and fill comments. diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi index 66554653def..eccb3e5e243 100644 --- a/doc/emacs/input.texi +++ b/doc/emacs/input.texi @@ -60,6 +60,14 @@ Touchscreens the tool around will make Emacs set the point to where the tool was and begin selecting text under the tool as it moves around, as if @code{mouse-1} were to be held down. @xref{Mouse Commands}. + +@vindex touch-screen-word-select +@cindex word selection mode, touchscreens + Some people find it difficult to position a tool accurately on a +touch screen display, to the detriment of text selection. The user +option @code{touch-screen-word-select} enables ``word selection +mode'', causing dragging to select the complete word, not only the +character containing the position of the tool. @end itemize @vindex touch-screen-delay diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 68a7e213cdb..4543dc5e8ce 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -78,13 +78,27 @@ touch-screen-precision-scroll :group 'mouse :version "30.1") +(defcustom touch-screen-word-select nil + "Whether or not to select whole words while dragging to select. +If non-nil, long-press events (see `touch-screen-delay') followed +by dragging will try to select entire words." + :type 'boolean + :group 'mouse + :version "30.1") + +(defvar-local touch-screen-word-select-bounds nil + "The start and end positions of the word last selected. +Normally a cons of those two positions or nil if no word was +selected.") + +(defvar-local touch-screen-word-select-initial-word nil + "The start and end positions of the first word to be selected. +Used in an attempt to keep this word selected during later +dragging.") + -;; Touch screen event translation. The code here translates raw touch -;; screen events into `touchscreen-scroll' events and mouse events in -;; a ``DWIM'' fashion, consulting the keymaps at the position of the -;; mouse event to determine the best course of action, while also -;; recognizing drag-to-select and other gestures. +;;; Scroll gesture. (defun touch-screen-relative-xy (posn window) "Return the coordinates of POSN, a mouse position list. @@ -237,25 +251,122 @@ touch-screen-scroll (global-set-key [touchscreen-scroll] #'touch-screen-scroll) + + +;;; Drag-to-select gesture. + (defun touch-screen-hold (event) "Handle a long press EVENT. -Beep, select the window at EVENT, set point there, and activate -the mark." +Ding and select the window at EVENT, then activate the mark. If +`touch-screen-word-select' is enabled, try to select the whole +word around EVENT; otherwise, set point to the location of EVENT." (interactive "e") (let* ((posn (cadr event)) (point (posn-point posn))) (when point (beep) (select-window (posn-window posn)) - (set-mark point) - (goto-char point) - (activate-mark)))) + (if (or (not touch-screen-word-select) + (when-let* ((char (char-after point)) + (class (char-syntax char))) + ;; Don't select words if point isn't inside a word + ;; constituent or similar. + (not (or (eq class ?w) (eq class ?_))))) + (progn + ;; Set the mark and activate it. + (setq touch-screen-word-select-initial-word nil + touch-screen-word-select-bounds nil) + (push-mark point) + (goto-char point) + (activate-mark)) + ;; Start word selection by trying to obtain the position + ;; around point. + (let ((word-start nil) + (word-end nil)) + (unless (posn-object posn) + ;; If there's an object under POSN avoid trying to + ;; ascertain the bounds of the word surrounding it. + (save-excursion + (goto-char point) + (forward-word-strictly) + ;; Set word-end to ZV if there is no word after this + ;; one. + (setq word-end (point)) + ;; Now try to move backwards. Set word-start to BEGV if + ;; this word is there. + (backward-word-strictly) + (setq word-start (point)))) + ;; Check if word-start and word-end are identical, if there + ;; is an object under POSN, or if point is looking at or + ;; outside a word. + (if (or (eq word-start word-end) + (>= word-start point)) + (progn + ;; If so, clear the bounds and set and activate the + ;; mark. + (setq touch-screen-word-select-bounds nil) + (push-mark point) + (goto-char point) + (activate-mark)) + ;; Otherwise, select the word. Move point to either the + ;; end or the start of the word, depending on which is + ;; closer to EVENT. + (let ((diff-beg (- point word-start)) + (diff-end (- word-end point)) + use-end) + (if (> diff-beg diff-end) + ;; Set the point to the end of the word. + (setq use-end t) + (if (< diff-end diff-beg) + (setq use-end nil) + ;; POINT is in the middle of the word. Use its + ;; window coordinates to establish whether or not it + ;; is closer to the start of the word or to the end + ;; of the word. + (let ((posn-beg (posn-at-point word-start)) + (posn-end (posn-at-point word-end))) + ;; Give up if there's an object at either of those + ;; positions, or they're not on the same row. + ;; If one of the positions isn't visible, use the + ;; window end. + (if (and posn-beg posn-end + (not (posn-object posn-beg)) + (not (posn-object posn-end)) + (eq (cdr (posn-col-row posn-beg)) + (cdr (posn-col-row posn-end)))) + (setq use-end nil) + ;; Compare the pixel positions. + (setq point (car (posn-x-y posn)) + diff-beg (- point (car (posn-x-y posn-beg))) + diff-end (- (car (posn-x-y posn-end)) point)) + ;; Now determine whether or not point should be + ;; moved to the end. + (setq use-end (>= diff-beg diff-end)))))) + (if use-end + (progn + (push-mark word-start) + (activate-mark) + (goto-char word-end)) + (progn + (push-mark word-end) + (activate-mark) + (goto-char word-start))) + ;; Record the bounds of the selected word. + (setq touch-screen-word-select-bounds + (cons word-start word-end) + ;; Save this for the benefit of touch-screen-drag. + touch-screen-word-select-initial-word + (cons word-start word-end))))))))) (defun touch-screen-drag (event) "Handle a drag EVENT by setting the region to its new point. -Scroll the window if necessary." +If `touch-screen-word-select' and EVENT lies outside the last +word that was selected, select the word that now contains POINT. +Scroll the window if EVENT's coordinates are outside its text +area." (interactive "e") (let* ((posn (cadr event)) ; Position of the tool. + (point (posn-point posn)) ; Point of the event. ; Window where the tap originated. (window (nth 1 touch-screen-current-tool))) ;; Keep dragging. @@ -265,22 +376,86 @@ touch-screen-drag ;; then go to the line before either window start or ;; window end. (if (and (eq (posn-window posn) window) - (posn-point posn)) - (goto-char (posn-point posn)) - (let ((relative-xy - (touch-screen-relative-xy posn window))) - (let ((scroll-conservatively 101)) - (cond - ((< (cdr relative-xy) 0) - (ignore-errors - (goto-char (1- (window-start)))) - (redisplay)) - ((> (cdr relative-xy) - (let ((edges (window-inside-pixel-edges))) - (- (nth 3 edges) (cadr edges)))) - (ignore-errors - (goto-char (1+ (window-end nil t)))) - (redisplay))))))))) + point (not (eq point (point)))) + (let* ((bounds touch-screen-word-select-bounds) + (initial touch-screen-word-select-initial-word) + (maybe-select-word (or (not touch-screen-word-select) + (or (not bounds) + (> point (cdr bounds)) + (< point (car bounds)))))) + (if (and touch-screen-word-select + ;; point is now outside the last word selected. + maybe-select-word + (not (posn-object posn)) + (when-let* ((char (char-after point)) + (class (char-syntax char))) + ;; Don't select words if point isn't inside a + ;; word constituent or similar. + (or (eq class ?w) (eq class ?_)))) + ;; Determine the confines of the word containing + ;; POINT. + (let (word-start word-end) + (save-excursion + (goto-char point) + (forward-word-strictly) + ;; Set word-end to ZV if there is no word after + ;; this one. + (setq word-end (point)) + ;; Now try to move backwards. Set word-start to + ;; BEGV if this word is there. + (backward-word-strictly) + (setq word-start (point))) + ;; If point is greater than the current point, set + ;; it to word-end. + (if (> point (point)) + (goto-char word-end) + ;; Else, go to the start of the word. + (goto-char word-start)) + ;; If point is less than mark, which is is less than + ;; the end of the word that was originally selected, + ;; try to keep it selected by moving mark there. + (when (and initial (<= (point) (mark)) + (< (mark) (cdr initial))) + (set-mark (cdr initial))) + ;; Do the opposite when the converse is true. + (when (and initial (>= (point) (mark)) + (> (mark) (car initial))) + (set-mark (car initial))) + (if bounds + (progn (setcar bounds word-start) + (setcdr bounds word-end)) + (setq touch-screen-word-select-bounds + (cons word-start word-end)))) + (when maybe-select-word + (goto-char (posn-point posn)) + (when initial + ;; If point is less than mark, which is is less than + ;; the end of the word that was originally selected, + ;; try to keep it selected by moving mark there. + (when (and (<= (point) (mark)) + (< (mark) (cdr initial))) + (set-mark (cdr initial))) + ;; Do the opposite when the converse is true. + (when (and (>= (point) (mark)) + (> (mark) (car initial))) + (set-mark (car initial)))) + (setq touch-screen-word-select-bounds nil))) + (let ((relative-xy + (touch-screen-relative-xy posn window))) + (let ((scroll-conservatively 101)) + (cond + ((< (cdr relative-xy) 0) + (ignore-errors + (goto-char (1- (window-start))) + (setq touch-screen-word-select-bounds nil)) + (redisplay)) + ((> (cdr relative-xy) + (let ((edges (window-inside-pixel-edges))) + (- (nth 3 edges) (cadr edges)))) + (ignore-errors + (goto-char (1+ (window-end nil t))) + (setq touch-screen-word-select-bounds nil)) + (redisplay)))))))))) (global-set-key [touchscreen-hold] #'touch-screen-hold) (global-set-key [touchscreen-drag] #'touch-screen-drag) @@ -291,6 +466,14 @@ touch-screen-drag (global-set-key [mode-line touchscreen-drag] #'touch-screen-drag) (global-set-key [tab-line touchscreen-drag] #'touch-screen-drag) + + +;; Touch screen event translation. The code here translates raw touch +;; screen events into `touchscreen-scroll' events and mouse events in +;; a ``DWIM'' fashion, consulting the keymaps at the position of the +;; mouse event to determine the best course of action, while also +;; recognizing drag-to-select and other gestures. + (defun touch-screen-handle-timeout (arg) "Start the touch screen timeout or handle it depending on ARG. When ARG is nil, start the `touch-screen-current-timer' to go off @@ -419,8 +602,9 @@ touch-screen-handle-point-update ;; Now start dragging. (setcar (nthcdr 3 touch-screen-current-tool) 'drag) - ;; Generate a (touchscreen-drag POSN) event. `touchscreen-hold' - ;; was generated when the timeout fired. + ;; Generate a (touchscreen-drag POSN) event. + ;; `touchscreen-hold' was generated when the timeout + ;; fired. (throw 'input-event (list 'touchscreen-drag posn)))) ((eq what 'drag) (let* ((posn (cdr point))) @@ -631,8 +815,8 @@ touch-screen-handle-touch ;; A tool has been removed from the screen. If it is the tool ;; currently being tracked, clear `touch-screen-current-tool'. (when (eq (caadr event) (car touch-screen-current-tool)) - ;; Cancel the touch screen long-press timer, if it is still there - ;; by any chance. + ;; Cancel the touch screen long-press timer, if it is still + ;; there by any chance. (when touch-screen-current-timer (cancel-timer touch-screen-current-timer) (setq touch-screen-current-timer nil)) @@ -714,8 +898,8 @@ touch-screen-translate-touch left-margin right-margin right-divider bottom-divider)) (setq prefix event1) - ;; If event1 is not a touch screen event, return - ;; it. + ;; If event1 is not a touch screen event, + ;; return it. (if (not (memq (car-safe event1) '(touchscreen-begin touchscreen-end @@ -727,8 +911,8 @@ touch-screen-translate-touch ;; or an empty vector if it is nil, meaning that ;; no key events have been translated. (if event (or (and prefix (consp event) - ;; If this is a mode line event, then generate - ;; the appropriate function key. + ;; If this is a mode line event, then + ;; generate the appropriate function key. (vector prefix event)) (vector event)) "")) @@ -753,8 +937,8 @@ function-key-map (define-key function-key-map [mode-line touchscreen-end] #'touch-screen-translate-touch) -;; These are used to translate events sent from the internal border -;; or from outside the frame. +;; These are used to translate events sent from the internal border or +;; from outside the frame. (define-key function-key-map [nil touchscreen-begin] #'touch-screen-translate-touch) commit 2e33d1b62def7a7f2ffb3227860d7831b25b18c5 Merge: 8335c76e88e a65ece8b201 Author: Po Lu Date: Mon Jul 17 07:29:11 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 8335c76e88ec9aa44963a3261ce0ab0eb0555a29 Author: Po Lu Date: Sun Jul 16 20:10:18 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsService.java (displayToast): * src/android.c (android_init_emacs_service): Remove unused function. * lisp/touch-screen.el (touch-screen-handle-point-up): Correctly compute posn point. (touch-screen-translate-touch): Update doc string. (function-key-map): Define touch screen translation functions within the internal border. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 22649167f8a..6240b0e475a 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -1086,26 +1086,6 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) temp, }; } - /* Display the specified STRING in a small dialog box on the main - thread. */ - - public void - displayToast (final String string) - { - runOnUiThread (new Runnable () { - @Override - public void - run () - { - Toast toast; - - toast = Toast.makeText (getApplicationContext (), - string, Toast.LENGTH_SHORT); - toast.show (); - } - }); - } - public void updateExtractedText (EmacsWindow window, ExtractedText text, int token) diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index ef9b14f27ce..68a7e213cdb 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -25,6 +25,9 @@ ;; This file provides code to recognize simple touch screen gestures. ;; It is used on X and Android, where the platform cannot recognize ;; them for us. +;; +;; See (elisp)Touchscreen Events for a description of the details of touch +;; events. ;;; Code: @@ -468,7 +471,7 @@ touch-screen-handle-point-up (posn (cdr point)) window point) (cond ((null what) (when (windowp (posn-window posn)) - (setq point (posn-point point) + (setq point (posn-point posn) window (posn-window posn)) ;; Select the window that was tapped. (select-window window) @@ -670,12 +673,18 @@ touch-screen-translate-touch where WINDOW specifies a window to scroll, and DX and DY are integers describing how many pixels to be scrolled horizontally -and vertically. +and vertically, + + (touchscreen-hold POSN) + (touchscreen-drag POSN) + +where POSN is the position of the long-press or touchpoint +motion, (down-mouse-1 POSN) (drag-mouse-1 POSN) -where POSN is the position of the mouse button press or click. +where POSN is the position of the mouse button press or click, (mouse-1 POSN) (mouse-2 POSN) @@ -744,6 +753,16 @@ function-key-map (define-key function-key-map [mode-line touchscreen-end] #'touch-screen-translate-touch) +;; These are used to translate events sent from the internal border +;; or from outside the frame. + +(define-key function-key-map [nil touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [nil touchscreen-update] + #'touch-screen-translate-touch) +(define-key function-key-map [nil touchscreen-end] + #'touch-screen-translate-touch) + (define-key function-key-map [header-line touchscreen-begin] #'touch-screen-translate-touch) (define-key function-key-map [header-line touchscreen-update] diff --git a/src/android.c b/src/android.c index acc49219b35..90288737c77 100644 --- a/src/android.c +++ b/src/android.c @@ -2330,8 +2330,6 @@ #define FIND_METHOD(c_name, name, signature) \ FIND_METHOD (check_content_uri, "checkContentUri", "([BZZ)Z"); FIND_METHOD (query_battery, "queryBattery", "()[J"); - FIND_METHOD (display_toast, "displayToast", - "(Ljava/lang/String;)V"); FIND_METHOD (update_extracted_text, "updateExtractedText", "(Lorg/gnu/emacs/EmacsWindow;" "Landroid/view/inputmethod/ExtractedText;I)V"); commit 64256d29234b0fc3012d07cb189ebeccc2dfc187 Author: Po Lu Date: Sun Jul 16 17:29:58 2023 +0800 Update Android port * doc/lispref/commands.texi (Touchscreen Events): Improve documentation. * lisp/tab-bar.el (tab-bar-map): Bind `[tab-bar touchscreen-hold]'. * lisp/touch-screen.el (touch-screen-hold, touch-screen-drag): New functions. (touch-screen-handle-timeout): Generate a `touchscreen-hold' event instead. (touch-screen-handle-point-update): Generate a `touchscreen-drag' event upon dragging. (touch-screen-translate-touch): Cancel touch screen timer upon exit. * src/keyboard.c (access_keymap_keyremap): Take unsigned int start and end instead. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 725ca900165..025ac197feb 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2080,25 +2080,42 @@ Touchscreen Events @code{mouse-drag-region} has already set point to the location of any mouse click and selected the window where it took place. +If touch gestures are detected during translation, one of the +following input events may be generated: + +@table @code @cindex @code{touchscreen-scroll} event +@item (touchscreen-scroll @var{window} @var{dx} @var{dy}) If a ``scrolling'' gesture is detected during the translation process, each subsequent @code{touchscreen-update} event is translated to a -@code{touchscreen-scroll} event of the form: - -@example -@w{@code{(touchscreen-scroll @var{window} @var{dx} @var{dy})}} -@end example - -where @var{dx} and @var{dy} specify, in pixels, the relative motion of -the tool from the position of the @code{touchscreen-begin} event that -started the sequence or the last @code{touchscreen-scroll} event, -whichever came later. +@code{touchscreen-scroll} event, where @var{dx} and @var{dy} specify, +in pixels, the relative motion of the touchpoint from the position of +the @code{touchscreen-begin} event that started the sequence or the +last @code{touchscreen-scroll} event, whichever came later. + +@cindex @code{touchscreen-hold} event +@item (touchscreen-hold @var{posn}) +If the single active touchpoint remains stationary for more than +@code{touch-screen-delay} seconds after a @code{touchscreen-begin} is +generated, a ``long-press'' gesture is detected during the translation +process, and a @code{touchscreen-hold} event is sent, with @var{posn} +set to a mouse position list containing the position of the +@code{touchscreen-begin} event. + +@cindex @code{touchscreen-drag} event +@item (touchscreen-drag @var{posn}) +If a ``long-press'' gesture is detected while translating the current +touch sequence, a @code{touchscreen-drag} event is sent upon each +subsequent @code{touchscreen-update} event, with @var{posn} set to the +new position of the touchpoint. +@end table @cindex handling touch screen events @cindex tap and drag, touch screen gestures -Emacs provides two functions to handle touch screen events. They are -intended to be used by a command bound to @code{touchscreen-begin} to -handle common gestures. +Emacs provides two functions to handle touch screen events independent +of gesture recognition or mouse event translation. They are intended +to be used by commands bound to @code{touchscreen-begin}, to recognize +and handle common gestures. @defun touch-screen-track-tap event &optional update data This function is used to track a single ``tap'' gesture originating diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 044337260ce..a81f42fc751 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -493,7 +493,9 @@ tab-bar-map "S-" #'tab-bar-move-tab "S-" #'tab-bar-move-tab-backward "S-" #'tab-bar-move-tab - "" #'tab-bar-touchscreen-begin) + "" #'tab-bar-touchscreen-begin + ;; Trying to set this in `touch-screen.el' runs afoul of the filter. + "" 'touchscreen-drag) (global-set-key [tab-bar] `(menu-item ,(purecopy "tab bar") ,(make-sparse-keymap) diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 0f584269931..ef9b14f27ce 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -234,15 +234,69 @@ touch-screen-scroll (global-set-key [touchscreen-scroll] #'touch-screen-scroll) +(defun touch-screen-hold (event) + "Handle a long press EVENT. +Beep, select the window at EVENT, set point there, and activate +the mark." + (interactive "e") + (let* ((posn (cadr event)) + (point (posn-point posn))) + (when point + (beep) + (select-window (posn-window posn)) + (set-mark point) + (goto-char point) + (activate-mark)))) + +(defun touch-screen-drag (event) + "Handle a drag EVENT by setting the region to its new point. +Scroll the window if necessary." + (interactive "e") + (let* ((posn (cadr event)) ; Position of the tool. + ; Window where the tap originated. + (window (nth 1 touch-screen-current-tool))) + ;; Keep dragging. + (with-selected-window window + ;; Figure out what character to go to. If this posn is + ;; in the window, go to (posn-point posn). If not, + ;; then go to the line before either window start or + ;; window end. + (if (and (eq (posn-window posn) window) + (posn-point posn)) + (goto-char (posn-point posn)) + (let ((relative-xy + (touch-screen-relative-xy posn window))) + (let ((scroll-conservatively 101)) + (cond + ((< (cdr relative-xy) 0) + (ignore-errors + (goto-char (1- (window-start)))) + (redisplay)) + ((> (cdr relative-xy) + (let ((edges (window-inside-pixel-edges))) + (- (nth 3 edges) (cadr edges)))) + (ignore-errors + (goto-char (1+ (window-end nil t)))) + (redisplay))))))))) + +(global-set-key [touchscreen-hold] #'touch-screen-hold) +(global-set-key [touchscreen-drag] #'touch-screen-drag) + +;; Bind this to most of the virtual prefix keys as well. +(global-set-key [tool-bar touchscreen-drag] #'touch-screen-drag) +(global-set-key [header-line touchscreen-drag] #'touch-screen-drag) +(global-set-key [mode-line touchscreen-drag] #'touch-screen-drag) +(global-set-key [tab-line touchscreen-drag] #'touch-screen-drag) + (defun touch-screen-handle-timeout (arg) "Start the touch screen timeout or handle it depending on ARG. When ARG is nil, start the `touch-screen-current-timer' to go off in `touch-screen-delay' seconds, and call this function with ARG t. -When ARG is t, beep. Then, set the fourth element of -touch-screen-current-tool to `held', and the mark to the last -known position of the tool." +When ARG is t, set the fourth element of +`touch-screen-current-tool' to `held', and generate a +`touchscreen-hold' event at the original position of that tool." (if (not arg) ;; Cancel the touch screen long-press timer, if it is still ;; there by any chance. @@ -253,19 +307,16 @@ touch-screen-handle-timeout (run-at-time touch-screen-delay nil #'touch-screen-handle-timeout t))) - ;; Beep. - (beep) ;; Set touch-screen-current-timer to nil. (setq touch-screen-current-timer nil) (when touch-screen-current-tool ;; Set the state to `held'. (setcar (nthcdr 3 touch-screen-current-tool) 'held) - ;; Go to the initial position of the touchpoint and activate the - ;; mark. - (select-window (cadr touch-screen-current-tool)) - (set-mark (posn-point (nth 4 touch-screen-current-tool))) - (goto-char (mark)) - (activate-mark)))) + ;; Generate an input event at the original position of the mark. + ;; This assumes that the timer is running within + ;; `touch-screen-translate-touch'. + (let ((posn (nth 4 touch-screen-current-tool))) + (throw 'input-event (list 'touchscreen-hold posn)))))) (defun touch-screen-handle-point-update (point) "Notice that the touch point POINT has changed position. @@ -297,7 +348,7 @@ touch-screen-handle-point-update If the fourth element of `touch-screen-current-tool' is `held', then the touch has been held down for some time. If motion happens, cancel `touch-screen-current-timer', and set the field -to `drag'. Then, activate the mark and start dragging. +to `drag'. Then, generate a `touchscreen-drag' event. If the fourth element of `touch-screen-current-tool' is `drag', then move point to the position of POINT." @@ -319,16 +370,16 @@ touch-screen-handle-point-update 'scroll) (setcar (nthcdr 2 touch-screen-current-tool) relative-xy) - ;; Generate a `touchscreen-scroll' event with `diff-x' - ;; and `diff-y'. - (throw 'input-event - (list 'touchscreen-scroll - window diff-x diff-y)) ;; Cancel the touch screen long-press timer, if it is ;; still there by any chance. (when touch-screen-current-timer (cancel-timer touch-screen-current-timer) - (setq touch-screen-current-timer nil))))) + (setq touch-screen-current-timer nil)) + ;; Generate a `touchscreen-scroll' event with `diff-x' + ;; and `diff-y'. + (throw 'input-event + (list 'touchscreen-scroll + window diff-x diff-y))))) ((eq what 'scroll) ;; Cancel the touch screen long-press timer, if it is still ;; there by any chance. @@ -361,67 +412,17 @@ touch-screen-handle-point-update (throw 'input-event (list 'mouse-movement (cdr point))))) ((eq what 'held) - (let* ((posn (cdr point)) - (relative-xy - (touch-screen-relative-xy posn window))) - (when touch-screen-current-timer - (cancel-timer touch-screen-current-timer) - (setq touch-screen-current-timer nil)) + (let* ((posn (cdr point))) ;; Now start dragging. (setcar (nthcdr 3 touch-screen-current-tool) 'drag) - (setcar (nthcdr 2 touch-screen-current-tool) - relative-xy) - (with-selected-window window - ;; Activate the mark. It should have been set by the - ;; time `touch-screen-timeout' was called. - (activate-mark) - ;; Figure out what character to go to. If this posn is - ;; in the window, go to (posn-point posn). If not, - ;; then go to the line before either window start or - ;; window end. - (if (and (eq (posn-window posn) window) - (posn-point posn)) - (goto-char (posn-point posn)) - (let ((relative-xy - (touch-screen-relative-xy posn window))) - (let ((scroll-conservatively 101)) - (cond - ((< (cdr relative-xy) 0) - (ignore-errors - (goto-char (1- (window-start)))) - (redisplay)) - ((> (cdr relative-xy) - (let ((edges (window-inside-pixel-edges))) - (- (nth 3 edges) (cadr edges)))) - (ignore-errors - (goto-char (1+ (window-end nil t)))) - (redisplay))))))))) + ;; Generate a (touchscreen-drag POSN) event. `touchscreen-hold' + ;; was generated when the timeout fired. + (throw 'input-event (list 'touchscreen-drag posn)))) ((eq what 'drag) (let* ((posn (cdr point))) - ;; Keep dragging. - (with-selected-window window - ;; Figure out what character to go to. If this posn is - ;; in the window, go to (posn-point posn). If not, - ;; then go to the line before either window start or - ;; window end. - (if (and (eq (posn-window posn) window) - (posn-point posn)) - (goto-char (posn-point posn)) - (let ((relative-xy - (touch-screen-relative-xy posn window))) - (let ((scroll-conservatively 101)) - (cond - ((< (cdr relative-xy) 0) - (ignore-errors - (goto-char (1- (window-start)))) - (redisplay)) - ((> (cdr relative-xy) - (let ((edges (window-inside-pixel-edges))) - (- (nth 3 edges) (cadr edges)))) - (ignore-errors - (goto-char (1+ (window-end nil t)))) - (redisplay)))))))))))) + ;; Generate a (touchscreen-drag POSN) event. + (throw 'input-event (list 'touchscreen-drag posn))))))) (defun touch-screen-window-selection-changed (frame) "Notice that FRAME's selected window has changed. @@ -681,7 +682,7 @@ touch-screen-translate-touch where POSN is the position of the mouse click, either `mouse-2' if POSN is on a link or a button, or `mouse-1' otherwise." - (if (> (length current-key-remap-sequence) 0) + (unwind-protect ;; Save the virtual function key if this is a mode line event. (let* ((prefix (and (> (length current-key-remap-sequence) 1) (aref current-key-remap-sequence 0))) @@ -721,7 +722,13 @@ touch-screen-translate-touch ;; the appropriate function key. (vector prefix event)) (vector event)) - "")))) + "")) + ;; Cancel the touch screen long-press timer, if it is still there + ;; by any chance. If the timer is to operate correctly, it must + ;; fire within the catch block above. + (when touch-screen-current-timer + (cancel-timer touch-screen-current-timer) + (setq touch-screen-current-timer nil)))) (define-key function-key-map [touchscreen-begin] #'touch-screen-translate-touch) diff --git a/src/keyboard.c b/src/keyboard.c index e10128def13..fa5eea31c3b 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -10001,7 +10001,7 @@ active_maps (Lisp_Object first_event, Lisp_Object second_event) static Lisp_Object access_keymap_keyremap (Lisp_Object map, Lisp_Object key, Lisp_Object prompt, - bool do_funcall, ptrdiff_t start, ptrdiff_t end, + bool do_funcall, unsigned int start, unsigned int end, Lisp_Object *keybuf) { Lisp_Object next; commit 7b346b92b4c30c634d094e6162b65a22a52b93bb Author: Po Lu Date: Sun Jul 16 15:30:01 2023 +0800 Improve touch-screen support * doc/emacs/emacs.texi (Top): * doc/emacs/input.texi (Other Input Devices): Correctly capitalize subsection name. (Touchscreens): Document additional translation. * doc/lispref/commands.texi (Touchscreen Events): Document that `touchscreen-end' events now have prefix keys. Also, describe mouse emulation and `touchscreen-scroll' events. * doc/lispref/keymaps.texi (Translation Keymaps): Document `current-key-remap-sequence'. * lisp/touch-screen.el (touch-screen-translate-prompt): New function. (touch-screen-scroll): New command. Bind to `touchscreen-scroll'. (touch-screen-handle-point-update, touch-screen-handle-point-up) (touch-screen-handle-touch): Refactor to actually translate touch screen event sequences, as opposed to looking up commands and executing them. (touch-screen-translate-touch): New function. Bind in function-key-map to all touch screen events. (touch-screen-drag-mode-line-1, touch-screen-drag-mode-line) (touch-screen-tap-header-line): Remove special commands for dragging the mode line and clicking on the header line. * lisp/wid-edit.el (widget-button-click): Adjust accordingly. * src/keyboard.c (access_keymap_keyremap): Bind `current-key-remap-sequence' to the key sequence being remapped. (keyremap_step): Give fkey->start and fkey->end to access_keymap_keyremap. (head_table): Add imaginary prefix to touchscreen-end events as well. (syms_of_keyboard): New variable Vcurrent_key_remap_sequence. diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi index 92be9f9b9a9..b255e679d5f 100644 --- a/doc/emacs/emacs.texi +++ b/doc/emacs/emacs.texi @@ -1273,7 +1273,7 @@ Top * Android Troubleshooting:: Dealing with problems. * Android Software:: Getting extra software. -Emacs and unconventional input devices +Emacs and Unconventional Input Devices * Touchscreens:: Using Emacs on touchscreens. * On-Screen Keyboards:: Using Emacs with virtual keyboards. diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi index 0df3162ce97..66554653def 100644 --- a/doc/emacs/input.texi +++ b/doc/emacs/input.texi @@ -2,7 +2,7 @@ @c Copyright (C) 2023 Free Software Foundation, Inc. @c See file emacs.texi for copying conditions. @node Other Input Devices -@appendix Emacs and unconventional input devices +@appendix Emacs and Unconventional Input Devices @cindex other input devices Emacs was originally developed with the assumption that its users @@ -21,7 +21,7 @@ Other Input Devices @node Touchscreens @section Using Emacs on touchscreens -@cindex touchscreens +@cindex touchscreen input Touchscreen input works by pressing and moving tools (which include fingers and some pointing devices--styluses, for example) onto a frame @@ -40,6 +40,9 @@ Touchscreens window. If the tap happened on top of a link (@pxref{Mouse References}), then Emacs will follow the link instead. + If a command bound to @code{down-mouse-1} is bound to the location +where the tap took place, Emacs will execute that command as well. + @item @cindex scrolling, touchscreens ``Scrolling'', meaning to place a tool on the display and move it up diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 7a076406bed..725ca900165 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2013,10 +2013,7 @@ Touchscreen Events These events also have imaginary prefixes keys added by @code{read-key-sequence} when they originate on top of a special part -of a frame or window. @xref{Key Sequence Input}. The reason the -other touch screen events do not undergo this treatment is that they -are rarely useful without being used in tandem from their -corresponding @code{touchscreen-begin} events. +of a frame or window. @xref{Key Sequence Input}. @cindex @code{touchscreen-update} event @item (touchscreen-update @var{points}) @@ -2029,12 +2026,73 @@ Touchscreen Events This event is sent when @var{point} is no longer present on the display, because another program took the grab, or because the user raised the finger from the touchscreen. + +These events also have imaginary prefixes keys added by +@code{read-key-sequence} when they originate on top of a special part +of a frame or window. @end table If a touchpoint is pressed against the menu bar, then Emacs will not generate any corresponding @code{touchscreen-begin} or @code{touchscreen-end} events; instead, the menu bar may be displayed -when @code{touchscreen-end} should have been delivered. +after @code{touchscreen-end} would have been delivered under other +circumstances. + +@cindex mouse emulation from touch screen events +When no command is bound to @code{touchscreen-begin}, +@code{touchscreen-end} or @code{touchscreen-update}, Emacs calls a +``key translation function'' (@pxref{Translation Keymaps}) to +translate key sequences containing touch screen events into ordinary +mouse events (@pxref{Mouse Events}.) Since Emacs doesn't support +distinguishing events originating from separate mouse devices, it +assumes that only one touchpoint is active while translation takes +place; breaking this assumption may lead to unexpected behavior. + +Emacs applies two different strategies for translating touch events +into mouse events, contingent on factors such as the commands bound to +keymaps that are active at the location of the +@code{touchscreen-begin} event. If a command is bound to +@code{down-mouse-1} at that location, the initial translation consists +of a single @code{down-mouse-1} event, with subsequent +@code{touchscreen-update} events translated to mouse motion events +(@pxref{Motion Events}), and a final @code{touchscreen-end} event +translated to a @code{mouse-1} or @code{drag-mouse-1} event. This is +referred to ``simple translation'', and produces a simple +correspondence between touchpoint motion and mouse motion. + +@cindex @code{ignored-mouse-command}, a symbol property +However, some commands bound to +@code{down-mouse-1}--@code{mouse-drag-region}, for example--either +conflict with defined touch screen gestures (such as ``long-press to +drag''), or with user expectations for touch input, and shouldn't +subject the touch sequence to simple translation. If a command whose +name contains the property @code{ignored-mouse-command} is encountered +or there is no command bound to @code{down-mouse-1}, a more irregular +form of translation takes place: here, Emacs processes touch screen +gestures (@pxref{Touchscreens,,, emacs, The GNU Emacs Manual}) first, +and finally attempts to translate touch screen events into mouse +events if no gesture was detected prior to a closing +@code{touchscreen-end} event and a command is bound to @code{mouse-1} +at the location of that event. Before generating the @code{mouse-1} +event, point is also set to the location of the @code{touchscreen-end} +event, and the window containing the position of that event is +selected, as a compromise for packages which assume +@code{mouse-drag-region} has already set point to the location of any +mouse click and selected the window where it took place. + +@cindex @code{touchscreen-scroll} event +If a ``scrolling'' gesture is detected during the translation process, +each subsequent @code{touchscreen-update} event is translated to a +@code{touchscreen-scroll} event of the form: + +@example +@w{@code{(touchscreen-scroll @var{window} @var{dx} @var{dy})}} +@end example + +where @var{dx} and @var{dy} specify, in pixels, the relative motion of +the tool from the position of the @code{touchscreen-begin} event that +started the sequence or the last @code{touchscreen-scroll} event, +whichever came later. @cindex handling touch screen events @cindex tap and drag, touch screen gestures diff --git a/doc/lispref/keymaps.texi b/doc/lispref/keymaps.texi index 05dc17eb03f..e41dbf9def8 100644 --- a/doc/lispref/keymaps.texi +++ b/doc/lispref/keymaps.texi @@ -2044,6 +2044,15 @@ Translation Keymaps @end group @end example +@cindex accessing events within a key translation function +@vindex current-key-remap-sequence +A key translation function might want to adjust its behavior based on +parameters to events within a key sequence containing non-key events +(@pxref{Input Events}.) This information is available from the +variable @code{current-key-remap-sequence}, which is bound to the key +sub-sequence being translated around calls to key translation +functions. + @subsection Interaction with normal keymaps The end of a key sequence is detected when that key sequence either is bound diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 242ea4fcd9b..0f584269931 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -49,6 +49,11 @@ touch-screen-current-timer "Timer used to track long-presses. This is always cleared upon any significant state change.") +(defvar touch-screen-translate-prompt nil + "Prompt given to the touch screen translation function. +If non-nil, the touch screen key event translation machinery +is being called from `read-sequence' or some similar function.") + (defcustom touch-screen-display-keyboard nil "If non-nil, always display the on screen keyboard. A buffer local value means to always display the on screen @@ -70,6 +75,14 @@ touch-screen-precision-scroll :group 'mouse :version "30.1") + + +;; Touch screen event translation. The code here translates raw touch +;; screen events into `touchscreen-scroll' events and mouse events in +;; a ``DWIM'' fashion, consulting the keymaps at the position of the +;; mouse event to determine the best course of action, while also +;; recognizing drag-to-select and other gestures. + (defun touch-screen-relative-xy (posn window) "Return the coordinates of POSN, a mouse position list. However, return the coordinates relative to WINDOW. @@ -201,6 +214,26 @@ touch-screen-handle-scroll (setcar (nthcdr 8 touch-screen-current-tool) lines-hscrolled) nil))))) +(defun touch-screen-scroll (event) + "Scroll the window within EVENT, a `touchscreen-scroll' event. +If `touch-screen-precision-scroll', scroll the window vertically +by the number of pixels specified within that event. Else, +scroll the window by one line for every +`window-default-line-height' pixels worth of movement. + +If EVENT also specifies horizontal motion and no significant +amount of vertical scrolling has taken place, also scroll the +window horizontally in conjunction with the number of pixels in +the event." + (interactive "e") + (let ((window (nth 1 event)) + (dx (nth 2 event)) + (dy (nth 3 event))) + (with-selected-window window + (touch-screen-handle-scroll dx dy)))) + +(global-set-key [touchscreen-scroll] #'touch-screen-scroll) + (defun touch-screen-handle-timeout (arg) "Start the touch screen timeout or handle it depending on ARG. When ARG is nil, start the `touch-screen-current-timer' to go off @@ -236,19 +269,30 @@ touch-screen-handle-timeout (defun touch-screen-handle-point-update (point) "Notice that the touch point POINT has changed position. +Perform the editing operations or throw to the input translation +function with an input event tied to any gesture that is +recognized. + POINT must be the touch point currently being tracked as `touch-screen-current-tool'. If the fourth element of `touch-screen-current-tool' is nil, then the touch has just begun. Determine how much POINT has moved. If POINT has moved upwards or downwards by a significant amount, -then set the fourth element to `scroll'. Then, call -`touch-screen-handle-scroll' to scroll the display by that -amount. +then set the fourth element to `scroll'. Then, generate a +`touchscreen-scroll' event with the window that POINT was +initially placed upon, and pixel deltas describing how much point +has moved relative to its previous position in the X and Y axes. + +If the fourth element of `touchscreen-current-tool' is `scroll', +then generate a `touchscreen-scroll' event with the window that +qPOINT was initially placed upon, and pixel deltas describing how +much point has moved relative to its previous position in the X +and Y axes. -If the fourth element of `touch-screen-current-tool' is `scroll', -then scroll the display by how much POINT has moved in the Y -axis. +If the fourth element of `touch-screen-current-tool' is +`mouse-drag' and `track-mouse' is non-nil, then generate a +`mouse-movement' event with the position of POINT. If the fourth element of `touch-screen-current-tool' is `held', then the touch has been held down for some time. If motion @@ -275,8 +319,11 @@ touch-screen-handle-point-update 'scroll) (setcar (nthcdr 2 touch-screen-current-tool) relative-xy) - (with-selected-window window - (touch-screen-handle-scroll diff-x diff-y)) + ;; Generate a `touchscreen-scroll' event with `diff-x' + ;; and `diff-y'. + (throw 'input-event + (list 'touchscreen-scroll + window diff-x diff-y)) ;; Cancel the touch screen long-press timer, if it is ;; still there by any chance. (when touch-screen-current-timer @@ -301,8 +348,18 @@ touch-screen-handle-point-update (setcar (nthcdr 2 touch-screen-current-tool) relative-xy) (unless (and (zerop diff-x) (zerop diff-y)) - (with-selected-window window - (touch-screen-handle-scroll diff-x diff-y))))) + (throw 'input-event + ;; Generate a `touchscreen-scroll' event with + ;; `diff-x' and `diff-y'. + (list 'touchscreen-scroll + window diff-x diff-y))))) + ((eq what 'mouse-drag) + ;; There was a `down-mouse-1' event bound at the starting + ;; point of the event. Generate a mouse-motion event if + ;; mouse movement is being tracked. + (when track-mouse + (throw 'input-event (list 'mouse-movement + (cdr point))))) ((eq what 'held) (let* ((posn (cdr point)) (relative-xy @@ -319,7 +376,6 @@ touch-screen-handle-point-update ;; Activate the mark. It should have been set by the ;; time `touch-screen-timeout' was called. (activate-mark) - ;; Figure out what character to go to. If this posn is ;; in the window, go to (posn-point posn). If not, ;; then go to the line before either window start or @@ -385,127 +441,357 @@ touch-screen-window-selection-changed (cancel-timer minibuffer-on-screen-keyboard-timer) (setq minibuffer-on-screen-keyboard-timer nil))))) -(defun touch-screen-handle-point-up (point) +(defun touch-screen-handle-point-up (point prefix) "Notice that POINT has been removed from the screen. POINT should be the point currently tracked as `touch-screen-current-tool'. +PREFIX should be a virtual function key used to look up key +bindings. + +If the fourth element of `touch-screen-current-tool' is nil, move +point to the position of POINT, selecting the window under POINT +as well, and deactivate the mark; if there is a button or link at +POINT, call the command bound to `mouse-2' there. Otherwise, +call the command bound to `mouse-1'. -If the fourth argument of `touch-screen-current-tool' is nil, -move point to the position of POINT, selecting the window under -POINT as well, and deactivate the mark; if there is a button or -link at POINT, call the command bound to `mouse-2' there. -Otherwise, call the command bound to `mouse-1'. +If the fourth element of `touch-screen-current-tool' is +`mouse-drag', then generate either a `mouse-1' or a +`drag-mouse-1' event depending on how far the position of POINT +is from the starting point of the touch. If the command being executed is listed in `touch-screen-set-point-commands' also display the on-screen keyboard if the current buffer and the character at the new point is not read-only." - (let ((what (nth 3 touch-screen-current-tool))) + (let ((what (nth 3 touch-screen-current-tool)) + (posn (cdr point)) window point) (cond ((null what) - (when (windowp (posn-window (cdr point))) + (when (windowp (posn-window posn)) + (setq point (posn-point point) + window (posn-window posn)) ;; Select the window that was tapped. - (select-window (posn-window (cdr point))) + (select-window window) ;; Now simulate a mouse click there. If there is a link ;; or a button, use mouse-2 to push it. - (let ((event (list (if (or (mouse-on-link-p (cdr point)) - (button-at (posn-point (cdr point)))) - 'mouse-2 - 'mouse-1) - (cdr point))) - ;; Look for an extra keymap to look in. - (keymap (and (posn-object (cdr point)) - (stringp - (posn-object (cdr point))) - (get-text-property - 0 'keymap - (posn-object (cdr point))))) - command) - (save-excursion - (when (posn-point (cdr point)) - (goto-char (posn-point (cdr point)))) - (if keymap - (setq keymap (cons keymap (current-active-maps t))) - (setq keymap (current-active-maps t))) - (setq command (lookup-key keymap (vector (car event))))) + (let* ((event (list (if (or (mouse-on-link-p posn) + (and point (button-at point))) + 'mouse-2 + 'mouse-1) + posn)) + ;; Look for the command bound to this event. + (command (key-binding (if prefix + (vector prefix + (car event)) + (vector (car event))) + t nil posn))) (deactivate-mark) - ;; This is necessary for following links. - (goto-char (posn-point (cdr point))) + (when point + ;; This is necessary for following links. + (goto-char point)) ;; Figure out if the on screen keyboard needs to be ;; displayed. (when command - (call-interactively command nil - (vector event)) - (when (memq command touch-screen-set-point-commands) - (if (and (or (not buffer-read-only) - touch-screen-display-keyboard) - ;; Detect the splash screen and avoid - ;; displaying the on screen keyboard - ;; there. - (not (equal (buffer-name) "*GNU Emacs*"))) - ;; Once the on-screen keyboard has been opened, - ;; add `touch-screen-window-selection-changed' - ;; as a window selection change function This - ;; allows the on screen keyboard to be hidden - ;; if the selected window's point becomes read - ;; only at some point in the future. - (progn - (add-hook 'window-selection-change-functions - #'touch-screen-window-selection-changed) - (frame-toggle-on-screen-keyboard (selected-frame) nil)) - ;; Otherwise, hide the on screen keyboard now. - (frame-toggle-on-screen-keyboard (selected-frame) t)))))))))) - -(defun touch-screen-handle-touch (event) + (if (memq command touch-screen-set-point-commands) + (if touch-screen-translate-prompt + ;; When a `mouse-set-point' command is + ;; encountered and + ;; `touch-screen-handle-touch' is being + ;; called from the keyboard command loop, + ;; call it immediately so that point is set + ;; prior to the on screen keyboard being + ;; displayed. + (call-interactively command nil + (vector event)) + (if (and (or (not buffer-read-only) + touch-screen-display-keyboard) + ;; Detect the splash screen and avoid + ;; displaying the on screen keyboard + ;; there. + (not (equal (buffer-name) "*GNU Emacs*"))) + ;; Once the on-screen keyboard has been + ;; opened, add + ;; `touch-screen-window-selection-changed' + ;; as a window selection change function + ;; This allows the on screen keyboard to be + ;; hidden if the selected window's point + ;; becomes read only at some point in the + ;; future. + (progn + (add-hook 'window-selection-change-functions + #'touch-screen-window-selection-changed) + (frame-toggle-on-screen-keyboard (selected-frame) + nil)) + ;; Otherwise, hide the on screen keyboard + ;; now. + (frame-toggle-on-screen-keyboard (selected-frame) t)) + ;; But if it's being called from `describe-key' + ;; or some such, return it as a key sequence. + (throw 'input-event event))) + ;; If not, return the event. + (throw 'input-event event))))) + ((eq what 'mouse-drag) + ;; Generate a corresponding `mouse-1' event. + (let* ((new-window (posn-window posn)) + (new-point (posn-point posn)) + (old-posn (nth 4 touch-screen-current-tool)) + (old-window (posn-window posn)) + (old-point (posn-point posn))) + (throw 'input-event + ;; If the position of the touch point hasn't + ;; changed, or it doesn't start or end on a + ;; window... + (if (and (eq new-window old-window) + (eq new-point old-point) + (windowp new-window) + (windowp old-window)) + ;; ... generate a mouse-1 event... + (list 'mouse-1 posn) + ;; ... otherwise, generate a drag-mouse-1 event. + (list 'drag-mouse-1 (cons old-window + old-posn) + (cons new-window posn))))))))) + +(defun touch-screen-handle-touch (event prefix &optional interactive) "Handle a single touch EVENT, and perform associated actions. -EVENT can either be a touchscreen-begin, touchscreen-update or -touchscreen-end event." - (interactive "e") - (cond - ((eq (car event) 'touchscreen-begin) - ;; A tool was just pressed against the screen. Figure out the - ;; window where it is and make it the tool being tracked on the - ;; window. - (let ((touchpoint (caadr event)) - (position (cdadr event))) - ;; Cancel the touch screen timer, if it is still there by any - ;; chance. - (when touch-screen-current-timer - (cancel-timer touch-screen-current-timer) - (setq touch-screen-current-timer nil)) - ;; Replace any previously ongoing gesture. If POSITION has no - ;; window or position, make it nil instead. - (setq touch-screen-current-tool (and (windowp (posn-window position)) - (posn-point position) - (list touchpoint - (posn-window position) - (posn-x-y position) - nil position nil nil - nil nil))) - ;; Start the long-press timer. - (touch-screen-handle-timeout nil))) - ((eq (car event) 'touchscreen-update) - ;; The positions of tools currently pressed against the screen - ;; have changed. If there is a tool being tracked as part of a - ;; gesture, look it up in the list of tools. - (let ((new-point (assq (car touch-screen-current-tool) - (cadr event)))) - (when new-point - (touch-screen-handle-point-update new-point)))) - ((eq (car event) 'touchscreen-end) - ;; A tool has been removed from the screen. If it is the tool - ;; currently being tracked, clear `touch-screen-current-tool'. - (when (eq (caadr event) (car touch-screen-current-tool)) - ;; Cancel the touch screen long-press timer, if it is still there - ;; by any chance. - (when touch-screen-current-timer - (cancel-timer touch-screen-current-timer) - (setq touch-screen-current-timer nil)) - (touch-screen-handle-point-up (cadr event)) - (setq touch-screen-current-tool nil))))) - -(define-key global-map [touchscreen-begin] #'touch-screen-handle-touch) -(define-key global-map [touchscreen-update] #'touch-screen-handle-touch) -(define-key global-map [touchscreen-end] #'touch-screen-handle-touch) +EVENT can either be a `touchscreen-begin', `touchscreen-update' or +`touchscreen-end' event. +PREFIX is either nil, or a symbol specifying a virtual function +key to apply to EVENT. + +If INTERACTIVE, execute the command associated with any event +generated instead of throwing `input-event'. Otherwise, throw +`input-event' with a single input event if that event should take +the place of EVENT within the key sequence being translated, or +`nil' if all tools have been released." + (interactive "e\ni\np") + (if interactive + ;; Called interactively (probably from wid-edit.el.) + ;; Add any event generated to `unread-command-events'. + (let ((event (catch 'input-event + (touch-screen-handle-touch event prefix) nil))) + (when event + (setq unread-command-events + (nconc unread-command-events + (list event))))) + (cond + ((eq (car event) 'touchscreen-begin) + ;; A tool was just pressed against the screen. Figure out the + ;; window where it is and make it the tool being tracked on the + ;; window. + (let* ((touchpoint (caadr event)) + (position (cdadr event)) + (window (posn-window position)) + (point (posn-point position))) + ;; Cancel the touch screen timer, if it is still there by any + ;; chance. + (when touch-screen-current-timer + (cancel-timer touch-screen-current-timer) + (setq touch-screen-current-timer nil)) + ;; Replace any previously ongoing gesture. If POSITION has no + ;; window or position, make it nil instead. + (setq touch-screen-current-tool (and (windowp window) + (list touchpoint window + (posn-x-y position) + nil position + nil nil nil nil))) + ;; Determine if there is a command bound to `down-mouse-1' at + ;; the position of the tap and that command is not a command + ;; whose functionality is replaced by the long-press mechanism. + ;; If so, set the fourth element of `touch-screen-current-tool' + ;; to `mouse-drag' and generate an emulated `mouse-1' event. + (if (and touch-screen-current-tool + (with-selected-window window + (let ((binding (key-binding (if prefix + (vector prefix + 'down-mouse-1) + [down-mouse-1]) + t nil position))) + (and binding + (not (and (symbolp binding) + (get binding 'ignored-mouse-command))))))) + (progn (setcar (nthcdr 3 touch-screen-current-tool) + 'mouse-drag) + (throw 'input-event (list 'down-mouse-1 position))) + (and point + ;; Start the long-press timer. + (touch-screen-handle-timeout nil))))) + ((eq (car event) 'touchscreen-update) + ;; The positions of tools currently pressed against the screen + ;; have changed. If there is a tool being tracked as part of a + ;; gesture, look it up in the list of tools. + (let ((new-point (assq (car touch-screen-current-tool) + (cadr event)))) + (when new-point + (touch-screen-handle-point-update new-point)))) + ((eq (car event) 'touchscreen-end) + ;; A tool has been removed from the screen. If it is the tool + ;; currently being tracked, clear `touch-screen-current-tool'. + (when (eq (caadr event) (car touch-screen-current-tool)) + ;; Cancel the touch screen long-press timer, if it is still there + ;; by any chance. + (when touch-screen-current-timer + (cancel-timer touch-screen-current-timer) + (setq touch-screen-current-timer nil)) + (unwind-protect + (touch-screen-handle-point-up (cadr event) prefix) + ;; Make sure the tool list is cleared even if + ;; `touch-screen-handle-point-up' throws. + (setq touch-screen-current-tool nil))) + ;; Throw to the key translation function. + (throw 'input-event nil))))) + +;; Mark `mouse-drag-region' as ignored for the purposes of mouse click +;; emulation. + +(put 'mouse-drag-region 'ignored-mouse-command t) + +(defun touch-screen-translate-touch (prompt) + "Translate touch screen events into a sequence of mouse events. +PROMPT is the prompt string given to `read-key-sequence', or nil +if this function is being called from the keyboard command loop. +Value is a new key sequence. + +Read the touch screen event within `current-key-remap-sequence' +and give it to `touch-screen-handle-touch'. Return any key +sequence signaled. + +If `touch-screen-handle-touch' does not signal for an event to be +returned after the last element of the key sequence is read, +continue reading touch screen events until +`touch-screen-handle-touch' signals. Return a sequence +consisting of the first event encountered that is not a touch +screen event. + +In addition to non-touchscreen events read, key sequences +returned may contain any one of the following events: + + (touchscreen-scroll WINDOW DX DY) + +where WINDOW specifies a window to scroll, and DX and DY are +integers describing how many pixels to be scrolled horizontally +and vertically. + + (down-mouse-1 POSN) + (drag-mouse-1 POSN) + +where POSN is the position of the mouse button press or click. + + (mouse-1 POSN) + (mouse-2 POSN) + +where POSN is the position of the mouse click, either `mouse-2' +if POSN is on a link or a button, or `mouse-1' otherwise." + (if (> (length current-key-remap-sequence) 0) + ;; Save the virtual function key if this is a mode line event. + (let* ((prefix (and (> (length current-key-remap-sequence) 1) + (aref current-key-remap-sequence 0))) + (touch-screen-translate-prompt prompt) + (event (catch 'input-event + ;; First, process the one event already within + ;; `current-key-remap-sequence'. + (touch-screen-handle-touch + (aref current-key-remap-sequence + (if prefix 1 0)) + prefix) + ;; Next, continue reading input events. + (while t + (let ((event1 (read-event))) + ;; If event1 is a virtual function key, make + ;; it the new prefix. + (if (memq event1 '(mode-line tab-line + header-line tool-bar tab-bar + left-fringe right-fringe + left-margin right-margin + right-divider bottom-divider)) + (setq prefix event1) + ;; If event1 is not a touch screen event, return + ;; it. + (if (not (memq (car-safe event1) + '(touchscreen-begin + touchscreen-end + touchscreen-update))) + (throw 'input-event event1) + ;; Process this event as well. + (touch-screen-handle-touch event1 prefix)))))))) + ;; Return a key sequence consisting of event + ;; or an empty vector if it is nil, meaning that + ;; no key events have been translated. + (if event (or (and prefix (consp event) + ;; If this is a mode line event, then generate + ;; the appropriate function key. + (vector prefix event)) + (vector event)) + "")))) + +(define-key function-key-map [touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [touchscreen-update] + #'touch-screen-translate-touch) +(define-key function-key-map [touchscreen-end] + #'touch-screen-translate-touch) + +(define-key function-key-map [mode-line touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [mode-line touchscreen-update] + #'touch-screen-translate-touch) +(define-key function-key-map [mode-line touchscreen-end] + #'touch-screen-translate-touch) + +(define-key function-key-map [header-line touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [header-line touchscreen-update] + #'touch-screen-translate-touch) +(define-key function-key-map [header-line touchscreen-end] + #'touch-screen-translate-touch) + +(define-key function-key-map [bottom-divider touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [bottom-divider touchscreen-update] + #'touch-screen-translate-touch) +(define-key function-key-map [bottom-divider touchscreen-end] + #'touch-screen-translate-touch) + +(define-key function-key-map [right-divider touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [right-divider touchscreen-update] + #'touch-screen-translate-touch) +(define-key function-key-map [right-divider touchscreen-end] + #'touch-screen-translate-touch) + +(define-key function-key-map [right-divider touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [right-divider touchscreen-update] + #'touch-screen-translate-touch) +(define-key function-key-map [right-divider touchscreen-end] + #'touch-screen-translate-touch) + +(define-key function-key-map [left-fringe touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [left-fringe touchscreen-update] + #'touch-screen-translate-touch) +(define-key function-key-map [left-fringe touchscreen-end] + #'touch-screen-translate-touch) + +(define-key function-key-map [right-fringe touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [right-fringe touchscreen-update] + #'touch-screen-translate-touch) +(define-key function-key-map [right-fringe touchscreen-end] + #'touch-screen-translate-touch) + +(define-key function-key-map [left-margin touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [left-margin touchscreen-update] + #'touch-screen-translate-touch) +(define-key function-key-map [left-margin touchscreen-end] + #'touch-screen-translate-touch) + +(define-key function-key-map [right-margin touchscreen-begin] + #'touch-screen-translate-touch) +(define-key function-key-map [right-margin touchscreen-update] + #'touch-screen-translate-touch) +(define-key function-key-map [right-margin touchscreen-end] + #'touch-screen-translate-touch) ;; Exports. These functions are intended for use externally. @@ -582,149 +868,6 @@ touch-screen-track-drag -;; Modeline dragging. - -(defun touch-screen-drag-mode-line-1 (event) - "Internal helper for `touch-screen-drag-mode-line'. -This is called when that function determines that no drag really -happened. EVENT is the same as in `touch-screen-drag-mode-line'." - ;; If there is an object at EVENT, then look either a keymap bound - ;; to [down-mouse-1] or a command bound to [mouse-1]. Then, if a - ;; keymap was found, pop it up as a menu. Otherwise, wait for a tap - ;; to complete and run the command found. - ;; Also, select the window in EVENT. - (select-window (posn-window (cdadr event))) - (let* ((object (posn-object (cdadr event))) - (object-keymap (and (consp object) - (stringp (car object)) - (or (get-text-property (cdr object) - 'keymap - (car object)) - (get-text-property (cdr object) - 'local-map - (car object))))) - (keymap (lookup-key object-keymap [mode-line down-mouse-1])) - (command (or (lookup-key object-keymap [mode-line mouse-1]) - keymap))) - (when (or (keymapp keymap) command) - (if (keymapp keymap) - (when-let* ((command (x-popup-menu event keymap)) - (tem (lookup-key keymap - (if (consp command) - (apply #'vector command) - (vector command)) - t))) - (call-interactively tem)) - (when (commandp command) - (call-interactively command nil - (vector (list 'mouse-1 (cdadr event))))))))) - -(defun touch-screen-drag-mode-line (event) - "Begin dragging the mode line in response to a touch EVENT. -Change the height of the window based on where the touch point in -EVENT moves. - -If it does not actually move anywhere and the touch point is -removed, and EVENT lies on top of text with a mouse command -bound, run that command instead." - (interactive "e") - ;; Find the window that should be dragged and the starting position. - (let* ((window (posn-window (cdadr event))) - (relative-xy (touch-screen-relative-xy (cdadr event) - 'frame)) - (last-position (cdr relative-xy))) - (when (window-resizable window 0) - (when (eq - (touch-screen-track-drag - event (lambda (new-event &optional _data) - ;; Find the position of the touchpoint in - ;; NEW-EVENT. - (let* ((touchpoint (assq (caadr event) - (cadr new-event))) - (new-relative-xy - (touch-screen-relative-xy (cdr touchpoint) 'frame)) - (position (cdr new-relative-xy)) - (window-resize-pixelwise t) - growth) - ;; Now set the new height of the window. If - ;; new-relative-y is above relative-xy, then - ;; make the window that much shorter. - ;; Otherwise, make it bigger. - (unless (or (zerop (setq growth - (- position last-position))) - (and (> growth 0) - (< position - (+ (window-pixel-top window) - (window-pixel-height window)))) - (and (< growth 0) - (> position - (+ (window-pixel-top window) - (window-pixel-height window))))) - (when (ignore-errors - (adjust-window-trailing-edge window growth nil t) t) - (setq last-position position)))))) - 'no-drag) - ;; Dragging did not actually happen, so try to run any command - ;; necessary. - (touch-screen-drag-mode-line-1 event))))) - -(global-set-key [mode-line touchscreen-begin] - #'touch-screen-drag-mode-line) -(global-set-key [bottom-divider touchscreen-begin] - #'touch-screen-drag-mode-line) - - - -;; Header line tapping. - -(defun touch-screen-tap-header-line (event) - "Handle a `touchscreen-begin' EVENT on the header line. -Wait for the tap to complete, then run any command bound to -`mouse-1' at the position of EVENT. - -If another keymap is bound to `down-mouse-1', then display a menu -with its contents instead, and run the selected command." - (interactive "e") - (let* ((posn (cdadr event)) - (object (posn-object posn)) - ;; Look for the keymap defined by the object itself. - (object-keymap (and (consp object) - (stringp (car object)) - (or (get-text-property (cdr object) - 'keymap - (car object)) - (get-text-property (cdr object) - 'local-map - (car object))))) - command keymap) - ;; Now look for either a command bound to `mouse-1' or a keymap - ;; bound to `down-mouse-1'. - (with-selected-window (posn-window posn) - (setq command (lookup-key object-keymap - [header-line mouse-1] t) - keymap (lookup-key object-keymap - [header-line down-mouse-1] t)) - (unless (keymapp keymap) - (setq keymap nil))) - ;; Wait for the tap to complete. - (when (touch-screen-track-tap event) - ;; Select the window whose header line was clicked. - (with-selected-window (posn-window posn) - (if keymap - (when-let* ((command (x-popup-menu event keymap)) - (tem (lookup-key keymap - (if (consp command) - (apply #'vector command) - (vector command)) - t))) - (call-interactively tem)) - (when (commandp command) - (call-interactively command nil - (vector (list 'mouse-1 (cdadr event)))))))))) - -(global-set-key [header-line touchscreen-begin] - #'touch-screen-tap-header-line) - (provide 'touch-screen) ;;; touch-screen ends here diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el index 4df1fb7ab08..fa801cab51b 100644 --- a/lisp/wid-edit.el +++ b/lisp/wid-edit.el @@ -1193,8 +1193,7 @@ widget-button-click ;; up event. (cond ((eq (car event) 'touchscreen-begin) - (setq command (lookup-key widget-global-map - [touchscreen-begin]))) + (setq command 'touch-screen-handle-touch)) (mouse-1 (cond ((setq command ;down event (lookup-key widget-global-map [down-mouse-1])) (setq up nil)) @@ -1213,6 +1212,11 @@ widget-button-click (call-interactively command))))) (message "You clicked somewhere weird."))) +;; Make sure `touch-screen-handle-touch' abstains from emulating +;; down-mouse-1 events for `widget-button-click'. + +(put 'widget-button-click 'ignored-mouse-command t) + (defun widget-button-press (pos &optional event) "Invoke button at POS." (interactive "@d") diff --git a/src/keyboard.c b/src/keyboard.c index ea07c538aa2..e10128def13 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -9994,13 +9994,18 @@ active_maps (Lisp_Object first_event, Lisp_Object second_event) If the mapping is a function and DO_FUNCALL is true, the function is called with PROMPT as parameter and its return value is used as the return value of this function (after checking - that it is indeed a vector). */ + that it is indeed a vector). + + START and END are the indices of the first and last key of the + sequence being remapped within the keyboard buffer KEYBUF. */ static Lisp_Object access_keymap_keyremap (Lisp_Object map, Lisp_Object key, Lisp_Object prompt, - bool do_funcall) + bool do_funcall, ptrdiff_t start, ptrdiff_t end, + Lisp_Object *keybuf) { Lisp_Object next; + specpdl_ref count; next = access_keymap (map, key, 1, 0, 1); @@ -10016,10 +10021,18 @@ access_keymap_keyremap (Lisp_Object map, Lisp_Object key, Lisp_Object prompt, its value instead. */ if (do_funcall && FUNCTIONP (next)) { - Lisp_Object tem; + Lisp_Object tem, remap; tem = next; - next = call1 (next, prompt); + /* Build Vcurrent_key_remap_sequence. */ + remap = Fvector (end - start + 1, keybuf + start); + + /* Bind `current-key-remap-sequence' to the key sequence being + remapped. */ + count = SPECPDL_INDEX (); + specbind (Qcurrent_key_remap_sequence, remap); + next = unbind_to (count, call1 (next, prompt)); + /* If the function returned something invalid, barf--don't ignore it. */ if (! (NILP (next) || VECTORP (next) || STRINGP (next))) @@ -10044,11 +10057,17 @@ keyremap_step (Lisp_Object *keybuf, volatile keyremap *fkey, int input, bool doit, int *diff, Lisp_Object prompt) { Lisp_Object next, key; + ptrdiff_t buf_start, buf_end; + + /* Save the key sequence being translated. */ + buf_start = fkey->start; + buf_end = fkey->end; key = keybuf[fkey->end++]; if (KEYMAPP (fkey->parent)) - next = access_keymap_keyremap (fkey->map, key, prompt, doit); + next = access_keymap_keyremap (fkey->map, key, prompt, doit, + buf_start, buf_end, keybuf); else next = Qnil; @@ -12479,6 +12498,7 @@ init_keyboard (void) {SYMBOL_INDEX (Qselect_window), SYMBOL_INDEX (Qswitch_frame)}, /* Touchscreen events should be prefixed by the posn. */ {SYMBOL_INDEX (Qtouchscreen_begin), SYMBOL_INDEX (Qtouchscreen)}, + {SYMBOL_INDEX (Qtouchscreen_end), SYMBOL_INDEX (Qtouchscreen)}, }; static Lisp_Object @@ -13575,6 +13595,15 @@ syms_of_keyboard (void) key has been read inside `read-key-sequence'. */); disable_inhibit_text_conversion = false; + DEFVAR_LISP ("current-key-remap-sequence", + Vcurrent_key_remap_sequence, + doc: /* The key sequence currently being remap, or nil. +Bound to a vector containing the sub-sequence matching a binding +within `input-decode-map' or `local-function-key-map' when its bound +function is called to remap that sequence. */); + Vcurrent_key_remap_sequence = Qnil; + DEFSYM (Qcurrent_key_remap_sequence, "current-key-remap-sequence"); + pdumper_do_now_and_after_load (syms_of_keyboard_for_pdumper); } commit d78d7aa78391c84e3d5536514d245d844c08d43d Merge: e9a39fd8983 c5fa58cbc4a Author: Po Lu Date: Sun Jul 16 08:18:13 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit e9a39fd89831e83708928409ec473b493845196f Author: Po Lu Date: Sat Jul 15 09:53:22 2023 +0800 Update Android port * doc/emacs/android.texi (Android): Add new node to menu. (Android Environment): Add footnote pointing to new node. (Android Software): New node. * doc/emacs/emacs.texi (Top): Add new node to menu. * java/AndroidManifest.xml.in (manifest): Fix location of sharedUserId property. * java/INSTALL: Improve documentation of shared user ID support. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 3e27c019257..df73ee60c5d 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -21,6 +21,7 @@ Android * Android Windowing:: The Android window system. * Android Fonts:: Font selection under Android. * Android Troubleshooting:: Dealing with problems. +* Android Software:: Getting extra software. @end menu @node What is Android? @@ -254,7 +255,12 @@ Android Environment Emacs, the system has an overwhelming number of users. Each application runs in its own user, with its home directory set -to its app data directory (@pxref{Android File System}.) +to its app data directory (@pxref{Android File +System}.)@footnote{Except in cases where a ``shared user ID'' is +specified and other applications signed using the same ``package +signing key'' are installed, in which case Emacs runs as the same user +and has access to the same files as each of the aformentioned +applications.} Each application is also prohibited from accessing many system directories and the app data directories of other applications. @@ -663,3 +669,40 @@ Android Troubleshooting If you can find out how to open that documents provider in the file manager that comes with your device, you can rename, delete, or edit your initialization or dump files from there instead. + +@node Android Software +@section Installing extra software on Android +@cindex installing extra software on Android +@cindex installing Unix software on Android + + Android includes an extremely limited set of Unix-like command line +tools in a default installation. Several projects exist to argument +this selection, providing options that range from improved +reproductions of Unix command-line utilities to package repositories +containing extensive collections of free GNU and Unix software. + + @uref{http://busybox.net, Busybox} provides Unix utilities and +limited replicas of certain popular GNU programs such as +@command{wget} in a single statically-linked Linux binary, which is +capable of running under Android. + + @uref{https://termux.dev, Termux} provides a package manager based +on the Debian project's @command{dpkg} system and a set of package +repositories containing substantial amounts of free software for Unix +systems, including compilers, debuggers, and runtimes for languages +such as C, C++, Java, Python and Common Lisp. These packages are +normally installed from within a purpose-built terminal emulator +application, but Emacs can access them if it is built with the same +application signing key as the Termux terminal emulator, and with its +``shared user ID'' set to the package name of the terminal emulator +program. The @file{java/INSTALL} within the Emacs distribution +explains how to build Emacs in this fashion. + + @uref{https://github.com/termux/termux-packages, termux-packages} +provides the package definitions that are used by Termux to generate +their package repositories, which may also be independently compiled +for installation within Emacs's home directory. + + In addition to the projects mentioned above, statically linked +binaries for most Linux kernel-based systems can also be run on +Android. diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi index 7ce1a53f934..92be9f9b9a9 100644 --- a/doc/emacs/emacs.texi +++ b/doc/emacs/emacs.texi @@ -1271,6 +1271,7 @@ Top * Android Windowing:: The Android window system. * Android Fonts:: Font selection under Android. * Android Troubleshooting:: Dealing with problems. +* Android Software:: Getting extra software. Emacs and unconventional input devices diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in index 3aae2032fff..895e7f88c57 100644 --- a/java/AndroidManifest.xml.in +++ b/java/AndroidManifest.xml.in @@ -25,6 +25,8 @@ along with GNU Emacs. If not, see . --> android:targetSandboxVersion="1" android:installLocation="auto" android:requestLegacyExternalStorage="true" + @ANDROID_SHARED_USER_ID@ + @ANDROID_SHARED_USER_NAME@ android:versionCode="@emacs_major_version@" android:versionName="@version@"> @@ -74,8 +76,7 @@ along with GNU Emacs. If not, see . --> android:supportsRtl="true" android:theme="@style/EmacsStyle" android:debuggable="@ANDROID_DEBUGGABLE@" - @ANDROID_SHARED_USER_ID@ - @ANDROID_SHARED_USER_NAME@ + android:allowBackup="true" android:extractNativeLibs="true"> Date: Sat Jul 15 07:54:24 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 11c8a2fa87df2b6cc505e670a972552750eab71f Author: Po Lu Date: Fri Jul 14 20:30:30 2023 +0800 Make --with-shared-user-id work * configure.ac (ANDROID_SHARED_USER_NAME): New variable. Substitute it. * java/AndroidManifest.xml.in: Set `sharedUserLabel' if a shared user ID is enabled. * java/res/values/strings.xml (shared_user_name): New string resource. diff --git a/configure.ac b/configure.ac index 86abf54911e..5e5c6a88323 100644 --- a/configure.ac +++ b/configure.ac @@ -844,6 +844,7 @@ AC_DEFUN ANDROID_ABI= WARN_JAVAFLAGS= ANDROID_SHARED_USER_ID= +ANDROID_SHARED_USER_NAME= # This is a list of Makefiles that have alternative versions for # Android. @@ -2656,7 +2657,12 @@ AC_DEFUN emacs_val=`AS_ECHO(["$with_shared_user_id"]) \ | sed -e 's/"/\\"/'` emacs_val="\"$emacs_val\"" - ANDROID_SHARED_USER_ID="android:sharedUserId=$emacs_val"])],[ + ANDROID_SHARED_USER_ID="android:sharedUserId=$emacs_val" + # `android:sharedUserName' is required for sharedUserID to work + # on recent Android releases. It does not otherwise affect the + # behavior of any code. + emacs_val="\"@string/shared_user_name\"" + ANDROID_SHARED_USER_NAME="android:sharedUserLabel=$emacs_val"])],[ # Emacs will be built as a shared library, and a wrapper around it # will also be built for the benefit of applications. This # requires Emacs be built as a position independent executable. @@ -2698,6 +2704,7 @@ AC_DEFUN AC_SUBST([ANDROID_LDFLAGS]) AC_SUBST([ANDROID_BUILD_CFLAGS]) AC_SUBST([ANDROID_SHARED_USER_ID]) +AC_SUBST([ANDROID_SHARED_USER_NAME]) if test "${with_pgtk}" = "yes"; then window_system=pgtk diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in index e79fb4e46e7..3aae2032fff 100644 --- a/java/AndroidManifest.xml.in +++ b/java/AndroidManifest.xml.in @@ -75,6 +75,7 @@ along with GNU Emacs. If not, see . --> android:theme="@style/EmacsStyle" android:debuggable="@ANDROID_DEBUGGABLE@" @ANDROID_SHARED_USER_ID@ + @ANDROID_SHARED_USER_NAME@ android:extractNativeLibs="true"> Remove the dumped state created when Emacs was installed. + + + + Emacs shared user + commit cb4ea3e7fb36d942dd9d495462fc591f54707b40 Author: Po Lu Date: Fri Jul 14 15:35:58 2023 +0800 Update Android port * src/android.c (android_blit_copy): Don't check for overflow where not required. diff --git a/src/android.c b/src/android.c index f8ad78a2b39..acc49219b35 100644 --- a/src/android.c +++ b/src/android.c @@ -4382,13 +4382,16 @@ android_blit_copy (int src_x, int src_y, int width, int height, overlap. Calculate the coordinate of the last pixel of the last row in both src and dst. */ - overflow = ckd_mul (&start, src_y + height - 1, - src_info->stride); - if (mask) /* If a mask is set, put the pointers before the end - of the row. */ + overflow = ckd_mul (&start, src_y + height - 1, + src_info->stride); + + if (mask) + /* If a mask is set, put the pointers before the end of the + row. */ overflow |= ckd_mul (&end, src_x + width - 1, pixel); else - overflow |= ckd_mul (&end, src_x, pixel); + end = src_x * pixel; + overflow |= ckd_add (&start, start, end); overflow |= ckd_add (&start, (uintptr_t) src, start); @@ -4397,13 +4400,16 @@ android_blit_copy (int src_x, int src_y, int width, int height, src_current = (unsigned char *) start; - overflow = ckd_mul (&start, dst_y + height - 1, - dst_info->stride); - if (mask) /* If a mask is set, put the pointers before the end - of the row. */ + overflow = ckd_mul (&start, dst_y + height - 1, + dst_info->stride); + + if (mask) + /* If a mask is set, put the pointers before the end of the + row. */ overflow |= ckd_mul (&end, dst_x + width - 1, pixel); else - overflow |= ckd_mul (&end, dst_x, pixel); + end = dst_x * pixel; + overflow |= ckd_add (&start, start, end); overflow |= ckd_add (&start, (uintptr_t) dst, start); commit 4f95ab3837d5ccd75a934148b3ea767791438934 Merge: 4c390f14f4f 2df086d121e Author: Po Lu Date: Fri Jul 14 15:35:21 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 4c390f14f4f344ce63c04fface5c8a2a11061412 Author: Po Lu Date: Fri Jul 14 08:51:07 2023 +0800 Clean up Android debug code * java/org/gnu/emacs/EmacsInputConnection.java (getSurroundingText): Don't print debug information if DEBUG_IC is off. diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index f8dce5dfa79..c3764a7b29f 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -587,7 +587,7 @@ public final class EmacsInputConnection implements InputConnection text = EmacsNative.getSurroundingText (windowHandle, beforeLength, afterLength, flags); - if (text != null) + if (EmacsService.DEBUG_IC && text != null) Log.d (TAG, ("getSurroundingText: " + text.getSelectionStart () + "," commit b67a23f3e32927df7c3f6ab27944f86856050e6a Author: Po Lu Date: Fri Jul 14 08:05:21 2023 +0800 ; * lisp/calc/calc.el (calc): Fix typo. diff --git a/lisp/calc/calc.el b/lisp/calc/calc.el index c0e68ceab0c..652cb8c1a88 100644 --- a/lisp/calc/calc.el +++ b/lisp/calc/calc.el @@ -1458,7 +1458,7 @@ calc ;; selected window is read only, and no on screen keyboard should ;; be displayed. Make sure that any active on screen keyboard is ;; not hidden by accident. - (let ((touch-screen-display-buffer t)) + (let ((touch-screen-display-keyboard t)) (when (get-buffer-window "*Calc Keypad*") (calc-keypad) (set-buffer (window-buffer))) commit a65960c5b9cc48496fb4e6d3759ada18c14a8eac Merge: 3615bdd5bcf 3ffb99f28f2 Author: Po Lu Date: Fri Jul 14 07:58:49 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 3615bdd5bcf28674861f1bb7aa9b1ca5fa6d57a6 Author: Po Lu Date: Thu Jul 13 19:19:57 2023 +0800 Add a Doc View tool bar * etc/NEWS: Announce the new tool bar. * etc/images/last-page.xpm: * etc/images/last-page.pbm: New images. Mirrored from next-page.xpm. * lisp/doc-view.el (doc-view-menu): Use `doc-view-new-search' instead of an anonymous function. (doc-view-tool-bar-map): New keymap. (doc-view-search): Update the tool bar after initiating a search. (doc-view-new-search): New command. (doc-view-mode): Set the tool bar map appropriately. diff --git a/etc/NEWS b/etc/NEWS index db4fa9f403e..8150c71af11 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -360,6 +360,12 @@ this new face when DocView displays documents, customize this face to restore the colors you were used to, or to get colors more to your liking. +--- +*** DocView buffers now display a new tool bar. +This tool bar contains options for searching and navigating within the +document, replacing the incompatible items for incremental search and +editing within the default tool bar displayed in the past. + ** Shortdoc +++ diff --git a/etc/images/last-page.pbm b/etc/images/last-page.pbm new file mode 100644 index 00000000000..d25ce022183 Binary files /dev/null and b/etc/images/last-page.pbm differ diff --git a/etc/images/last-page.xpm b/etc/images/last-page.xpm new file mode 100644 index 00000000000..5704143aa2e --- /dev/null +++ b/etc/images/last-page.xpm @@ -0,0 +1,122 @@ +/* XPM */ +static char *last_page[] = { +/* columns rows colors chars-per-pixel */ +"24 24 92 1 ", +" c None", +". c black", +"X c gray15", +"o c #2F4050", +"O c #344353", +"+ c #3B4F63", +"@ c #384F66", +"# c #3A5067", +"$ c #3C5064", +"% c #3C5065", +"& c #3E5166", +"* c #3F5266", +"= c #3A5168", +"- c #3B5269", +"; c #3D526A", +": c #3E546A", +"> c #3F556B", +", c #3E5975", +"< c #3F5A76", +"1 c #464646", +"2 c #494949", +"3 c #405367", +"4 c #405468", +"5 c #40566C", +"6 c #41576D", +"7 c #42586E", +"8 c #44586F", +"9 c #45596F", +"0 c #465B70", +"q c #415B77", +"w c #425C78", +"e c #435E79", +"r c #445F7A", +"t c #46607B", +"y c #47617B", +"u c #47617C", +"i c #48627D", +"p c #49637D", +"a c #4B647E", +"s c #4C647F", +"d c #4C657F", +"f c gray38", +"g c #6A6A6A", +"h c #616A73", +"j c #68727D", +"k c #7C7C7C", +"l c #4E6780", +"z c #4F6881", +"x c #506982", +"c c #526A83", +"v c #556D85", +"b c #5B7289", +"n c #7D8185", +"m c #77838F", +"M c #868788", +"N c #888888", +"B c #8B8B8B", +"V c #8F9296", +"C c #8F9396", +"Z c #8F9397", +"A c #909397", +"S c #959595", +"D c #91969C", +"F c #91979C", +"G c #92979C", +"H c #92979D", +"J c #9C9FA1", +"K c #9D9FA2", +"L c #A2A3A4", +"P c #A6A6A6", +"I c #ACACAC", +"U c gray68", +"Y c #B0B0B0", +"T c #B2B2B2", +"R c gray71", +"E c #B6B6B6", +"W c gray75", +"Q c #C5C5C5", +"! c gray79", +"~ c gray80", +"^ c LightGray", +"/ c #D6D6D6", +"( c #D8D8D8", +") c #DADADA", +"_ c #DEDEDE", +"` c gray89", +"' c #E5E5E5", +"] c #E6E6E6", +"[ c #EEEEEE", +"{ c #F2F2F2", +"} c #F6F6F6", +"| c white", +/* pixels */ +" ", +" ", +" ........ ........ ", +" .vU/_][`!N2gNT~)]{|b. ", +" .@T/_][}||PUTW~)]{|t. ", +" .#T/_][}`|PUTW~)]{|t. ", +" .-T/_][).|PUTW~)]{|t. ", +" .-T/_]^..|PUTW~)]{|t. ", +" .;T/_~...|PUTW~)]{|u. ", +" .>T/Q.....X1fkSR]{|i. ", +" .>T/_~...|PUTW~)]{|i. ", +" .5T/_]^..|PUTW~)]{|a. ", +" .5T/_][).|PUTW~)]{|d. ", +" .7T/_][}`|PUTW~)]{|d. ", +" .8T/_][}||PUTW~)]{|l. ", +" .8R/_][}||PUTW~)]{|z. ", +" .0MAZZZZZKPLHHHHHKmc. ", +" .O43&&&&+hnjtrwwq< (doc-view-current-page) 1) + :help "Move to the next page.") + (tool-bar-local-item-from-menu 'doc-view-next-page "next-page" + map doc-view-mode-map :vert-only t + :enable '(< (doc-view-current-page) + (doc-view-last-page-number)) + :help "Move to the next page.") + map) + "Like the default `tool-bar-map', but with additions for DocView.") + ;;;; Navigation Commands (defun doc-view-last-page-number () @@ -1863,7 +1902,16 @@ doc-view-search ;; We must convert to TXT first! (if doc-view--current-converter-processes (message "DocView: please wait till conversion finished.") - (doc-view-doc->txt txt (lambda () (doc-view-search nil)))))))) + (doc-view-doc->txt txt (lambda () (doc-view-search nil)))))) + ;; Update the tool bar items. + (force-mode-line-update))) + +(defun doc-view-new-search () + "Initiate a new search query. +Prompt for a string, then search for its appearances within +the document text." + (interactive) + (doc-view-search t nil)) (defun doc-view-search-next-match (arg) "Go to the ARGth next matching page." @@ -2238,6 +2286,8 @@ doc-view-mode major-mode 'doc-view-mode) (doc-view-imenu-setup) (doc-view-initiate-display) + ;; Replace the tool bar map with `doc-view-tool-bar-map'. + (setq-local tool-bar-map doc-view-tool-bar-map) ;; Switch off view-mode explicitly, because doc-view-mode is the ;; canonical view mode for PDF/PS/DVI files. This could be ;; switched on automatically depending on the value of commit ae9f1a075c3a5f5bd0425828b6144f97265d8794 Author: Po Lu Date: Thu Jul 13 18:17:59 2023 +0800 Improve workaround for partial texture updates on Android * java/AndroidManifest.xml.in: * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Don't change hardware acceleration state. * java/org/gnu/emacs/EmacsNative.java (notifyPixelsChanged): New function. * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): New field `bitmapChanged'. (copyToFrontBuffer): Signal that the bitmap has changed. (onDraw): If the bitmap has changed, increment the generation ID. * src/android.c (JNICALL): Implement new function. diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in index f2aede7369c..e79fb4e46e7 100644 --- a/java/AndroidManifest.xml.in +++ b/java/AndroidManifest.xml.in @@ -77,14 +77,10 @@ along with GNU Emacs. If not, see . --> @ANDROID_SHARED_USER_ID@ android:extractNativeLibs="true"> - - @@ -177,7 +173,6 @@ along with GNU Emacs. If not, see . --> = Build.VERSION_CODES.HONEYCOMB) - { - flag = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; - window = dialog.getWindow (); - window.addFlags (flag); - } - return dialog; } diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 5b8b0f1eae5..1331539879a 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -249,6 +249,11 @@ public static native SurroundingText getSurroundingText (short window, public static native void blitRect (Bitmap src, Bitmap dest, int x1, int y1, int x2, int y2); + /* Increment the generation ID of the specified BITMAP, forcing its + texture to be re-uploaded to the GPU. */ + + public static native void notifyPixelsChanged (Bitmap bitmap); + static { /* Older versions of Android cannot link correctly with shared diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index 54fe70e1634..c47696b35c0 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -42,6 +42,10 @@ public final class EmacsSurfaceView extends View /* The complete buffer contents at the time of the last draw. */ private Bitmap frontBuffer; + /* Whether frontBuffer has been updated since the last call to + `onDraw'. */ + private boolean bitmapChanged; + /* Canvas representing the front buffer. */ private Canvas bitmapCanvas; @@ -105,6 +109,9 @@ public final class EmacsSurfaceView extends View bitmap.getWidth (), bitmap.getHeight ()); } + + /* See the large comment inside `onDraw'. */ + bitmapChanged = true; } private void @@ -176,27 +183,41 @@ else if (bitmap != null) onDraw (Canvas canvas) { /* Paint the view's bitmap; the bitmap might be recycled right - now. - - Hardware acceleration is disabled in AndroidManifest.xml to - prevent Android from uploading the front buffer to the GPU from - a separate thread. This is important for two reasons: first, - the GPU command queue uses a massive amount of memory (dozens - of MiB) to upload bitmaps to the GPU, regardless of how much of - the bitmap has actually changed. - - Secondly, asynchronous texturization leads to race conditions - when a buffer swap occurs before the front buffer is fully - uploaded to the GPU. Normally, only slight and tolerable - tearing should result from this behavior, but Android does not - properly interlock the ``generation ID'' used to avoid - texturizing unchanged bitmaps with the bitmap contents, - consequentially leading to textures in an incomplete state - remaining in use to the GPU if a buffer swap happens between - the image data being uploaded and the ``generation ID'' being - read. */ + now. */ if (frontBuffer != null) - canvas.drawBitmap (frontBuffer, 0f, 0f, uiThreadPaint); + { + /* The first time the bitmap is drawn after a buffer swap, + mark its contents as having changed. This increments the + ``generation ID'' used by Android to avoid uploading buffer + textures for unchanged bitmaps. + + When a buffer swap takes place, the bitmap is initially + updated from the Emacs thread, resulting in the generation + ID being increased. If the render thread is texturizing + the bitmap while the swap takes place, it might record the + generation ID after the update for a texture containing the + contents of the bitmap prior to the swap, leaving the + texture tied to the bitmap partially updated. + + Android never calls `onDraw' if the render thread is still + processing the bitmap. Update the generation ID here to + ensure that a new texture will be uploaded if the bitmap + has changed. + + Uploading the bitmap contents to the GPU uses an excessive + amount of memory, as the entire bitmap is placed into the + graphics command queue, but this memory is actually shared + among all other applications and reclaimed by the system + when necessary. */ + + if (bitmapChanged) + { + EmacsNative.notifyPixelsChanged (frontBuffer); + bitmapChanged = false; + } + + canvas.drawBitmap (frontBuffer, 0f, 0f, uiThreadPaint); + } } }; diff --git a/src/android.c b/src/android.c index 5eb6f65419c..f8ad78a2b39 100644 --- a/src/android.c +++ b/src/android.c @@ -3133,6 +3133,23 @@ NATIVE_NAME (blitRect) (JNIEnv *env, jobject object, AndroidBitmap_unlockPixels (env, src); } +JNIEXPORT void JNICALL +NATIVE_NAME (notifyPixelsChanged) (JNIEnv *env, jobject object, + jobject bitmap) +{ + void *data; + + /* Lock and unlock the bitmap. This calls + SkBitmap->notifyPixelsChanged. */ + + if (AndroidBitmap_lockPixels (env, bitmap, &data) < 0) + /* The return value is less than 0 if an error occurs. + Good luck finding this in the documentation. */ + return; + + AndroidBitmap_unlockPixels (env, bitmap); +} + /* Forward declarations of deadlock prevention functions. */ static void android_begin_query (void); commit 140755f2cfe6a39f643ab0a9ca2d81b0ed470ae7 Author: Po Lu Date: Thu Jul 13 12:05:50 2023 +0800 Disable hardware acceleration on Android It serves no purpose and causes tearing. Uploading the bitmap to the GPU takes about as long as it does to incrementally update the surface in software. * java/AndroidManifest.xml.in: Disable hardware acceleration. * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): Make lastClosedMenu static. * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Enable hardware acceleration within alert dialogs. * java/org/gnu/emacs/EmacsSurfaceView.java (onDraw): Describe why hardware acceleration is disabled. * java/org/gnu/emacs/EmacsWindow.java (run): Remove redundant call. diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in index e79fb4e46e7..f2aede7369c 100644 --- a/java/AndroidManifest.xml.in +++ b/java/AndroidManifest.xml.in @@ -77,10 +77,14 @@ along with GNU Emacs. If not, see . --> @ANDROID_SHARED_USER_ID@ android:extractNativeLibs="true"> + + @@ -173,6 +177,7 @@ along with GNU Emacs. If not, see . --> = Build.VERSION_CODES.HONEYCOMB) + { + flag = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; + window = dialog.getWindow (); + window.addFlags (flag); + } + return dialog; } diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index 3f62af4ab99..54fe70e1634 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -176,7 +176,25 @@ else if (bitmap != null) onDraw (Canvas canvas) { /* Paint the view's bitmap; the bitmap might be recycled right - now. */ + now. + + Hardware acceleration is disabled in AndroidManifest.xml to + prevent Android from uploading the front buffer to the GPU from + a separate thread. This is important for two reasons: first, + the GPU command queue uses a massive amount of memory (dozens + of MiB) to upload bitmaps to the GPU, regardless of how much of + the bitmap has actually changed. + + Secondly, asynchronous texturization leads to race conditions + when a buffer swap occurs before the front buffer is fully + uploaded to the GPU. Normally, only slight and tolerable + tearing should result from this behavior, but Android does not + properly interlock the ``generation ID'' used to avoid + texturizing unchanged bitmaps with the bitmap contents, + consequentially leading to textures in an incomplete state + remaining in use to the GPU if a buffer swap happens between + the image data being uploaded and the ``generation ID'' being + read. */ if (frontBuffer != null) canvas.drawBitmap (frontBuffer, 0f, 0f, uiThreadPaint); diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 5e45275631b..0e96a8382d0 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -1377,7 +1377,7 @@ else if (EmacsWindow.this.isMapped) if (tem != null) { - activity = (EmacsActivity) getAttachedConsumer (); + activity = (EmacsActivity) tem; activity.syncFullscreenWith (EmacsWindow.this); } } commit 4e2fda28edd3eb229c60576a215c3e4af5aa53e3 Merge: 43d6c2bddc1 d861de72289 Author: Po Lu Date: Thu Jul 13 08:33:27 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 43d6c2bddc1319e217cce196741c6181fd0ff9dd Author: Po Lu Date: Wed Jul 12 17:04:35 2023 +0800 ; * src/android.c (android_run_select_thread): Fix typo. diff --git a/src/android.c b/src/android.c index 065f90b3463..5eb6f65419c 100644 --- a/src/android.c +++ b/src/android.c @@ -398,7 +398,7 @@ android_run_select_thread (void *data) if (rc != -1 && FD_ISSET (select_pipe[0], &readfds)) { rc -= 1; - FD_CLR (fd, &readfds); + FD_CLR (select_pipe[0], &readfds); /* If no file descriptors aside from the select pipe are ready, then pretend that an error has occurred. */ commit 3e6eaa3cb2eea00d83561458d8eb1e24b77d28cb Author: Po Lu Date: Wed Jul 12 16:45:24 2023 +0800 Update Android port * src/android.c (android_run_select_thread): Correctly return the set of ready read and write descriptors on __ANDROID_API__ < 16 systems. (android_select): Clear the set of ready file descriptors if events from the event queue are present despite pselect failing. diff --git a/src/android.c b/src/android.c index 4e9897f4648..065f90b3463 100644 --- a/src/android.c +++ b/src/android.c @@ -349,7 +349,7 @@ android_run_select_thread (void *data) int rc; #if __ANDROID_API__ < 16 int nfds; - fd_set readfds, writefds; + fd_set readfds; char byte; #else sigset_t signals, waitset; @@ -386,18 +386,30 @@ android_run_select_thread (void *data) nfds = select_pipe[0] + 1; FD_SET (select_pipe[0], &readfds); - rc = pselect (nfds, &readfds, &writefds, + rc = pselect (nfds, &readfds, + android_pselect_writefds, android_pselect_exceptfds, android_pselect_timeout, NULL); - /* Subtract 1 from rc if writefds contains the select pipe. */ - if (FD_ISSET (select_pipe[0], &writefds)) - rc -= 1; + /* Subtract 1 from rc if readfds contains the select pipe, and + also remove it from that set. */ + + if (rc != -1 && FD_ISSET (select_pipe[0], &readfds)) + { + rc -= 1; + FD_CLR (fd, &readfds); + + /* If no file descriptors aside from the select pipe are + ready, then pretend that an error has occurred. */ + if (!rc) + rc = -1; + } + + /* Save the read file descriptor set back again. */ - /* Save the writefds back again. */ - if (android_pselect_writefds) - *android_pselect_writefds = writefds; + if (android_pselect_readfds) + *android_pselect_readfds = readfds; android_pselect_rc = rc; pthread_mutex_unlock (&event_queue.select_mutex); @@ -795,13 +807,33 @@ android_select (int nfds, fd_set *readfds, fd_set *writefds, nfds_return = 1; pthread_mutex_unlock (&event_queue.mutex); - /* Add the return value of pselect. */ + /* Add the return value of pselect if it has also found ready file + descriptors. */ + if (android_pselect_rc >= 0) nfds_return += android_pselect_rc; - - if (!nfds_return && android_pselect_rc < 0) + else if (!nfds_return) + /* If pselect was interrupted and nfds_return is 0 (meaning that + no events have been read), indicate that an error has taken + place. */ nfds_return = android_pselect_rc; + if ((android_pselect_rc < 0) && nfds_return >= 0) + { + /* Clear the file descriptor sets if events will be delivered + but no file descriptors have become ready to prevent the + caller from misinterpreting a non-zero return value. */ + + if (readfds) + FD_ZERO (readfds); + + if (writefds) + FD_ZERO (writefds); + + if (exceptfds) + FD_ZERO (exceptfds); + } + /* This is to shut up process.c when pselect gets EINTR. */ if (nfds_return < 0) errno = EINTR; commit 7a6c7bac6a775a8175edd583018f990625630b17 Author: Po Lu Date: Wed Jul 12 12:46:08 2023 +0800 Fix keyboard state translation on Android * src/androidterm.c (android_android_to_emacs_modifiers) (android_emacs_to_android_modifiers): Fix statement precedence bugs. diff --git a/src/androidterm.c b/src/androidterm.c index 135eda45245..27800a61864 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -362,20 +362,20 @@ android_flush_dirty_back_buffer_on (struct frame *f) android_android_to_emacs_modifiers (struct android_display_info *dpyinfo, int state) { - return ((state & ANDROID_CONTROL_MASK) ? ctrl_modifier : 0 - | (state & ANDROID_SHIFT_MASK) ? shift_modifier : 0 - | (state & ANDROID_ALT_MASK) ? meta_modifier : 0 - | (state & ANDROID_SUPER_MASK) ? super_modifier : 0); + return (((state & ANDROID_CONTROL_MASK) ? ctrl_modifier : 0) + | ((state & ANDROID_SHIFT_MASK) ? shift_modifier : 0) + | ((state & ANDROID_ALT_MASK) ? meta_modifier : 0) + | ((state & ANDROID_SUPER_MASK) ? super_modifier : 0)); } static int android_emacs_to_android_modifiers (struct android_display_info *dpyinfo, intmax_t state) { - return ((state & ctrl_modifier) ? ANDROID_CONTROL_MASK : 0 - | (state & shift_modifier) ? ANDROID_SHIFT_MASK : 0 - | (state & meta_modifier) ? ANDROID_ALT_MASK : 0 - | (state & super_modifier) ? ANDROID_SUPER_MASK : 0); + return (((state & ctrl_modifier) ? ANDROID_CONTROL_MASK : 0) + | ((state & shift_modifier) ? ANDROID_SHIFT_MASK : 0) + | ((state & meta_modifier) ? ANDROID_ALT_MASK : 0) + | ((state & super_modifier) ? ANDROID_SUPER_MASK : 0)); } static void android_frame_rehighlight (struct android_display_info *); commit b27394b466bc8482e13dc30ead07be9e093933d1 Author: Po Lu Date: Wed Jul 12 09:57:13 2023 +0800 ; * src/doc.c (doc_close): Remove unused macro. diff --git a/src/doc.c b/src/doc.c index 56991ffcdfe..37e04262681 100644 --- a/src/doc.c +++ b/src/doc.c @@ -44,7 +44,6 @@ Copyright (C) 1985-1986, 1993-1995, 1997-2023 Free Software Foundation, #define doc_fd int #define doc_fd_p(fd) ((fd) >= 0) #define doc_open emacs_open -#define doc_close emacs_close #define doc_read_quit emacs_read_quit #define doc_lseek lseek #else /* HAVE_ANDROID && !defined ANDROID_STUBIFY @@ -64,7 +63,6 @@ #define doc_lseek lseek #define doc_fd struct android_fd_or_asset #define doc_fd_p(fd) ((fd).asset != (void *) -1) #define doc_open android_open_asset -#define doc_close android_close_asset #define doc_read_quit android_asset_read_quit #define doc_lseek android_asset_lseek #define USE_ANDROID_ASSETS commit c8c2bec5f8e4964f23345e1150a7ab85003e688b Author: Po Lu Date: Wed Jul 12 09:45:58 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsWindow.java (whatButtonWasIt): Handle back and forward buttons along with styluses. * src/doc.c (close_file_unwind_android_fd): New function. (get_doc_string, Fsnarf_documentation): Don't create a temporary fd if it can be avoided. diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 6816f3e8e71..5e45275631b 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -700,29 +700,36 @@ private static class Coordinate Android does not conceptually distinguish between mouse events (those coming from a device whose movement affects the on-screen - pointer image) and touch screen events. When a touch, click, or - pointer motion takes place, several kinds of event can be sent: + pointer image) and touch screen events. Each click or touch + starts a single pointer gesture sequence, and subsequent motion + of the device will result in updates being reported relative to + that sequence until the mouse button or touch is released. + + When a touch, click, or pointer motion takes place, several kinds + of event can be sent: ACTION_DOWN or ACTION_POINTER_DOWN is sent with a new coordinate - and an associated ``pointer ID'' identifying the event when a - click or touch takes place. Emacs is responsible for recording - both the position of this click for the purpose of determining - future changes to the position of that touch. + and an associated ``pointer ID'' identifying the event and its + gesture sequence when a click or touch takes place. Emacs is + responsible for recording both the position and pointer ID of + this click for the purpose of determining future changes to its + position. ACTION_UP or ACTION_POINTER_UP is sent with a pointer ID when the click associated with a previous ACTION_DOWN event is released. ACTION_CANCEL (or ACTION_POINTER_UP with FLAG_CANCELED) is sent if a similar situation transpires: the window system has chosen - to grab of the click, and future movement will no longer be - reported to Emacs. + to grab the click, and future changes to its position will no + longer be reported to Emacs. ACTION_MOVE is sent if a coordinate tied to a click that has not been released changes. Emacs processes this event by comparing each of the coordinates within the event with its recollection of those contained within prior ACTION_DOWN and ACTION_MOVE events; - the pointer ID of the difference is then reported within a touch - or pointer motion event along with its new position. + the pointer ID of the differing coordinate is then reported + within a touch or pointer motion event along with its new + position. The events described above are all sent for both touch and mouse click events. Determining whether an ACTION_DOWN event is @@ -746,7 +753,12 @@ private static class Coordinate coordinate. ACTION_HOVER_ENTER and ACTION_HOVER_LEAVE are respectively sent - when the mouse pointer enters and leaves a frame. + when the mouse pointer enters and leaves a frame. Moreover, + ACTION_HOVER_LEAVE events are sent immediately before an + ACTION_DOWN event associated with a mouse click. These + extraneous events are distinct in that their button states always + contain an additional button compared to the button state + recorded at the time of the last ACTION_UP event. On Android 6.0 and later, ACTION_BUTTON_PRESS is sent with the coordinate of the mouse pointer if a mouse click occurs, @@ -789,8 +801,25 @@ private static class Coordinate if ((notIn & MotionEvent.BUTTON_TERTIARY) != 0) return 2; + /* Buttons 4, 5, 6 and 7 are actually scroll wheels under X. + Thus, report additional buttons starting at 8. */ + + if ((notIn & MotionEvent.BUTTON_BACK) != 0) + return 8; + + if ((notIn & MotionEvent.BUTTON_FORWARD) != 0) + return 9; + + /* Report stylus events as touch screen events. */ + + if ((notIn & MotionEvent.BUTTON_STYLUS_PRIMARY) != 0) + return 0; + + if ((notIn & MotionEvent.BUTTON_STYLUS_SECONDARY) != 0) + return 0; + /* Not a real value. */ - return 4; + return 11; } /* Return the mouse button associated with the specified ACTION_DOWN diff --git a/src/doc.c b/src/doc.c index 174341523d7..56991ffcdfe 100644 --- a/src/doc.c +++ b/src/doc.c @@ -37,6 +37,41 @@ Copyright (C) 1985-1986, 1993-1995, 1997-2023 Free Software Foundation, #include "intervals.h" #include "keymap.h" + + +#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY \ + || (__ANDROID_API__ < 9) +#define doc_fd int +#define doc_fd_p(fd) ((fd) >= 0) +#define doc_open emacs_open +#define doc_close emacs_close +#define doc_read_quit emacs_read_quit +#define doc_lseek lseek +#else /* HAVE_ANDROID && !defined ANDROID_STUBIFY + && __ANDROID_API__ >= 9 */ + +#include "android.h" + +/* Use an Android file descriptor under Android instead, as this + allows loading directly from asset files without loading each asset + into memory and creating a separate file descriptor every time. + + However, lread requires the ability to seek inside asset files, + which is not provided under Android 2.2. So when building for that + particular system, fall back to the usual file descriptor-based + code. */ + +#define doc_fd struct android_fd_or_asset +#define doc_fd_p(fd) ((fd).asset != (void *) -1) +#define doc_open android_open_asset +#define doc_close android_close_asset +#define doc_read_quit android_asset_read_quit +#define doc_lseek android_asset_lseek +#define USE_ANDROID_ASSETS +#endif /* !HAVE_ANDROID || ANDROID_STUBIFY || __ANDROID_API__ < 9 */ + + + /* Buffer used for reading from documentation file. */ static char *get_doc_string_buffer; static ptrdiff_t get_doc_string_buffer_size; @@ -59,6 +94,22 @@ read_bytecode_char (bool unreadflag) return *read_bytecode_pointer++; } +#ifdef USE_ANDROID_ASSETS + +/* Like `close_file_unwind'. However, PTR is a pointer to an Android + file descriptor instead of a system file descriptor. */ + +static void +close_file_unwind_android_fd (void *ptr) +{ + struct android_fd_or_asset *fd; + + fd = ptr; + android_close_asset (*fd); +} + +#endif /* USE_ANDROID_ASSETS */ + /* Extract a doc string from a file. FILEPOS says where to get it. If it is an integer, use that position in the standard DOC file. If it is (FILE . INTEGER), use FILE as the file name @@ -123,8 +174,8 @@ get_doc_string (Lisp_Object filepos, bool unibyte, bool definition) name = SAFE_ALLOCA (docdir_sizemax + SBYTES (file)); lispstpcpy (lispstpcpy (name, docdir), file); - int fd = emacs_open (name, O_RDONLY, 0); - if (fd < 0) + doc_fd fd = doc_open (name, O_RDONLY, 0); + if (!doc_fd_p (fd)) { if (will_dump_p ()) { @@ -132,9 +183,9 @@ get_doc_string (Lisp_Object filepos, bool unibyte, bool definition) So check in ../etc. */ lispstpcpy (stpcpy (name, sibling_etc), file); - fd = emacs_open (name, O_RDONLY, 0); + fd = doc_open (name, O_RDONLY, 0); } - if (fd < 0) + if (!doc_fd_p (fd)) { if (errno != ENOENT && errno != ENOTDIR) report_file_error ("Read error on documentation file", file); @@ -145,14 +196,18 @@ get_doc_string (Lisp_Object filepos, bool unibyte, bool definition) return concat3 (cannot_open, file, quote_nl); } } +#ifndef USE_ANDROID_ASSETS record_unwind_protect_int (close_file_unwind, fd); +#else /* USE_ANDROID_ASSETS */ + record_unwind_protect_ptr (close_file_unwind_android_fd, &fd); +#endif /* !USE_ANDROID_ASSETS */ /* Seek only to beginning of disk block. */ /* Make sure we read at least 1024 bytes before `position' so we can check the leading text for consistency. */ int offset = min (position, max (1024, position % (8 * 1024))); if (TYPE_MAXIMUM (off_t) < position - || lseek (fd, position - offset, 0) < 0) + || doc_lseek (fd, position - offset, 0) < 0) error ("Position %"pI"d out of range in doc string file \"%s\"", position, name); @@ -181,7 +236,7 @@ get_doc_string (Lisp_Object filepos, bool unibyte, bool definition) If we read the same block last time, maybe skip this? */ if (space_left > 1024 * 8) space_left = 1024 * 8; - int nread = emacs_read_quit (fd, p, space_left); + int nread = doc_read_quit (fd, p, space_left); if (nread < 0) report_file_error ("Read error on documentation file", file); p[nread] = 0; @@ -504,7 +559,7 @@ DEFUN ("Snarf-documentation", Fsnarf_documentation, Ssnarf_documentation, the same file name is found in the `doc-directory'. */) (Lisp_Object filename) { - int fd; + doc_fd fd; char buf[1024 + 1]; int filled; EMACS_INT pos; @@ -551,21 +606,25 @@ DEFUN ("Snarf-documentation", Fsnarf_documentation, Ssnarf_documentation, Vbuild_files = Fpurecopy (Vbuild_files); } - fd = emacs_open (name, O_RDONLY, 0); - if (fd < 0) + fd = doc_open (name, O_RDONLY, 0); + if (!doc_fd_p (fd)) { int open_errno = errno; report_file_errno ("Opening doc string file", build_string (name), open_errno); } +#ifndef USE_ANDROID_ASSETS record_unwind_protect_int (close_file_unwind, fd); +#else /* USE_ANDROID_ASSETS */ + record_unwind_protect_ptr (close_file_unwind_android_fd, &fd); +#endif /* !USE_ANDROID_ASSETS */ Vdoc_file_name = filename; filled = 0; pos = 0; while (true) { if (filled < 512) - filled += emacs_read_quit (fd, &buf[filled], sizeof buf - 1 - filled); + filled += doc_read_quit (fd, &buf[filled], sizeof buf - 1 - filled); if (!filled) break; commit fe7861f8088b481584b8122769121d2bfe26f643 Merge: fcf53bf9f58 04710bd01b2 Author: Po Lu Date: Wed Jul 12 08:53:54 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit fcf53bf9f5894665af07499137d7e8ef888cdcdd Author: Po Lu Date: Tue Jul 11 16:47:13 2023 +0800 Fix doc file generation on Android * .gitignore: Ignore cross/etc/DOC. * configure.ac: Make the directory `cross/etc'. * cross/Makefile.in (CLEAN_SUBDIRS): Clean files inside `etc' as well. * java/Makefile.in (install_temp): Copy cross/etc/DOC to the package if it is available. * src/Makefile.in (SOME_MACHINE_OBJECTS): Add androidselect.c, sfntfont-android.c and sfntfont.c. (libemacs.so): Depend on $(etc)/DOC. diff --git a/.gitignore b/.gitignore index c95bccccfbf..889008dd96d 100644 --- a/.gitignore +++ b/.gitignore @@ -107,6 +107,7 @@ cross/lib-src/* cross/sys/* cross/config.status cross/*.bak +cross/etc/DOC cross/ndk-build/Makefile cross/ndk-build/ndk-build.mk diff --git a/configure.ac b/configure.ac index 48d474f6888..86abf54911e 100644 --- a/configure.ac +++ b/configure.ac @@ -7828,6 +7828,12 @@ AC_DEFUN AS_MKDIR_P([cross/lib/sys]) AS_MKDIR_P([cross/lib-src]) + dnl Make cross/etc; this directory will hold the documentation file + dnl holding doc strings for Android specific C files that aren't + dnl built during the initial compilation of Emacs for the build + dnl machine. + AS_MKDIR_P([cross/etc]) + dnl Link gnulib files to cross/lib as well. dnl af_alg.h and lib/save-cwd.h are copied manually from dnl gnulib, and as such aren't specified in gl_FILE_LIST. diff --git a/cross/Makefile.in b/cross/Makefile.in index 5976272b253..a7b5700440e 100644 --- a/cross/Makefile.in +++ b/cross/Makefile.in @@ -51,7 +51,7 @@ LIB_SRC_TOP_SRCDIR = LIBSRC_BINARIES = lib-src/etags lib-src/ctags lib-src/emacsclient \ lib-src/ebrowse lib-src/hexl lib-src/movemail -CLEAN_SUBDIRS=src lib-src lib +CLEAN_SUBDIRS = src lib-src lib etc .PHONY: all all: lib/libgnu.a src/libemacs.so src/android-emacs $(LIBSRC_BINARIES) diff --git a/cross/verbose.mk.android b/cross/verbose.mk.android index 998f9843c7d..d41d6b6aac0 100644 --- a/cross/verbose.mk.android +++ b/cross/verbose.mk.android @@ -45,7 +45,7 @@ have_working_info = $(filter notintermediate,$(value .FEATURES)) # since the bug is not annoying elsewhere. AM_V_AR = @$(info $ AR $@) -AM_V_at = @ +AM_V_at = @ AM_V_CC = @$(info $ CC $@) AM_V_CXX = @$(info $ CXX $@) AM_V_CCLD = @$(info $ CCLD $@) diff --git a/java/Makefile.in b/java/Makefile.in index 84173cd9655..d11278e6110 100644 --- a/java/Makefile.in +++ b/java/Makefile.in @@ -191,6 +191,11 @@ install_temp: $(AM_V_SILENT) cp -r $(top_srcdir)/lisp install_temp/assets $(AM_V_SILENT) cp -r $(top_srcdir)/etc install_temp/assets $(AM_V_SILENT) cp -r $(top_srcdir)/info install_temp/assets +# Replace etc/DOC generated by compiling Emacs for the build machine +# with etc/DOC from the cross-compiled Emacs. + $(AM_V_SILENT) test -f $(top_builddir)/cross/etc/DOC \ + && cp -r $(top_builddir)/cross/etc/DOC \ + install_temp/assets/etc # Remove undesirable files from those directories. $(AM_V_SILENT) \ for subdir in `find install_temp -type d -print`; do \ diff --git a/src/Makefile.in b/src/Makefile.in index 9d43af4dad6..8cbdd67378c 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -494,7 +494,7 @@ SOME_MACHINE_OBJECTS = w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \ xsettings.o xgselect.o termcap.o hbfont.o \ haikuterm.o haikufns.o haikumenu.o haikufont.o androidterm.o androidfns.o \ - androidfont.o + androidfont.o androidselect.c sfntfont-android.c sfntfont.c ## gmalloc.o if !SYSTEM_MALLOC && !DOUG_LEA_MALLOC, else empty. GMALLOC_OBJ=@GMALLOC_OBJ@ @@ -749,11 +749,11 @@ temacs$(EXEEXT): endif ifeq ($(XCONFIGURE),android) -## The Android package internally links to and communicates with a -## shared library named `libemacs.so' at startup. This is built -## almost the same way temacs is. But it is position independent. It -## is not dumped here. Instead, it dumps itself the first time it -## starts on the user's device. +## The Android package internally links to a shared library named +## `libemacs.so' at startup. It is built almost the same way temacs +## is. But it is position independent, and is not dumped here. +## Instead, it dumps itself the first time it starts on the user's +## device. # Include ndk-build.mk in order to build Emacs dependencies. old_top_builddir := $(top_builddir) @@ -763,7 +763,7 @@ top_builddir := libemacs.so: $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(EMACSRES) \ $(MAKE_PDUMPER_FINGERPRINT) $(NDK_BUILD_SHARED) \ - $(NDK_BUILD_STATIC) + $(NDK_BUILD_STATIC) $(etc)/DOC $(AM_V_CCLD)$(CC) -o $@ $(ALL_CFLAGS) $(TEMACS_LDFLAGS) \ $(ANDROID_LDFLAGS) $(LDFLAGS) -shared $(ALLOBJS) \ $(LIBEGNU_ARCHIVE) $(LIBES) commit f2c4643890837c156b8332227232060028102c01 Author: Po Lu Date: Tue Jul 11 12:40:13 2023 +0800 Update Android port * src/sfnt.c (sfnt_fill_span): Correctly clip span to raster width, ensuring that the last pixel is filled. (main): Adjust test sizes. diff --git a/src/sfnt.c b/src/sfnt.c index da4cb3cc783..dc190aa950b 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -4250,15 +4250,22 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, sfnt_fixed left, right, end; unsigned short w, a; int row; +#ifndef NDEBUG + unsigned char *row_end; +#endif /* NDEBUG */ /* Clip bounds to pixmap. */ + if (x0 < 0) x0 = 0; - if (x1 >= raster->width << 16) - x1 = (raster->width - 1) << 16; + /* If x1 is greater than the raster width, make sure the last pixel + is filled and no more after that. */ + + if (x1 > raster->width * 65536) + x1 = raster->width * 65536; - /* Check for empty bounds. */ + /* Check for empty spans. */ if (x1 <= x0) return; @@ -4276,6 +4283,9 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, left = x0 >> (16 - SFNT_POLY_SHIFT); right = x1 >> (16 - SFNT_POLY_SHIFT); start = raster->cells + row * raster->stride; +#ifndef NDEBUG + row_end = start + raster->width; +#endif /* NDEBUG */ start += left >> SFNT_POLY_SHIFT; /* If left and right actually lie in the same pixel, just fill with @@ -4283,6 +4293,9 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, if ((left & ~SFNT_POLY_MASK) == (right & ~SFNT_POLY_MASK)) { + /* Assert that start does not exceed the end of the row. */ + assert (start <= row_end); + w = coverage[right - left]; a = *start + w; @@ -4296,6 +4309,9 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, if (left & SFNT_POLY_MASK) { + /* Assert that start does not exceed the end of the row. */ + assert (start <= row_end); + /* Compute the coverage for the first pixel, and move left past it. The coverage is a number from 1 to 7 describing how ``partially'' covered this pixel is. */ @@ -4320,6 +4336,9 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, /* Fill pixels between left and right. */ while (left + SFNT_POLY_MASK < right) { + /* Assert that start does not exceed the end of the row. */ + assert (start <= row_end); + a = *start + w; *start++ = sfnt_saturate_short (a); left += SFNT_POLY_SAMPLE; @@ -4329,12 +4348,13 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, if (right & SFNT_POLY_MASK) { + /* Assert that start does not exceed the end of the row. */ + assert (start <= row_end); + w = coverage[right - left]; a = *start + w; *start = sfnt_saturate_short (a); } - - /* All done. */ } /* Poly each span starting from START onto RASTER, at position Y. Y @@ -19026,8 +19046,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 19 -#define EASY_PPEM 19 +#define FANCY_PPEM 12 +#define EASY_PPEM 12 interpreter = NULL; head = sfnt_read_head_table (fd, font); commit 24ae08c11342d93ac68b284302fcf5929c19ffb0 Author: Po Lu Date: Tue Jul 11 10:22:03 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsView.java (onGenericMotionEvent): Call onGenericMotionEvent. * java/org/gnu/emacs/EmacsWindow.java (Coordinate): New fields `button' and `id'. (): Add new arguments to the construtor. (whatButtonWasIt): Return 0 if the button state has not changed. (buttonForEvent): New function. (figureChange): Return the Coordinate object associated to EVENT. Determine whether or not EVENT was accompanied by a change to the button state, and ascertain which button that was. (motionEvent): New function. (onGenericMotionEvent, onTouchEvent): Factor out touch and mouse event delivery to motionEvent. diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index bb4dace655a..12d8ff4da56 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -475,7 +475,7 @@ else if (child.getVisibility () != GONE) public boolean onGenericMotionEvent (MotionEvent motion) { - return window.onSomeKindOfMotionEvent (motion); + return window.onGenericMotionEvent (motion); } @Override diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 15d5fe8a175..6816f3e8e71 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -66,11 +66,20 @@ private static class Coordinate /* Integral coordinate. */ int x, y; + /* Button associated with the coordinate, or 0 if it is a touch + event. */ + int button; + + /* Pointer ID associated with the coordinate. */ + int id; + public - Coordinate (int x, int y) + Coordinate (int x, int y, int button, int id) { this.x = x; this.y = y; + this.button = button; + this.id = id; } }; @@ -595,31 +604,6 @@ private static class Coordinate return state; } - /* Return the modifier mask associated with the specified motion - EVENT. Replace bits corresponding to Left or Right keys with - their corresponding general modifier bits. */ - - private int - motionEventModifiers (MotionEvent event) - { - int state; - - state = event.getMetaState (); - - /* Normalize the state by setting the generic modifier bit if - either a left or right modifier is pressed. */ - - if ((state & KeyEvent.META_ALT_LEFT_ON) != 0 - || (state & KeyEvent.META_ALT_RIGHT_ON) != 0) - state |= KeyEvent.META_ALT_MASK; - - if ((state & KeyEvent.META_CTRL_LEFT_ON) != 0 - || (state & KeyEvent.META_CTRL_RIGHT_ON) != 0) - state |= KeyEvent.META_CTRL_MASK; - - return state; - } - /* event.getCharacters is used because older input methods still require it. */ @SuppressWarnings ("deprecation") @@ -710,6 +694,69 @@ private static class Coordinate EmacsNative.sendWindowAction (this.handle, 0); } + + + /* Mouse and touch event handling. + + Android does not conceptually distinguish between mouse events + (those coming from a device whose movement affects the on-screen + pointer image) and touch screen events. When a touch, click, or + pointer motion takes place, several kinds of event can be sent: + + ACTION_DOWN or ACTION_POINTER_DOWN is sent with a new coordinate + and an associated ``pointer ID'' identifying the event when a + click or touch takes place. Emacs is responsible for recording + both the position of this click for the purpose of determining + future changes to the position of that touch. + + ACTION_UP or ACTION_POINTER_UP is sent with a pointer ID when the + click associated with a previous ACTION_DOWN event is released. + + ACTION_CANCEL (or ACTION_POINTER_UP with FLAG_CANCELED) is sent + if a similar situation transpires: the window system has chosen + to grab of the click, and future movement will no longer be + reported to Emacs. + + ACTION_MOVE is sent if a coordinate tied to a click that has not + been released changes. Emacs processes this event by comparing + each of the coordinates within the event with its recollection of + those contained within prior ACTION_DOWN and ACTION_MOVE events; + the pointer ID of the difference is then reported within a touch + or pointer motion event along with its new position. + + The events described above are all sent for both touch and mouse + click events. Determining whether an ACTION_DOWN event is + associated with a button event is performed by inspecting the + mouse button state associated with that event. If it contains + any mouse buttons that were not contained in the button state at + the time of the last ACTION_DOWN or ACTION_UP event, the + coordinate contained within is assumed to be a mouse click, + leading to it and associated motion or ACTION_UP events being + reported as mouse button or motion events. Otherwise, those + events are reported as touch screen events, with the touch ID set + to the pointer ID. + + In addition to the events illustrated above, Android also sends + several other types of event upon select types of activity from a + mouse device: + + ACTION_HOVER_MOVE is sent with the coordinate of the mouse + pointer if it moves above a frame prior to any click taking + place. Emacs sends a mouse motion event containing the + coordinate. + + ACTION_HOVER_ENTER and ACTION_HOVER_LEAVE are respectively sent + when the mouse pointer enters and leaves a frame. + + On Android 6.0 and later, ACTION_BUTTON_PRESS is sent with the + coordinate of the mouse pointer if a mouse click occurs, + alongside a ACTION_DOWN event. ACTION_BUTTON_RELEASE is sent + with the same information upon a mouse click being released, also + accompanying an ACTION_UP event. + + However, both types of button events are implemented in a buggy + fashion and cannot be used to report button events. */ + /* Look through the button state to determine what button EVENT was generated from. DOWN is true if EVENT is a button press event, false otherwise. Value is the X number of the button. */ @@ -719,16 +766,20 @@ private static class Coordinate { int eventState, notIn; - if (Build.VERSION.SDK_INT - < Build.VERSION_CODES.ICE_CREAM_SANDWICH) - /* Earlier versions of Android only support one mouse - button. */ - return 1; - + /* Obtain the new button state. */ eventState = event.getButtonState (); + + /* Compute which button is now set or no longer set. */ + notIn = (down ? eventState & ~lastButtonState : lastButtonState & ~eventState); + if ((notIn & (MotionEvent.BUTTON_PRIMARY + | MotionEvent.BUTTON_SECONDARY + | MotionEvent.BUTTON_TERTIARY)) == 0) + /* No buttons have been pressed, so this is a touch event. */ + return 0; + if ((notIn & MotionEvent.BUTTON_PRIMARY) != 0) return 1; @@ -742,53 +793,55 @@ private static class Coordinate return 4; } - /* Return the ID of the pointer which changed in EVENT. Value is -1 - if it could not be determined, else the pointer that changed, or - -2 if -1 would have been returned, but there is also a pointer - that is a mouse. */ + /* Return the mouse button associated with the specified ACTION_DOWN + or ACTION_POINTER_DOWN EVENT. + + Value is 0 if no mouse button was pressed, or the X number of + that mouse button. */ private int + buttonForEvent (MotionEvent event) + { + /* ICS and earlier don't support true mouse button events, so + treat all down events as touch screen events. */ + + if (Build.VERSION.SDK_INT + < Build.VERSION_CODES.ICE_CREAM_SANDWICH) + return 0; + + return whatButtonWasIt (event, true); + } + + /* Return the coordinate object associated with the specified + EVENT, or null if it is not known. */ + + private Coordinate figureChange (MotionEvent event) { - int pointerID, i, truncatedX, truncatedY, pointerIndex; + int i, truncatedX, truncatedY, pointerIndex, pointerID, count; Coordinate coordinate; - boolean mouseFlag; - /* pointerID is always initialized but the Java compiler is too - dumb to know that. */ - pointerID = -1; - mouseFlag = false; + /* Initialize this variable now. */ + coordinate = null; switch (event.getActionMasked ()) { case MotionEvent.ACTION_DOWN: /* Primary pointer pressed with index 0. */ - /* Detect mice. If this is a mouse event, give it to - onSomeKindOfMotionEvent. */ - if ((Build.VERSION.SDK_INT - >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) - && event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE) - return -2; - pointerID = event.getPointerId (0); - pointerMap.put (pointerID, - new Coordinate ((int) event.getX (0), - (int) event.getY (0))); + coordinate = new Coordinate ((int) event.getX (0), + (int) event.getY (0), + buttonForEvent (event), + pointerID); + pointerMap.put (pointerID, coordinate); break; case MotionEvent.ACTION_UP: - - /* Detect mice. If this is a mouse event, give it to - onSomeKindOfMotionEvent. */ - if ((Build.VERSION.SDK_INT - >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) - && event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE) - return -2; - + case MotionEvent.ACTION_CANCEL: /* Primary pointer released with index 0. */ pointerID = event.getPointerId (0); - pointerMap.remove (pointerID); + coordinate = pointerMap.remove (pointerID); break; case MotionEvent.ACTION_POINTER_DOWN: @@ -796,22 +849,26 @@ private static class Coordinate it in the map. */ pointerIndex = event.getActionIndex (); pointerID = event.getPointerId (pointerIndex); - pointerMap.put (pointerID, - new Coordinate ((int) event.getX (pointerIndex), - (int) event.getY (pointerIndex))); + coordinate = new Coordinate ((int) event.getX (0), + (int) event.getY (0), + buttonForEvent (event), + pointerID); + pointerMap.put (pointerID, coordinate); break; case MotionEvent.ACTION_POINTER_UP: /* Pointer removed. Remove it from the map. */ pointerIndex = event.getActionIndex (); pointerID = event.getPointerId (pointerIndex); - pointerMap.remove (pointerID); + coordinate = pointerMap.remove (pointerID); break; default: /* Loop through each pointer in the event. */ - for (i = 0; i < event.getPointerCount (); ++i) + + count = event.getPointerCount (); + for (i = 0; i < count; ++i) { pointerID = event.getPointerId (i); @@ -835,73 +892,152 @@ private static class Coordinate break; } } - - /* See if this is a mouse. If so, set the mouseFlag. */ - if ((Build.VERSION.SDK_INT - >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) - && event.getToolType (i) == MotionEvent.TOOL_TYPE_MOUSE) - mouseFlag = true; } - /* Set the pointer ID to -1 if the loop failed to find any - changed pointer. If a mouse pointer was found, set it to - -2. */ - if (i == event.getPointerCount ()) - pointerID = (mouseFlag ? -2 : -1); + /* Set coordinate to NULL if the loop failed to find any + matching pointer. */ + + if (i == count) + coordinate = null; } /* Return the pointer ID. */ - return pointerID; + return coordinate; } - public boolean - onTouchEvent (MotionEvent event) + /* Return the modifier mask associated with the specified motion + EVENT. Replace bits corresponding to Left or Right keys with + their corresponding general modifier bits. */ + + private int + motionEventModifiers (MotionEvent event) + { + int state; + + state = event.getMetaState (); + + /* Normalize the state by setting the generic modifier bit if + either a left or right modifier is pressed. */ + + if ((state & KeyEvent.META_ALT_LEFT_ON) != 0 + || (state & KeyEvent.META_ALT_RIGHT_ON) != 0) + state |= KeyEvent.META_ALT_MASK; + + if ((state & KeyEvent.META_CTRL_LEFT_ON) != 0 + || (state & KeyEvent.META_CTRL_RIGHT_ON) != 0) + state |= KeyEvent.META_CTRL_MASK; + + return state; + } + + /* Process a single ACTION_DOWN, ACTION_POINTER_DOWN, ACTION_UP, + ACTION_POINTER_UP, ACTION_CANCEL, or ACTION_MOVE event. + + Ascertain which coordinate changed and send an appropriate mouse + or touch screen event. */ + + private void + motionEvent (MotionEvent event) { - int pointerID, index; + Coordinate coordinate; + int modifiers; + long time; - /* Extract the ``touch ID'' (or in Android, the ``pointer - ID''.) */ - pointerID = figureChange (event); + /* Find data associated with this event's pointer. Namely, its + current location, whether or not a change has taken place, and + whether or not it is a button event. */ - if (pointerID < 0) + coordinate = figureChange (event); + + if (coordinate == null) + return; + + time = event.getEventTime (); + + if (coordinate.button != 0) { - /* If this is a mouse event, give it to - onSomeKindOfMotionEvent. */ - if (pointerID == -2) - return onSomeKindOfMotionEvent (event); + /* This event is tied to a mouse click, so report mouse motion + and button events. */ + + modifiers = motionEventModifiers (event); - return false; + switch (event.getAction ()) + { + case MotionEvent.ACTION_POINTER_DOWN: + case MotionEvent.ACTION_DOWN: + EmacsNative.sendButtonPress (this.handle, coordinate.x, + coordinate.y, time, modifiers, + coordinate.button); + break; + + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + EmacsNative.sendButtonRelease (this.handle, coordinate.x, + coordinate.y, time, modifiers, + coordinate.button); + break; + + case MotionEvent.ACTION_MOVE: + EmacsNative.sendMotionNotify (this.handle, coordinate.x, + coordinate.y, time); + break; + } } + else + { + /* This event is a touch event, and the touch ID is the + pointer ID. */ - /* Find the pointer index corresponding to the event. */ - index = event.findPointerIndex (pointerID); + switch (event.getActionMasked ()) + { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + /* Touch down event. */ + EmacsNative.sendTouchDown (this.handle, coordinate.x, + coordinate.y, time, + coordinate.id); + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + /* Touch up event. Android documentation says ACTION_CANCEL + should be treated as more or less equivalent to ACTION_UP, + so that is what is done here. */ + EmacsNative.sendTouchUp (this.handle, coordinate.x, + coordinate.y, time, coordinate.id); + break; + + case MotionEvent.ACTION_MOVE: + /* Pointer motion event. */ + EmacsNative.sendTouchMove (this.handle, coordinate.x, + coordinate.y, time, coordinate.id); + break; + } + } + + if (Build.VERSION.SDK_INT + < Build.VERSION_CODES.ICE_CREAM_SANDWICH) + return; + + /* Now update the button state. */ + lastButtonState = event.getButtonState (); + return; + } + public boolean + onTouchEvent (MotionEvent event) + { switch (event.getActionMasked ()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: - /* Touch down event. */ - EmacsNative.sendTouchDown (this.handle, (int) event.getX (index), - (int) event.getY (index), - event.getEventTime (), pointerID); - return true; - case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_CANCEL: - /* Touch up event. Android documentation says ACTION_CANCEL - should be treated as more or less equivalent to ACTION_UP, - so that is what is done here. */ - EmacsNative.sendTouchUp (this.handle, (int) event.getX (index), - (int) event.getY (index), - event.getEventTime (), pointerID); - return true; - case MotionEvent.ACTION_MOVE: - /* Pointer motion event. */ - EmacsNative.sendTouchMove (this.handle, (int) event.getX (index), - (int) event.getY (index), - event.getEventTime (), pointerID); + motionEvent (event); return true; } @@ -909,18 +1045,8 @@ private static class Coordinate } public boolean - onSomeKindOfMotionEvent (MotionEvent event) + onGenericMotionEvent (MotionEvent event) { - /* isFromSource is not available until API level 18. */ - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) - { - if (!event.isFromSource (InputDevice.SOURCE_CLASS_POINTER)) - return false; - } - else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) - return false; - switch (event.getAction ()) { case MotionEvent.ACTION_HOVER_ENTER: @@ -929,7 +1055,6 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) event.getEventTime ()); return true; - case MotionEvent.ACTION_MOVE: case MotionEvent.ACTION_HOVER_MOVE: EmacsNative.sendMotionNotify (this.handle, (int) event.getX (), (int) event.getY (), @@ -950,64 +1075,16 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) return true; - case MotionEvent.ACTION_BUTTON_PRESS: - /* Find the button which was pressed. */ - EmacsNative.sendButtonPress (this.handle, (int) event.getX (), - (int) event.getY (), - event.getEventTime (), - motionEventModifiers (event), - whatButtonWasIt (event, true)); - - if (Build.VERSION.SDK_INT - < Build.VERSION_CODES.ICE_CREAM_SANDWICH) - return true; - - lastButtonState = event.getButtonState (); - return true; - - case MotionEvent.ACTION_BUTTON_RELEASE: - /* Find the button which was released. */ - EmacsNative.sendButtonRelease (this.handle, (int) event.getX (), - (int) event.getY (), - event.getEventTime (), - motionEventModifiers (event), - whatButtonWasIt (event, false)); - - if (Build.VERSION.SDK_INT - < Build.VERSION_CODES.ICE_CREAM_SANDWICH) - return true; - - lastButtonState = event.getButtonState (); - return true; - case MotionEvent.ACTION_DOWN: - /* Emacs must return true even though touch events are not - handled here, because the value of this function is used by - the system to decide whether or not Emacs gets ACTION_MOVE - events. */ - return true; - + case MotionEvent.ACTION_POINTER_DOWN: case MotionEvent.ACTION_UP: - /* However, if ACTION_UP reports a different button state from - the last known state, look up which button was released and - send a ButtonRelease event; this is to work around a bug in - the framework where real ACTION_BUTTON_RELEASE events are - not delivered. */ - - if (Build.VERSION.SDK_INT - < Build.VERSION_CODES.ICE_CREAM_SANDWICH) - return true; - - if (event.getButtonState () == 0 && lastButtonState != 0) - { - EmacsNative.sendButtonRelease (this.handle, (int) event.getX (), - (int) event.getY (), - event.getEventTime (), - motionEventModifiers (event), - whatButtonWasIt (event, false)); - lastButtonState = event.getButtonState (); - } - + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_MOVE: + /* MotionEvents may either be sent to onGenericMotionEvent or + onTouchEvent depending on if Android thinks it is a mouse + event or not, but we detect them ourselves. */ + motionEvent (event); return true; case MotionEvent.ACTION_SCROLL: @@ -1024,6 +1101,8 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) return false; } + + public synchronized void reparentTo (final EmacsWindow otherWindow, int x, int y) { commit dc7ecc6e31ae431e060464a9a24f050cc29077a3 Merge: cf2dde4261a 3b7273f4ae3 Author: Po Lu Date: Tue Jul 11 08:19:32 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit cf2dde4261a311406203a38d6bf1be72b4f9e8a7 Author: Po Lu Date: Mon Jul 10 13:31:57 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsService.java (browseUrl): New argument SEND. Choose from a list of applications that want to share the URL if true. * lisp/net/browse-url.el (browse-url-android-share): New user option. (browse-url-default-android-browser): Respect said user option. * src/android.c (android_init_emacs_service) (android_browse_url): Expose new option. * src/android.h: Update prototypes. * src/androidselect.c (Fandroid_browse_url): Likewise. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 0543c3a1bdd..22649167f8a 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -583,12 +583,18 @@ invocation of app_process (through android-emacs) can } } - /* Ask the system to open the specified URL. + /* Ask the system to open the specified URL in an application that + understands how to open it. + + If SEND, tell the system to also open applications that can + ``send'' the URL (through mail, for example), instead of only + those that can view the URL. + Value is NULL upon success, or a string describing the error upon failure. */ public String - browseUrl (String url) + browseUrl (String url, boolean send) { Intent intent; Uri uri; @@ -596,28 +602,47 @@ invocation of app_process (through android-emacs) can try { /* Parse the URI. */ - uri = Uri.parse (url); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + if (!send) { - /* On Android 4.4 and later, check if URI is actually a - file name. If so, rewrite it into a content provider - URI, so that it can be accessed by other programs. */ - - if (uri.getScheme ().equals ("file") - && uri.getPath () != null) - uri - = DocumentsContract.buildDocumentUri ("org.gnu.emacs", - uri.getPath ()); + uri = Uri.parse (url); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + { + /* On Android 4.4 and later, check if URI is actually + a file name. If so, rewrite it into a content + provider URI, so that it can be accessed by other + programs. */ + + if (uri.getScheme ().equals ("file") + && uri.getPath () != null) + uri + = DocumentsContract.buildDocumentUri ("org.gnu.emacs", + uri.getPath ()); + } + + Log.d (TAG, ("browseUri: browsing " + url + + " --> " + uri.getPath () + + " --> " + uri)); + + intent = new Intent (Intent.ACTION_VIEW, uri); + intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_GRANT_READ_URI_PERMISSION); } + else + { + intent = new Intent (Intent.ACTION_SEND); + intent.setType ("text/plain"); + intent.putExtra (Intent.EXTRA_SUBJECT, "Sharing link"); + intent.putExtra (Intent.EXTRA_TEXT, url); + + /* Display a list of programs able to send this URL. */ + intent = Intent.createChooser (intent, "Send"); - Log.d (TAG, ("browseUri: browsing " + url - + " --> " + uri.getPath () - + " --> " + uri)); + /* Apparently flags need to be set after a choser is + created. */ + intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK); + } - intent = new Intent (Intent.ACTION_VIEW, uri); - intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity (intent); } catch (Exception e) diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el index 8036bbc8acc..daa46baf8f2 100644 --- a/lisp/net/browse-url.el +++ b/lisp/net/browse-url.el @@ -1307,18 +1307,31 @@ browse-url-default-haiku-browser (function-put 'browse-url-default-haiku-browser 'browse-url-browser-kind 'external) +(defcustom browse-url-android-share nil + "If non-nil, share URLs instead of opening them. +When non-nil, `browse-url-default-android-browser' will try to +share the URL being browsed through programs such as mail clients +and instant messengers instead of opening it in a web browser." + :type 'boolean + :version "30.1") + (declare-function android-browse-url "androidselect.c") ;;;###autoload (defun browse-url-default-android-browser (url &optional _new-window) "Browse URL with the system default browser. -Default to the URL around or before point." +If `browse-url-android-share' is non-nil, try to share URL using +an external program instead. Default to the URL around or before +point." (interactive (browse-url-interactive-arg "URL: ")) - (setq url (browse-url-encode-url url)) + (unless browse-url-android-share + ;; The URL shouldn't be encoded if it's being shared through + ;; another program. + (setq url (browse-url-encode-url url))) ;; Make sure the URL starts with an appropriate scheme. (unless (string-match "\\(.+\\):/" url) (setq url (concat "http://" url))) - (android-browse-url url)) + (android-browse-url url browse-url-android-share)) (function-put 'browse-url-default-android-browser 'browse-url-browser-kind 'external) diff --git a/src/android.c b/src/android.c index 5850b6079c9..4e9897f4648 100644 --- a/src/android.c +++ b/src/android.c @@ -2286,7 +2286,7 @@ #define FIND_METHOD(c_name, name, signature) \ FIND_METHOD (get_screen_height, "getScreenHeight", "(Z)I"); FIND_METHOD (detect_mouse, "detectMouse", "()Z"); FIND_METHOD (name_keysym, "nameKeysym", "(I)Ljava/lang/String;"); - FIND_METHOD (browse_url, "browseUrl", "(Ljava/lang/String;)" + FIND_METHOD (browse_url, "browseUrl", "(Ljava/lang/String;Z)" "Ljava/lang/String;"); FIND_METHOD (restart_emacs, "restartEmacs", "()V"); FIND_METHOD (update_ic, "updateIC", @@ -6959,12 +6959,15 @@ android_project_image_nearest (struct android_image *image, /* Other miscellaneous functions. */ -/* Ask the system to start browsing the specified encoded URL. Upon - failure, return a string describing the error. Else, value is - nil. */ +/* Ask the system to start browsing the specified URL. Upon failure, + return a string describing the error. Else, value is nil. URL + should be encoded unless SEND. + + If SEND, open the URL with applications that can ``send'' or + ``share'' the URL (through mail, for example.) */ Lisp_Object -android_browse_url (Lisp_Object url) +android_browse_url (Lisp_Object url, Lisp_Object send) { jobject value, string; Lisp_Object tem; @@ -6974,7 +6977,8 @@ android_browse_url (Lisp_Object url) value = (*android_java_env)->CallObjectMethod (android_java_env, emacs_service, service_class.browse_url, - string); + string, + (jboolean) !NILP (send)); android_exception_check (); ANDROID_DELETE_LOCAL_REF (string); diff --git a/src/android.h b/src/android.h index 33ca379e6ba..2323690fa25 100644 --- a/src/android.h +++ b/src/android.h @@ -178,7 +178,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. int temperature; }; -extern Lisp_Object android_browse_url (Lisp_Object); +extern Lisp_Object android_browse_url (Lisp_Object, Lisp_Object); extern int android_query_battery (struct android_battery_state *); extern void android_display_toast (const char *); diff --git a/src/androidselect.c b/src/androidselect.c index d1f2ebb52f9..f5371280457 100644 --- a/src/androidselect.c +++ b/src/androidselect.c @@ -232,11 +232,15 @@ DEFUN ("android-clipboard-exists-p", Fandroid_clipboard_exists_p, } DEFUN ("android-browse-url", Fandroid_browse_url, - Sandroid_browse_url, 1, 1, 0, - doc: /* Start the system web browser. -Then, point the web browser to URL, which should be a URL-encoded -URL with a scheme specified. Signal an error upon failure. */) - (Lisp_Object url) + Sandroid_browse_url, 1, 2, 0, + doc: /* Open URL in an external application. URL should be a +URL-encoded URL with a scheme specified unless SEND is non-nil. +Signal an error upon failure. + +If SEND is nil, start a program that is able to display the URL, such +as a web browser. Otherwise, try to share URL using programs such as +email clients. */) + (Lisp_Object url, Lisp_Object send) { Lisp_Object value; @@ -244,7 +248,7 @@ DEFUN ("android-browse-url", Fandroid_browse_url, error ("No Android display connection!"); CHECK_STRING (url); - value = android_browse_url (url); + value = android_browse_url (url, send); /* Signal an error upon failure. */ if (!NILP (value)) commit faca007b61422969f5c8888c67a1e356a8c5b64a Author: Po Lu Date: Mon Jul 10 08:24:40 2023 +0800 ; Update from Gnulib * lib/vasnprintf.c (VASNPRINTF): * m4/printf.m4 (gl_SWPRINTF_WORKS): (gl_SWPRINTF_DIRECTIVE_LA): * m4/vasnprintf.m4 (gl_PREREQ_PRINTF_PARSE): diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 63a6cd60f35..9ad31b2a084 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -247,7 +247,7 @@ local_strnlen (const char *string, size_t maxlen) # endif #endif -#if (((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && WIDE_CHAR_VERSION) || ((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS) && !WIDE_CHAR_VERSION && DCHAR_IS_TCHAR)) && HAVE_WCHAR_T +#if (((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_WPRINTF_DIRECTIVE_LC) && WIDE_CHAR_VERSION) || ((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS) && !WIDE_CHAR_VERSION && DCHAR_IS_TCHAR)) && HAVE_WCHAR_T # if HAVE_WCSLEN # define local_wcslen wcslen # else @@ -2765,14 +2765,14 @@ #define ENSURE_ALLOCATION(needed) \ } } #endif -#if WIDE_CHAR_VERSION && !DCHAR_IS_TCHAR +#if WIDE_CHAR_VERSION && (!DCHAR_IS_TCHAR || NEED_WPRINTF_DIRECTIVE_LC) else if ((dp->conversion == 's' && a.arg[dp->arg_index].type == TYPE_WIDE_STRING) || (dp->conversion == 'c' && a.arg[dp->arg_index].type == TYPE_WIDE_CHAR)) { /* %ls or %lc in vasnwprintf. See the specification of - fwprintf. */ + fwprintf. */ /* It would be silly to use snprintf ("%ls", ...) and then convert back the result from a char[] to a wchar_t[]. Instead, just copy the argument wchar_t[] to the result. */ diff --git a/m4/printf.m4 b/m4/printf.m4 index efb85a57afd..8b8f01067fc 100644 --- a/m4/printf.m4 +++ b/m4/printf.m4 @@ -1,4 +1,4 @@ -# printf.m4 serial 82 +# printf.m4 serial 84 dnl Copyright (C) 2003, 2007-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -1906,7 +1906,7 @@ AC_DEFUN int main() { int result = 0; - { /* This test fails on musl, FreeBSD, NetBSD, OpenBSD, macOS, AIX. */ + { /* This test fails on musl libc 1.2.3, FreeBSD, NetBSD, OpenBSD, macOS, AIX. */ wchar_t buf[5] = { 0xBEEF, 0xBEEF, 0xBEEF, 0xBEEF, 0xBEEF }; int ret = swprintf (buf, 4, L"%cz", '\0'); /* Expected result: @@ -1937,7 +1937,7 @@ AC_DEFUN # Guess yes on glibc systems. *-gnu* | gnu*) gl_cv_func_swprintf_works="guessing yes";; # Guess no on musl systems. - *-musl* | midipix*) gl_cv_func_swprintf_works="guessing yes";; + *-musl* | midipix*) gl_cv_func_swprintf_works="guessing no";; # Guess no on FreeBSD, NetBSD, OpenBSD, macOS, AIX. freebsd* | midnightbsd* | netbsd* | openbsd* | darwin* | aix*) gl_cv_func_swprintf_works="guessing no";; @@ -1999,8 +1999,8 @@ AC_DEFUN # Guess yes on musl systems. *-musl* | midipix*) gl_cv_func_swprintf_directive_la="guessing yes";; # Guess yes on Android. - linux*-android*) gl_cv_func_swprintf_directive_la="guessing no";; - # Guess yes on native Windows. + linux*-android*) gl_cv_func_swprintf_directive_la="guessing yes";; + # Guess no on native Windows. mingw*) gl_cv_func_swprintf_directive_la="guessing no";; # If we don't know, obey --enable-cross-guesses. *) gl_cv_func_swprintf_directive_la="$gl_cross_guess_normal";; @@ -2009,6 +2009,52 @@ AC_DEFUN ]) ]) +dnl Test whether the *wprintf family of functions supports the 'lc' conversion +dnl specifier for all wide characters. +dnl (ISO C11, POSIX:2001) +dnl Result is gl_cv_func_swprintf_directive_lc. + +AC_DEFUN([gl_SWPRINTF_DIRECTIVE_LC], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_CACHE_CHECK([whether swprintf supports the 'lc' directive], + [gl_cv_func_swprintf_directive_lc], + [ + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include +static wchar_t buf[100]; +static wint_t L_invalid = (wchar_t) 0x76543210; +int main () +{ + int result = 0; + /* This catches a musl libc 1.2.4, Android bug. + Reported at . */ + if (swprintf (buf, sizeof (buf) / sizeof (wchar_t), + L"%lc %d", L_invalid, 33, 44, 55) < 0) + result |= 1; + return result; +}]])], + [gl_cv_func_swprintf_directive_lc=yes], + [gl_cv_func_swprintf_directive_lc=no], + [case "$host_os" in + # Guess yes on glibc systems. + *-gnu* | gnu*) gl_cv_func_swprintf_directive_lc="guessing yes";; + # Guess no on musl systems. + *-musl* | midipix*) gl_cv_func_swprintf_directive_lc="guessing no";; + # Guess no on Android. + linux*-android*) gl_cv_func_swprintf_directive_lc="guessing no";; + # Guess yes on native Windows. + mingw*) gl_cv_func_swprintf_directive_lc="guessing yes";; + # If we don't know, obey --enable-cross-guesses. + *) gl_cv_func_swprintf_directive_lc="$gl_cross_guess_normal";; + esac + ]) + ]) +]) + dnl The results of these tests on various platforms are: dnl dnl 1 = gl_PRINTF_SIZES_C99 @@ -2037,6 +2083,7 @@ AC_DEFUN dnl 24 = gl_VSNPRINTF_ZEROSIZE_C99 dnl 25 = gl_SWPRINTF_WORKS dnl 26 = gl_SWPRINTF_DIRECTIVE_LA +dnl 27 = gl_SWPRINTF_DIRECTIVE_LC dnl dnl 1 = checking whether printf supports size specifiers as in C99... dnl 2 = checking whether printf supports size specifiers as in C23... @@ -2064,47 +2111,48 @@ AC_DEFUN dnl 24 = checking whether vsnprintf respects a zero size as in C99... dnl 25 = checking whether swprintf works... dnl 26 = checking whether swprintf supports the 'La' and 'LA' directives... +dnl 27 = checking whether swprintf supports the 'lc' directive... dnl dnl . = yes, # = no. dnl -dnl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 -dnl musl libc 1.2.3 . # . . . . # # . . . . . . . . . . . . . . . . # . -dnl glibc 2.35 . # . . . . . . . . . # . . . . . . . . . . . . . . -dnl glibc 2.5 . # . . . . # # . . . # . . . . . . . . . . . . . # -dnl glibc 2.3.6 . # . . . # # # . . . # . . . . . . . . . . . . . # -dnl FreeBSD 13.0 . # . . . # # # . . . # . . . . . # . . . . . . # . -dnl FreeBSD 5.4, 6.1 . # . . . # # # . . . # . . . # . # . . . . . . # ? -dnl Mac OS X 10.13.5 . # . . # # # # . # . # . . . . . . . . . # . . # ? -dnl Mac OS X 10.5.8 . # . . # # # # . . . # . . . # . . . . . . . . # ? -dnl Mac OS X 10.3.9 . # . . . # # # . . . # . . . # . # . . . . . . # ? -dnl OpenBSD 6.0, 6.7 . # . . . # # # . . . # . . . . . # . . . . . . # . -dnl OpenBSD 3.9, 4.0 . # . # # # # # # . # # . # . # . # . . . . . . # ? -dnl Cygwin 1.7.0 (2009) . # . . # . # # . . ? ? . . . . . ? . . . . . . ? ? -dnl Cygwin 1.5.25 (2008) . # . . # # # # . . # ? . . . . . # . . . . . . ? ? -dnl Cygwin 1.5.19 (2006) # # . . # # # # # . # ? . # . # # # . . . . . . ? ? -dnl Solaris 11.4 . # . # # # # # . . # # . . . # . . . . . . . . . ? -dnl Solaris 11.3 . # . . . # # # . . # # . . . . . . . . . . . . . ? -dnl Solaris 11.0 . # . # # # # # . . # # . . . # . . . . . . . . ? ? -dnl Solaris 10 . # . # # # # # . . # # . . . # # . . . . . . . ? ? -dnl Solaris 2.6 ... 9 # # . # # # # # # . # # . . . # # . . . # . . . ? ? -dnl Solaris 2.5.1 # # . # # # # # # . # # . . . # . . # # # # # # ? ? -dnl AIX 7.1 . # . # # # # # . . . # . . . # # . . . . . . . # . -dnl AIX 5.2 . # . # # # # # . . . # . . . # . . . . . . . . # ? -dnl AIX 4.3.2, 5.1 # # . # # # # # # . . # . . . # . . . . # . . . # ? -dnl HP-UX 11.31 . # . . . # # # . . . ? . . . # . . . . # # . . ? ? -dnl HP-UX 11.{00,11,23} # # . . . # # # # . . ? . . . # . . . . # # . # ? ? -dnl HP-UX 10.20 # # . # . # # # # . ? ? . . # # . . . . # # ? # ? ? -dnl IRIX 6.5 # # . # # # # # # . # # . . . # . . . . # . . . # ? -dnl OSF/1 5.1 # # . # # # # # # . . ? . . . # . . . . # . . # ? ? -dnl OSF/1 4.0d # # . # # # # # # . . ? . . . # . . # # # # # # ? ? -dnl NetBSD 9.0 . # . . . # # # . . . # . . . . . . . . . . . . # . -dnl NetBSD 5.0 . # . . # # # # . . . # . . . # . # . . . . . . # ? -dnl NetBSD 4.0 . # ? ? ? ? # # ? . ? # . ? ? ? ? ? . . . ? ? ? # ? -dnl NetBSD 3.0 . # . . . # # # # . ? # # # ? # . # . . . . . . # ? -dnl Haiku . # . . # # # # # . # ? . . . . . ? . . ? . . . . # -dnl BeOS # # # . # # # # # . ? ? # . ? . # ? . . ? . . . ? ? -dnl Android 4.3 . # . # # # # # # # # ? . # . # . # . . . # . . ? ? -dnl old mingw / msvcrt # # # # # # # # # . . ? # # . # # ? . # # # . . # ? -dnl MSVC 9 # # # # # # # # # # . ? # # . # # ? # # # # . . # ? -dnl mingw 2009-2011 . # # . # . # # . . . ? # # . . . ? . . . . . . # ? -dnl mingw-w64 2011 # # # # # # # # # . . ? # # . # # ? . # # # . . # ? +dnl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 +dnl musl libc 1.2.3 . # . . . . # # . . . . . . . . . . . . . . . . # . # +dnl glibc 2.35 . # . . . . . . . . . # . . . . . . . . . . . . . . . +dnl glibc 2.5 . # . . . . # # . . . # . . . . . . . . . . . . . # . +dnl glibc 2.3.6 . # . . . # # # . . . # . . . . . . . . . . . . . # . +dnl FreeBSD 13.0 . # . . . # # # . . . # . . . . . # . . . . . . # . # +dnl FreeBSD 5.4, 6.1 . # . . . # # # . . . # . . . # . # . . . . . . # ? ? +dnl Mac OS X 10.13.5 . # . . # # # # . # . # . . . . . . . . . # . . # ? ? +dnl Mac OS X 10.5.8 . # . . # # # # . . . # . . . # . . . . . . . . # ? ? +dnl Mac OS X 10.3.9 . # . . . # # # . . . # . . . # . # . . . . . . # ? ? +dnl OpenBSD 6.0, 6.7 . # . . . # # # . . . # . . . . . # . . . . . . # . # +dnl OpenBSD 3.9, 4.0 . # . # # # # # # . # # . # . # . # . . . . . . # ? ? +dnl Cygwin 1.7.0 (2009) . # . . # . # # . . ? ? . . . . . ? . . . . . . ? ? ? +dnl Cygwin 1.5.25 (2008) . # . . # # # # . . # ? . . . . . # . . . . . . ? ? ? +dnl Cygwin 1.5.19 (2006) # # . . # # # # # . # ? . # . # # # . . . . . . ? ? ? +dnl Solaris 11.4 . # . # # # # # . . # # . . . # . . . . . . . . . # . +dnl Solaris 11.3 . # . . . # # # . . # # . . . . . . . . . . . . . # . +dnl Solaris 11.0 . # . # # # # # . . # # . . . # . . . . . . . . ? ? ? +dnl Solaris 10 . # . # # # # # . . # # . . . # # . . . . . . . . # . +dnl Solaris 2.6 ... 9 # # . # # # # # # . # # . . . # # . . . # . . . ? ? ? +dnl Solaris 2.5.1 # # . # # # # # # . # # . . . # . . # # # # # # ? ? ? +dnl AIX 7.1 . # . # # # # # . . . # . . . # # . . . . . . . # . . +dnl AIX 5.2 . # . # # # # # . . . # . . . # . . . . . . . . # ? ? +dnl AIX 4.3.2, 5.1 # # . # # # # # # . . # . . . # . . . . # . . . # ? ? +dnl HP-UX 11.31 . # . . . # # # . . . ? . . . # . . . . # # . . ? ? ? +dnl HP-UX 11.{00,11,23} # # . . . # # # # . . ? . . . # . . . . # # . # ? ? ? +dnl HP-UX 10.20 # # . # . # # # # . ? ? . . # # . . . . # # ? # ? ? ? +dnl IRIX 6.5 # # . # # # # # # . # # . . . # . . . . # . . . # ? ? +dnl OSF/1 5.1 # # . # # # # # # . . ? . . . # . . . . # . . # ? ? ? +dnl OSF/1 4.0d # # . # # # # # # . . ? . . . # . . # # # # # # ? ? ? +dnl NetBSD 9.0 . # . . . # # # . . . # . . . . . . . . . . . . # . # +dnl NetBSD 5.0 . # . . # # # # . . . # . . . # . # . . . . . . # ? ? +dnl NetBSD 4.0 . # ? ? ? ? # # ? . ? # . ? ? ? ? ? . . . ? ? ? # ? ? +dnl NetBSD 3.0 . # . . . # # # # . ? # # # ? # . # . . . . . . # ? ? +dnl Haiku . # . . # # # # # . # ? . . . . . ? . . ? . . . . # . +dnl BeOS # # # . # # # # # . ? ? # . ? . # ? . . ? . . . ? ? ? +dnl Android 4.3 . # . # # # # # # # # ? . # . # . # . . . # . . ? ? ? +dnl old mingw / msvcrt # # # # # # # # # . . ? # # . # # ? . # # # . . # ? ? +dnl MSVC 9 # # # # # # # # # # . ? # # . # # ? # # # # . . # ? ? +dnl mingw 2009-2011 . # # . # . # # . . . ? # # . . . ? . . . . . . # ? ? +dnl mingw-w64 2011 # # # # # # # # # . . ? # # . # # ? . # # # . . # ? ? diff --git a/m4/vasnprintf.m4 b/m4/vasnprintf.m4 index 639b29a1f39..df87b9e7986 100644 --- a/m4/vasnprintf.m4 +++ b/m4/vasnprintf.m4 @@ -1,4 +1,4 @@ -# vasnprintf.m4 serial 49 +# vasnprintf.m4 serial 50 dnl Copyright (C) 2002-2004, 2006-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -107,13 +107,56 @@ AC_DEFUN_ONCE esac gl_MBRTOWC_C_LOCALE case "$gl_cv_func_mbrtowc_C_locale_sans_EILSEQ" in - *yes) ;; - *) - AC_DEFINE([NEED_WPRINTF_DIRECTIVE_C], [1], - [Define if the vasnwprintf implementation needs special code for - the 'c' directive.]) + *yes) + AC_CACHE_CHECK([whether swprintf in the C locale is free of encoding errors], + [gl_cv_func_swprintf_C_locale_sans_EILSEQ], + [ + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#ifndef __USE_MINGW_ANSI_STDIO +# define __USE_MINGW_ANSI_STDIO 1 +#endif +#include +#include +int main() +{ + int result = 0; + { /* This test fails on glibc 2.35, musl libc 1.2.4, FreeBSD 13.2, NetBSD 9.3, + OpenBSD 7.2, Cygwin 2.9.0. + Reported at . */ + wchar_t buf[12]; + int ret = swprintf (buf, 12, L"%c", '\377'); + if (ret < 0) + result |= 1; + } + return result; +}]])], + [gl_cv_func_swprintf_C_locale_sans_EILSEQ=yes], + [gl_cv_func_swprintf_C_locale_sans_EILSEQ=no], + [case "$host_os" in + # Guess no on glibc systems. + *-gnu* | gnu*) gl_cv_func_swprintf_C_locale_sans_EILSEQ="guessing yes";; + # Guess no on musl systems. + *-musl* | midipix*) gl_cv_func_swprintf_C_locale_sans_EILSEQ="guessing no";; + # If we don't know, obey --enable-cross-guesses. + *) gl_cv_func_swprintf_C_locale_sans_EILSEQ="$gl_cross_guess_normal";; + esac + ]) + ]) ;; esac + if case "$gl_cv_func_mbrtowc_C_locale_sans_EILSEQ" in + *yes) false ;; + *) true ;; + esac \ + || case "$gl_cv_func_swprintf_C_locale_sans_EILSEQ" in + *yes) false ;; + *) true ;; + esac; then + AC_DEFINE([NEED_WPRINTF_DIRECTIVE_C], [1], + [Define if the vasnwprintf implementation needs special code for + the 'c' directive.]) + fi gl_SWPRINTF_DIRECTIVE_LA case "$gl_cv_func_swprintf_directive_la" in *yes) ;; @@ -123,6 +166,15 @@ AC_DEFUN_ONCE the 'a' directive with 'long double' arguments.]) ;; esac + gl_SWPRINTF_DIRECTIVE_LC + case "$gl_cv_func_swprintf_directive_lc" in + *yes) ;; + *) + AC_DEFINE([NEED_WPRINTF_DIRECTIVE_LC], [1], + [Define if the vasnwprintf implementation needs special code for + the 'lc' directive.]) + ;; + esac gl_MUSL_LIBC gl_PREREQ_VASNXPRINTF ]) commit 8dd34ad8d80b0bd07cf59add94bfb6a6e52098b2 Merge: ed5ade097e5 b76878cfab1 Author: Po Lu Date: Mon Jul 10 08:21:59 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit ed5ade097e5b675167f9c7e4916a4beec62d118a Author: Po Lu Date: Sun Jul 9 13:13:53 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsWindow.java (eventModifiers) (motionEventModifiers): New functions. (onKeyDown, onKeyUp, onFocusChanged, onSomeKindOfMotionEvent): Don't record the previous modifier mask; instead, always use the modifier state specified in the event. * src/androidterm.c (handle_one_android_event): Don't dispatch button release events when a popup is active. diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 2d8a8627468..15d5fe8a175 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -99,9 +99,8 @@ private static class Coordinate private EmacsGC scratchGC; /* The button state and keyboard modifier mask at the time of the - last button press or release event. The modifier mask is reset - upon each window focus change. */ - public int lastButtonState, lastModifiers; + last button press or release event. */ + public int lastButtonState; /* Whether or not the window is mapped. */ private volatile boolean isMapped; @@ -562,15 +561,16 @@ private static class Coordinate eventStrings.put (serial, string); } - /* event.getCharacters is used because older input methods still - require it. */ - @SuppressWarnings ("deprecation") - public void - onKeyDown (int keyCode, KeyEvent event) + + + /* Return the modifier mask associated with the specified keyboard + input EVENT. Replace bits corresponding to Left or Right keys + with their corresponding general modifier bits. */ + + private int + eventModifiers (KeyEvent event) { - int state, state_1; - long serial; - String characters; + int state; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) state = event.getModifiers (); @@ -592,6 +592,46 @@ private static class Coordinate state |= KeyEvent.META_CTRL_MASK; } + return state; + } + + /* Return the modifier mask associated with the specified motion + EVENT. Replace bits corresponding to Left or Right keys with + their corresponding general modifier bits. */ + + private int + motionEventModifiers (MotionEvent event) + { + int state; + + state = event.getMetaState (); + + /* Normalize the state by setting the generic modifier bit if + either a left or right modifier is pressed. */ + + if ((state & KeyEvent.META_ALT_LEFT_ON) != 0 + || (state & KeyEvent.META_ALT_RIGHT_ON) != 0) + state |= KeyEvent.META_ALT_MASK; + + if ((state & KeyEvent.META_CTRL_LEFT_ON) != 0 + || (state & KeyEvent.META_CTRL_RIGHT_ON) != 0) + state |= KeyEvent.META_CTRL_MASK; + + return state; + } + + /* event.getCharacters is used because older input methods still + require it. */ + @SuppressWarnings ("deprecation") + public void + onKeyDown (int keyCode, KeyEvent event) + { + int state, state_1; + long serial; + String characters; + + state = eventModifiers (event); + /* Ignore meta-state understood by Emacs for now, or Ctrl+C will not be recognized as an ASCII key press event. */ state_1 @@ -605,7 +645,6 @@ private static class Coordinate state, keyCode, getEventUnicodeChar (event, state_1)); - lastModifiers = state; characters = event.getCharacters (); @@ -620,25 +659,8 @@ private static class Coordinate int state, state_1; long time; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) - state = event.getModifiers (); - else - { - /* Replace this with getMetaState and manual - normalization. */ - state = event.getMetaState (); - - /* Normalize the state by setting the generic modifier bit if - either a left or right modifier is pressed. */ - - if ((state & KeyEvent.META_ALT_LEFT_ON) != 0 - || (state & KeyEvent.META_ALT_RIGHT_ON) != 0) - state |= KeyEvent.META_ALT_MASK; - - if ((state & KeyEvent.META_CTRL_LEFT_ON) != 0 - || (state & KeyEvent.META_CTRL_RIGHT_ON) != 0) - state |= KeyEvent.META_CTRL_MASK; - } + /* Compute the event's modifier mask. */ + state = eventModifiers (event); /* Ignore meta-state understood by Emacs for now, or Ctrl+C will not be recognized as an ASCII key press event. */ @@ -650,7 +672,6 @@ private static class Coordinate state, keyCode, getEventUnicodeChar (event, state_1)); - lastModifiers = state; if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) { @@ -671,12 +692,6 @@ private static class Coordinate onFocusChanged (boolean gainFocus) { EmacsActivity.invalidateFocus (); - - /* If focus has been lost, reset the keyboard modifier state, as - subsequent changes will not be recorded. */ - - if (!gainFocus) - lastModifiers = 0; } /* Notice that the activity has been detached or destroyed. @@ -940,7 +955,7 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) EmacsNative.sendButtonPress (this.handle, (int) event.getX (), (int) event.getY (), event.getEventTime (), - lastModifiers, + motionEventModifiers (event), whatButtonWasIt (event, true)); if (Build.VERSION.SDK_INT @@ -955,7 +970,7 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) EmacsNative.sendButtonRelease (this.handle, (int) event.getX (), (int) event.getY (), event.getEventTime (), - lastModifiers, + motionEventModifiers (event), whatButtonWasIt (event, false)); if (Build.VERSION.SDK_INT @@ -988,7 +1003,7 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) EmacsNative.sendButtonRelease (this.handle, (int) event.getX (), (int) event.getY (), event.getEventTime (), - lastModifiers, + motionEventModifiers (event), whatButtonWasIt (event, false)); lastButtonState = event.getButtonState (); } @@ -1000,7 +1015,7 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) EmacsNative.sendWheel (this.handle, (int) event.getX (), (int) event.getY (), event.getEventTime (), - lastModifiers, + motionEventModifiers (event), event.getAxisValue (MotionEvent.AXIS_HSCROLL), event.getAxisValue (MotionEvent.AXIS_VSCROLL)); return true; diff --git a/src/androidterm.c b/src/androidterm.c index 466a99a1e28..135eda45245 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -1315,12 +1315,13 @@ handle_one_android_event (struct android_display_info *dpyinfo, } if (!(tab_bar_p && NILP (tab_bar_arg)) && !tool_bar_p) - { - android_construct_mouse_click (&inev.ie, &event->xbutton, f); + if (! popup_activated ()) + { + android_construct_mouse_click (&inev.ie, &event->xbutton, f); - if (!NILP (tab_bar_arg)) - inev.ie.arg = tab_bar_arg; - } + if (!NILP (tab_bar_arg)) + inev.ie.arg = tab_bar_arg; + } } if (event->type == ANDROID_BUTTON_PRESS) commit 97f926b82d5674f90a16327b0ad4d6b226fcacb4 Author: Po Lu Date: Sun Jul 9 12:50:15 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsService.java (onStartCommand): Fix typo in notification message. * java/org/gnu/emacs/EmacsWindow.java (onFocusChanged): Reset the recorded modifier state upon a change to the window focus. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index a7e83e276f2..0543c3a1bdd 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -147,7 +147,7 @@ public final class EmacsService extends Service manager = (NotificationManager) tem; infoBlurb = ("This notification is displayed to keep Emacs" + " running while it is in the background. You" - + " may disable if you want;" + + " may disable it if you want;" + " see (emacs)Android Environment."); channel = new NotificationChannel ("emacs", "Emacs persistent notification", diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 739a1f43b7d..2d8a8627468 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -99,7 +99,8 @@ private static class Coordinate private EmacsGC scratchGC; /* The button state and keyboard modifier mask at the time of the - last button press or release event. */ + last button press or release event. The modifier mask is reset + upon each window focus change. */ public int lastButtonState, lastModifiers; /* Whether or not the window is mapped. */ @@ -670,6 +671,12 @@ private static class Coordinate onFocusChanged (boolean gainFocus) { EmacsActivity.invalidateFocus (); + + /* If focus has been lost, reset the keyboard modifier state, as + subsequent changes will not be recorded. */ + + if (!gainFocus) + lastModifiers = 0; } /* Notice that the activity has been detached or destroyed. commit 8afb5644fd21162f8fd6c39f111844b79e002b7c Author: Po Lu Date: Sun Jul 9 10:06:18 2023 +0800 ; * java/org/gnu/emacs/EmacsService.java (onCreate): Fix typo. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index f484e2c9ca3..a7e83e276f2 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -212,6 +212,7 @@ public final class EmacsService extends Service final double pixelDensityX; final double pixelDensityY; final double scaledDensity; + double tempScaledDensity; SERVICE = this; handler = new Handler (Looper.getMainLooper ()); @@ -220,9 +221,9 @@ public final class EmacsService extends Service metrics = getResources ().getDisplayMetrics (); pixelDensityX = metrics.xdpi; pixelDensityY = metrics.ydpi; - scaledDensity = ((metrics.scaledDensity - / metrics.density) - * pixelDensityX); + tempScaledDensity = ((metrics.scaledDensity + / metrics.density) + * pixelDensityX); resolver = getContentResolver (); /* If the density used to compute the text size is lesser than @@ -233,8 +234,12 @@ public final class EmacsService extends Service corresponds to 1 pixel, not 72 or 96 as used elsewhere. This difference is codified in PT_PER_INCH defined in font.h. */ - if (scaledDensity < 160) - scaledDensity = 160; + if (tempScaledDensity < 160) + tempScaledDensity = 160; + + /* scaledDensity is const as required to refer to it from within + the nested function below. */ + scaledDensity = tempScaledDensity; try { commit 105c876a609d4610ce684c7513b8548feae3638a Author: Po Lu Date: Sun Jul 9 10:05:08 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsDrawPoint.java (perform): Don't fill an extra pixel. * java/org/gnu/emacs/EmacsService.java (onCreate): Make sure scaledDensity is always at least 160 dpi. diff --git a/java/org/gnu/emacs/EmacsDrawPoint.java b/java/org/gnu/emacs/EmacsDrawPoint.java index de8ddf09cc4..6a1cb744d60 100644 --- a/java/org/gnu/emacs/EmacsDrawPoint.java +++ b/java/org/gnu/emacs/EmacsDrawPoint.java @@ -25,7 +25,10 @@ public final class EmacsDrawPoint perform (EmacsDrawable drawable, EmacsGC immutableGC, int x, int y) { - EmacsDrawRectangle.perform (drawable, immutableGC, + /* Use EmacsFillRectangle instead of EmacsDrawRectangle, as the + latter actually draws a rectangle one pixel wider than + specified. */ + EmacsFillRectangle.perform (drawable, immutableGC, x, y, 1, 1); } } diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 62fd2740286..f484e2c9ca3 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -225,6 +225,17 @@ public final class EmacsService extends Service * pixelDensityX); resolver = getContentResolver (); + /* If the density used to compute the text size is lesser than + 160, there's likely a bug with display density computation. + Reset it to 160 in that case. + + Note that Android uses 160 ``dpi'' as the density where 1 point + corresponds to 1 pixel, not 72 or 96 as used elsewhere. This + difference is codified in PT_PER_INCH defined in font.h. */ + + if (scaledDensity < 160) + scaledDensity = 160; + try { /* Configure Emacs with the asset manager and other necessary @@ -240,7 +251,9 @@ invocation of app_process (through android-emacs) can Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir + ", libDir = " + libDir + ", and classPath = " + classPath - + "; fileToOpen = " + EmacsOpenActivity.fileToOpen); + + "; fileToOpen = " + EmacsOpenActivity.fileToOpen + + "; display density: " + pixelDensityX + " by " + + pixelDensityY + " scaled to " + scaledDensity); /* Start the thread that runs Emacs. */ thread = new EmacsThread (this, new Runnable () { commit f812d92f67a28f49823ff8414bb9bcfb3eb82676 Merge: a36207574ac 9c282faf26e Author: Po Lu Date: Sun Jul 9 08:40:38 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit a36207574ac436fd8871639388cc6400069bb9bb Author: Po Lu Date: Sat Jul 8 21:49:21 2023 +0800 Fix EmacsDrawLine again * java/org/gnu/emacs/EmacsDrawLine.java (perform): Symmetrically adjust coordinates to cover the last pixel. Then, fill the left most pixel of the line. diff --git a/java/org/gnu/emacs/EmacsDrawLine.java b/java/org/gnu/emacs/EmacsDrawLine.java index 3f5067c0bdd..d367ccff9c4 100644 --- a/java/org/gnu/emacs/EmacsDrawLine.java +++ b/java/org/gnu/emacs/EmacsDrawLine.java @@ -32,30 +32,45 @@ public final class EmacsDrawLine Rect rect; Canvas canvas; Paint paint; + int x0, x1, y0, y1; /* TODO implement stippling. */ if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) return; + /* Calculate the leftmost and rightmost points. */ + + x0 = Math.min (x, x2 + 1); + x1 = Math.max (x, x2 + 1); + y0 = Math.min (y, y2 + 1); + y1 = Math.max (y, y2 + 1); + + /* And the clip rectangle. */ + paint = gc.gcPaint; - rect = new Rect (Math.min (x, x2 + 1), - Math.min (y, y2 + 1), - Math.max (x2 + 1, x), - Math.max (y2 + 1, y)); + rect = new Rect (x0, y0, x1, y1); canvas = drawable.lockCanvas (gc); if (canvas == null) return; - paint.setStyle (Paint.Style.STROKE); + paint.setStyle (Paint.Style.FILL); /* Since drawLine has PostScript style behavior, adjust the - coordinates appropriately. */ + coordinates appropriately. + + The left most pixel of a straight line is always partially + filled. Patch it in manually. */ if (gc.clip_mask == null) - canvas.drawLine ((float) x, (float) y + 0.5f, - (float) x2 + 0.5f, (float) y2 + 0.5f, - paint); + { + canvas.drawLine ((float) x + 0.5f, (float) y + 0.5f, + (float) x2 + 0.5f, (float) y2 + 0.5f, + paint); + + if (x2 > x) + canvas.drawRect (new Rect (x, y, x + 1, y + 1), paint); + } /* DrawLine with clip mask not implemented; it is not used by Emacs. */ commit c843f3e23b48dcf65e72db0eefa6a5a74c815ff6 Author: Po Lu Date: Sat Jul 8 10:15:38 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsService.java (DEBUG_IC) (DEBUG_THREADS): Improve commentary. * src/androidterm.c (handle_one_android_event): Signal completion of IME events that have lost their frames. (requestCursorUpdates): Don't set an edit counter as this event won't be passed to the text conversion machinery. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 37048960f25..62fd2740286 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -96,11 +96,11 @@ public final class EmacsService extends Service public DisplayMetrics metrics; /* Flag that says whether or not to print verbose debugging - information. */ + information when responding to an input method. */ public static final boolean DEBUG_IC = false; - /* Flag that says whether or not to perform extra checks on threads - performing drawing calls. */ + /* Flag that says whether or not to stringently check that only the + Emacs thread is performing drawing calls. */ private static final boolean DEBUG_THREADS = false; /* Atomic integer used for synchronization between diff --git a/src/androidterm.c b/src/androidterm.c index 20a8860a913..466a99a1e28 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -774,6 +774,11 @@ android_handle_ime_event (union android_event *event, struct frame *f) } } + + +/* Forward declaration. */ +static void android_notify_conversion (unsigned long); + static int handle_one_android_event (struct android_display_info *dpyinfo, union android_event *event, int *finish, @@ -1659,8 +1664,21 @@ handle_one_android_event (struct android_display_info *dpyinfo, case ANDROID_INPUT_METHOD: if (!any) - /* Free any text allocated for this event. */ - xfree (event->ime.text); + { + /* Free any text allocated for this event. */ + xfree (event->ime.text); + + /* If edits associated with this event haven't been + processed yet, signal their completion to avoid delays + the next time a call to `android_sync_edit' is made. + + If events for a deleted frame are interleaved with events + for another frame, the edit counter may be prematurely + incremented before edits associated with the other frames + are processed. This is not a problem in practice. */ + + android_notify_conversion (event->ime.counter); + } else android_handle_ime_event (event, any); @@ -4585,10 +4603,12 @@ android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1) android_sync_edit (void) { struct timespec start, end, rem; + unsigned long counter; + + counter = __atomic_load_n (&last_edit_counter, + __ATOMIC_SEQ_CST); - if (__atomic_load_n (&last_edit_counter, - __ATOMIC_SEQ_CST) - == edit_counter) + if (counter == edit_counter) return; start = current_timespec (); @@ -5618,7 +5638,10 @@ NATIVE_NAME (requestCursorUpdates) (JNIEnv *env, jobject object, event.ime.length = mode; event.ime.position = 0; event.ime.text = NULL; - event.ime.counter = ++edit_counter; + + /* Since this does not affect the state of the buffer text, there is + no need to apply synchronization to this event. */ + event.ime.counter = 0; android_write_event (&event); } commit da27837d99fe2dd4c3825598b465107550a06a9d Author: Po Lu Date: Sat Jul 8 08:55:24 2023 +0800 ; Fix whitespace * src/android.c (android_blit_xor): (android_check_query_urgent): (android_run_in_emacs_thread): (android_update_extracted_text): Fix whitespace. diff --git a/src/android.c b/src/android.c index a6bc8217820..5850b6079c9 100644 --- a/src/android.c +++ b/src/android.c @@ -4815,8 +4815,8 @@ android_blit_xor (int src_x, int src_y, int width, int height, temp = MIN (mask_info->width, width); while (temp--) *long_dst++ = ((*(long_src++) - & (0u - (*(mask_current++) & 1))) - & 0xffffff); + & (0u - (*(mask_current++) & 1))) + & 0xffffff); } src_current += src_info->stride; @@ -7187,7 +7187,7 @@ android_check_query_urgent (void) return; __android_log_print (ANDROID_LOG_VERBOSE, __func__, - "Responding to urgent query..."); + "Responding to urgent query..."); if (!__atomic_load_n (&android_servicing_query, __ATOMIC_ACQUIRE)) return; @@ -7407,7 +7407,7 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) query. */ eassert (!__atomic_load_n (&android_servicing_query, - __ATOMIC_ACQUIRE) + __ATOMIC_ACQUIRE) || (__atomic_load_n (&android_servicing_query, __ATOMIC_ACQUIRE) == 2)); @@ -7500,13 +7500,12 @@ android_update_extracted_text (android_window window, void *text, emacs_service, service_class.class, method, object, - /* N.B. that - text is not - jobject, - because that - type is not - available in - androidgui.h. */ + /* N.B. that text is + not jobject, + because that type + is not available + in + androidgui.h. */ (jobject) text, (jint) token); android_exception_check_1 (text); commit dcf0fc2c434e055338b025572c4d06fcd50ca5e2 Merge: e40dca8361d 37101780243 Author: Po Lu Date: Sat Jul 8 08:53:55 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit e40dca8361dfa8aafbe17bfeb8ad06b6ba5230ad Author: Po Lu Date: Fri Jul 7 14:22:38 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsSurfaceView.java (copyToFrontBuffer): Use fallback bit blit function on Android 7.0 as well, as crashes have been observed in drawBitmap. diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index 5b3e05eb9f4..3f62af4ab99 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -76,7 +76,9 @@ public final class EmacsSurfaceView extends View EmacsService.checkEmacsThread (); if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O - && Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) + && Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1 + && Build.VERSION.SDK_INT != Build.VERSION_CODES.N_MR1 + && Build.VERSION.SDK_INT != Build.VERSION_CODES.N) { /* If `drawBitmap' can safely be used while a bitmap is locked by another thread, continue here... */ @@ -89,8 +91,8 @@ public final class EmacsSurfaceView extends View } else { - /* But if it can not, as on Android 8.0 and 8.1, then use a - replacement function. */ + /* But if it can not, as on Android 7.0 through 8.1, then use + a replacement function. */ if (damageRect != null) EmacsNative.blitRect (bitmap, frontBuffer, commit 4db8a10648acceac88052b25b9f380d5aad803ee Author: Po Lu Date: Fri Jul 7 10:00:56 2023 +0800 Update Android port * lisp/tool-bar.el (modifier-bar-modifier-list): New variable. (modifier-bar-button): New function. (tool-bar-event-apply-alt-modifier) (tool-bar-event-apply-super-modifier) (tool-bar-event-apply-hyper-modifier) (tool-bar-event-apply-shift-modifier) (tool-bar-event-apply-control-modifier) (tool-bar-event-apply-meta-modifier): Factor out modifier bar logic to that function, and redisplay the tool bar where necessary. (modifier-bar-available-p): New function. (modifier-bar-mode): Disable tool bar items corresponding to modifier keys that've already been pressed. * etc/images/shift.pbm: Regenerate from correct font. diff --git a/etc/images/shift.pbm b/etc/images/shift.pbm index 53128f56d96..fbb1f4abe06 100644 Binary files a/etc/images/shift.pbm and b/etc/images/shift.pbm differ diff --git a/lisp/tool-bar.el b/lisp/tool-bar.el index 1ede5c45971..ab81a21211d 100644 --- a/lisp/tool-bar.el +++ b/lisp/tool-bar.el @@ -378,10 +378,15 @@ tool-bar-setup -;; Modifier mode. +;; Modifier bar mode. ;; This displays a small tool bar containing modifier keys ;; above or below the main tool bar itself. +(defvar modifier-bar-modifier-list nil + "List of modifiers that are currently applied. +Each symbol in this list represents a modifier button that has +been pressed as part of decoding this key sequence.") + (declare-function set-text-conversion-style "textconv.c") ;; These functions are very similar to their counterparts in @@ -410,12 +415,23 @@ tool-bar-apply-modifiers (defvar overriding-text-conversion-style) -(defun tool-bar-event-apply-alt-modifier (_ignore-prompt) - "Like `event-apply-alt-modifier'. -However, take additional modifier tool bar items into account; -apply any extra modifiers bound to subsequent `tool-bar' events." +(defun modifier-bar-button (init-modifier-list) + "Decode the key sequence associated with a modifier bar button. +INIT-MODIFIER-LIST is a list of one symbol describing the button +being pressed. + +Bind `modifier-bar-modifier-list' to INIT-MODIFIER-LIST. Read +events, adding each subsequent modifier bar event's associated +modifier to that list while updating the tool bar to disable +buttons that were pressed. Return any other event read with all +modifier keys read applied. + +Temporarily disable text conversion and display the on screen +keyboard while doing so." ;; Save the previously used text conversion style. - (let ((old-text-conversion-style text-conversion-style)) + (let ((old-text-conversion-style text-conversion-style) + ;; Clear the list of modifiers currently pressed. + (modifier-bar-modifier-list init-modifier-list)) ;; Disable text conversion. (when (fboundp 'set-text-conversion-style) (set-text-conversion-style nil)) @@ -423,7 +439,9 @@ tool-bar-event-apply-alt-modifier (progn ;; Display the on screen keyboard. (frame-toggle-on-screen-keyboard nil nil) - (let* ((modifiers '(alt)) event1 + ;; Update the tool bar to disable this modifier key. + (force-mode-line-update) + (let* ((modifiers init-modifier-list) event1 (overriding-text-conversion-style nil) (event (read-event))) ;; Combine any more modifier key presses. @@ -435,7 +453,14 @@ tool-bar-event-apply-alt-modifier ;; If `event' is the name of a modifier key, apply that ;; modifier key as well. (unless (memq event1 modifiers) - (push event1 modifiers)) + (push event1 modifiers) + ;; This list is used to check which tool bar buttons + ;; need to be enabled. + (push event1 modifier-bar-modifier-list)) + ;; Update the tool bar to disable the modifier button + ;; that was read. + (force-mode-line-update) + (redisplay) ;; Read another event. (setq event (read-event))) ;; EVENT is a keyboard event to which the specified list of @@ -444,187 +469,52 @@ tool-bar-event-apply-alt-modifier ;; Re-enable text conversion if necessary. (unless (or (not (fboundp 'set-text-conversion-style)) (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style t))))) + (set-text-conversion-style old-text-conversion-style t)) + ;; Re-enable all modifier bar buttons which may have been + ;; disabled. + (force-mode-line-update)))) + +(defun tool-bar-event-apply-alt-modifier (_ignore-prompt) + "Like `event-apply-alt-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + (modifier-bar-button '(alt))) (defun tool-bar-event-apply-super-modifier (_ignore-prompt) "Like `event-apply-super-modifier'. However, take additional modifier tool bar items into account; apply any extra modifiers bound to subsequent `tool-bar' events." - ;; Save the previously used text conversion style. - (let ((old-text-conversion-style text-conversion-style)) - ;; Disable text conversion. - (when (fboundp 'set-text-conversion-style) - (set-text-conversion-style nil)) - (unwind-protect - (progn - ;; Display the on screen keyboard. - (frame-toggle-on-screen-keyboard nil nil) - (let* ((modifiers '(super)) event1 - (overriding-text-conversion-style nil) - (event (read-event))) - ;; Combine any more modifier key presses. - (while (eq event 'tool-bar) - (setq event1 (event-basic-type (read-event))) - ;; Reject unknown tool bar events. - (unless (memq event1 '(alt super hyper shift control meta)) - (user-error "Unknown tool-bar event %s" event1)) - ;; If `event' is the name of a modifier key, apply that - ;; modifier key as well. - (unless (memq event1 modifiers) - (push event1 modifiers)) - ;; Read another event. - (setq event (read-event))) - ;; EVENT is a keyboard event to which the specified list of - ;; modifier keys should be applied. - (vector (tool-bar-apply-modifiers event modifiers)))) - ;; Re-enable text conversion if necessary. - (unless (or (not (fboundp 'set-text-conversion-style)) - (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style t))))) + (modifier-bar-button '(super))) (defun tool-bar-event-apply-hyper-modifier (_ignore-prompt) "Like `event-apply-hyper-modifier'. However, take additional modifier tool bar items into account; apply any extra modifiers bound to subsequent `tool-bar' events." - ;; Save the previously used text conversion style. - (let ((old-text-conversion-style text-conversion-style)) - ;; Disable text conversion. - (when (fboundp 'set-text-conversion-style) - (set-text-conversion-style nil)) - (unwind-protect - (progn - ;; Display the on screen keyboard. - (frame-toggle-on-screen-keyboard nil nil) - (let* ((modifiers '(hyper)) event1 - (overriding-text-conversion-style nil) - (event (read-event))) - ;; Combine any more modifier key presses. - (while (eq event 'tool-bar) - (setq event1 (event-basic-type (read-event))) - ;; Reject unknown tool bar events. - (unless (memq event1 '(alt super hyper shift control meta)) - (user-error "Unknown tool-bar event %s" event1)) - ;; If `event' is the name of a modifier key, apply that - ;; modifier key as well. - (unless (memq event1 modifiers) - (push event1 modifiers)) - ;; Read another event. - (setq event (read-event))) - ;; EVENT is a keyboard event to which the specified list of - ;; modifier keys should be applied. - (vector (tool-bar-apply-modifiers event modifiers)))) - ;; Re-enable text conversion if necessary. - (unless (or (not (fboundp 'set-text-conversion-style)) - (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style t))))) + (modifier-bar-button '(hyper))) (defun tool-bar-event-apply-shift-modifier (_ignore-prompt) "Like `event-apply-shift-modifier'. However, take additional modifier tool bar items into account; apply any extra modifiers bound to subsequent `tool-bar' events." - ;; Save the previously used text conversion style. - (let ((old-text-conversion-style text-conversion-style)) - ;; Disable text conversion. - (when (fboundp 'set-text-conversion-style) - (set-text-conversion-style nil)) - (unwind-protect - (progn - ;; Display the on screen keyboard. - (frame-toggle-on-screen-keyboard nil nil) - (let* ((modifiers '(shift)) event1 - (overriding-text-conversion-style nil) - (event (read-event))) - ;; Combine any more modifier key presses. - (while (eq event 'tool-bar) - (setq event1 (event-basic-type (read-event))) - ;; Reject unknown tool bar events. - (unless (memq event1 '(alt super hyper shift control meta)) - (user-error "Unknown tool-bar event %s" event1)) - ;; If `event' is the name of a modifier key, apply that - ;; modifier key as well. - (unless (memq event1 modifiers) - (push event1 modifiers)) - ;; Read another event. - (setq event (read-event))) - ;; EVENT is a keyboard event to which the specified list of - ;; modifier keys should be applied. - (vector (tool-bar-apply-modifiers event modifiers)))) - ;; Re-enable text conversion if necessary. - (unless (or (not (fboundp 'set-text-conversion-style)) - (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style t))))) + (modifier-bar-button '(shift))) (defun tool-bar-event-apply-control-modifier (_ignore-prompt) "Like `event-apply-control-modifier'. However, take additional modifier tool bar items into account; apply any extra modifiers bound to subsequent `tool-bar' events." - ;; Save the previously used text conversion style. - (let ((old-text-conversion-style text-conversion-style)) - ;; Disable text conversion. - (when (fboundp 'set-text-conversion-style) - (set-text-conversion-style nil)) - (unwind-protect - (progn - ;; Display the on screen keyboard. - (frame-toggle-on-screen-keyboard nil nil) - (let* ((modifiers '(control)) event1 - (overriding-text-conversion-style nil) - (event (read-event))) - ;; Combine any more modifier key presses. - (while (eq event 'tool-bar) - (setq event1 (event-basic-type (read-event))) - ;; Reject unknown tool bar events. - (unless (memq event1 '(alt super hyper shift control meta)) - (user-error "Unknown tool-bar event %s" event1)) - ;; If `event' is the name of a modifier key, apply that - ;; modifier key as well. - (unless (memq event1 modifiers) - (push event1 modifiers)) - ;; Read another event. - (setq event (read-event))) - ;; EVENT is a keyboard event to which the specified list of - ;; modifier keys should be applied. - (vector (tool-bar-apply-modifiers event modifiers)))) - ;; Re-enable text conversion if necessary. - (unless (or (not (fboundp 'set-text-conversion-style)) - (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style t))))) + (modifier-bar-button '(control))) (defun tool-bar-event-apply-meta-modifier (_ignore-prompt) "Like `event-apply-meta-modifier'. However, take additional modifier tool bar items into account; apply any extra modifiers bound to subsequent `tool-bar' events." - ;; Save the previously used text conversion style. - (let ((old-text-conversion-style text-conversion-style)) - ;; Disable text conversion. - (when (fboundp 'set-text-conversion-style) - (set-text-conversion-style nil)) - (unwind-protect - (progn - ;; Display the on screen keyboard. - (frame-toggle-on-screen-keyboard nil nil) - (let* ((modifiers '(meta)) event1 - (overriding-text-conversion-style nil) - (event (read-event))) - ;; Combine any more modifier key presses. - (while (eq event 'tool-bar) - (setq event1 (event-basic-type (read-event))) - ;; Reject unknown tool bar events. - (unless (memq event1 '(alt super hyper shift control meta)) - (user-error "Unknown tool-bar event %s" event1)) - ;; If `event' is the name of a modifier key, apply that - ;; modifier key as well. - (unless (memq event1 modifiers) - (push event1 modifiers)) - ;; Read another event. - (setq event (read-event))) - ;; EVENT is a keyboard event to which the specified list of - ;; modifier keys should be applied. - (vector (tool-bar-apply-modifiers event modifiers)))) - ;; Re-enable text conversion if necessary. - (unless (or (not (fboundp 'set-text-conversion-style)) - (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style t))))) + (modifier-bar-button '(meta))) + +(defun modifier-bar-available-p (modifier) + "Return whether the modifier button for MODIFIER should be enabled. +Return t if MODIFIER has not yet been selected as part of +decoding the current key sequence, nil otherwise." + (not (memq modifier modifier-bar-modifier-list))) (define-minor-mode modifier-bar-mode "Toggle display of the modifier bar. @@ -645,27 +535,33 @@ modifier-bar-mode `(keymap (control menu-item "Control Key" event-apply-control-modifier :help "Add Control modifier to the following event" - :image ,(tool-bar--image-expression "ctrl")) + :image ,(tool-bar--image-expression "ctrl") + :enable (modifier-bar-available-p 'control)) (shift menu-item "Shift Key" event-apply-shift-modifier :help "Add Shift modifier to the following event" - :image ,(tool-bar--image-expression "shift")) + :image ,(tool-bar--image-expression "shift") + :enable (modifier-bar-available-p 'shift)) (meta menu-item "Meta Key" event-apply-meta-modifier :help "Add Meta modifier to the following event" - :image ,(tool-bar--image-expression "meta")) + :image ,(tool-bar--image-expression "meta") + :enable (modifier-bar-available-p 'meta)) (alt menu-item "Alt Key" event-apply-alt-modifier :help "Add Alt modifier to the following event" - :image ,(tool-bar--image-expression "alt")) + :image ,(tool-bar--image-expression "alt") + :enable (modifier-bar-available-p 'alt)) (super menu-item "Super Key" event-apply-super-modifier :help "Add Super modifier to the following event" - :image ,(tool-bar--image-expression "super")) + :image ,(tool-bar--image-expression "super") + :enable (modifier-bar-available-p 'super)) (hyper menu-item "Hyper Key" event-apply-hyper-modifier :help "Add Hyper modifier to the following event" - :image ,(tool-bar--image-expression "hyper")))) + :image ,(tool-bar--image-expression "hyper") + :enable (modifier-bar-available-p 'hyper)))) (define-key input-decode-map [tool-bar control] #'tool-bar-event-apply-control-modifier) (define-key input-decode-map [tool-bar shift] @@ -679,7 +575,8 @@ modifier-bar-mode (define-key input-decode-map [tool-bar hyper] #'tool-bar-event-apply-hyper-modifier)) (setq secondary-tool-bar-map nil)) - (force-mode-line-update t)) + ;; Update the mode line now. + (force-mode-line-update t)) (provide 'tool-bar) commit 7af686767a248081d212a252b037250dabf4e56d Merge: 75db4511704 3cea0a84900 Author: Po Lu Date: Fri Jul 7 08:45:07 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 75db4511704284a739c93895d7a31478b1a71181 Author: Po Lu Date: Thu Jul 6 09:35:27 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsNative.java (scaledDensity): Announce new argument `scaledDensity'. * java/org/gnu/emacs/EmacsNoninteractive.java (main): Specify new argument. * java/org/gnu/emacs/EmacsService.java (onCreate): Compute an adjusted DPI for the font size based on the ratio between density and scaledDensity. (run): Specify that adjusted density. * src/android.c (setEmacsParams): Set `android_scaled_pixel_density'. * src/android.h: (android_scaled_pixel_density: New variable. * src/androidterm.c (android_term_init): Set `font_resolution'. * src/androidterm.h (struct android_display_info): New field. * src/font.c (font_pixel_size, font_find_for_lface) (font_open_for_lface, Ffont_face_attributes, Fopen_font): Use FRAME_RES instead of FRAME_RES_Y. * src/frame.h (FRAME_RES): New macro. Use this to convert between font point and pixel sizes as opposed to FRAME_RES_Y. * src/w32font.c (fill_in_logfont): * src/xfaces.c (Fx_family_fonts, set_lface_from_font): Use FRAME_RES instead of FRAME_RES_Y. (bug#64444) diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 9e87c419f95..5b8b0f1eae5 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -60,6 +60,9 @@ public final class EmacsNative pixelDensityX and pixelDensityY are the DPI values that will be used by Emacs. + scaledDensity is the DPI value used to translate point sizes to + pixel sizes when loading fonts. + classPath must be the classpath of this app_process process, or NULL. @@ -70,6 +73,7 @@ public static native void setEmacsParams (AssetManager assetManager, String cacheDir, float pixelDensityX, float pixelDensityY, + float scaledDensity, String classPath, EmacsService emacsService); diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java index aaba74d877c..aa6fa41ba97 100644 --- a/java/org/gnu/emacs/EmacsNoninteractive.java +++ b/java/org/gnu/emacs/EmacsNoninteractive.java @@ -190,7 +190,7 @@ public final class EmacsNoninteractive EmacsNative.setEmacsParams (assets, filesDir, libDir, cacheDir, 0.0f, - 0.0f, null, null); + 0.0f, 0.0f, null, null); /* Now find the dump file that Emacs should use, if it has already been dumped. */ diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index e1fd2dbffda..37048960f25 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -211,6 +211,7 @@ public final class EmacsService extends Service final String filesDir, libDir, cacheDir, classPath; final double pixelDensityX; final double pixelDensityY; + final double scaledDensity; SERVICE = this; handler = new Handler (Looper.getMainLooper ()); @@ -219,6 +220,9 @@ public final class EmacsService extends Service metrics = getResources ().getDisplayMetrics (); pixelDensityX = metrics.xdpi; pixelDensityY = metrics.ydpi; + scaledDensity = ((metrics.scaledDensity + / metrics.density) + * pixelDensityX); resolver = getContentResolver (); try @@ -247,6 +251,7 @@ invocation of app_process (through android-emacs) can EmacsNative.setEmacsParams (manager, filesDir, libDir, cacheDir, (float) pixelDensityX, (float) pixelDensityY, + (float) scaledDensity, classPath, EmacsService.this); } }, extraStartupArgument, diff --git a/src/android.c b/src/android.c index 2a2d134c3c8..a6bc8217820 100644 --- a/src/android.c +++ b/src/android.c @@ -198,6 +198,10 @@ #define ANDROID_MAX_ASSET_FD 65535 /* The display's pixel densities. */ double android_pixel_density_x, android_pixel_density_y; +/* The display pixel density used to convert between point and pixel + font sizes. */ +double android_scaled_pixel_density; + /* The Android application data directory. */ static char *android_files_dir; @@ -2000,6 +2004,7 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, jobject cache_dir, jfloat pixel_density_x, jfloat pixel_density_y, + jfloat scaled_density, jobject class_path, jobject emacs_service_object) { @@ -2021,6 +2026,7 @@ NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object, android_pixel_density_x = pixel_density_x; android_pixel_density_y = pixel_density_y; + android_scaled_pixel_density = scaled_density; __android_log_print (ANDROID_LOG_INFO, __func__, "Initializing "PACKAGE_STRING"...\nPlease report bugs to " diff --git a/src/android.h b/src/android.h index 8634ba01a3d..33ca379e6ba 100644 --- a/src/android.h +++ b/src/android.h @@ -58,6 +58,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. extern const char *android_get_home_directory (void); extern double android_pixel_density_x, android_pixel_density_y; +extern double android_scaled_pixel_density; enum android_handle_type { diff --git a/src/androidterm.c b/src/androidterm.c index aed8e24b9fa..20a8860a913 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -6295,11 +6295,10 @@ android_term_init (void) dpyinfo->color_map = color_map; #ifndef ANDROID_STUBIFY - dpyinfo->resx = android_pixel_density_x; dpyinfo->resy = android_pixel_density_y; - -#endif + dpyinfo->font_resolution = android_scaled_pixel_density; +#endif /* ANDROID_STUBIFY */ /* https://lists.gnu.org/r/emacs-devel/2015-11/msg00194.html */ dpyinfo->smallest_font_height = 1; diff --git a/src/androidterm.h b/src/androidterm.h index e3738fb2192..e75d46b1dfb 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -65,6 +65,11 @@ #define _ANDROID_TERM_H_ /* DPI of the display. */ double resx, resy; + /* DPI used to convert font point sizes into pixel dimensions. + This is resy adjusted by a fixed scaling factor specified by + the user. */ + double font_resolution; + /* Scratch GC for drawing a cursor in a non-default face. */ struct android_gc *scratch_cursor_gc; diff --git a/src/font.c b/src/font.c index 9dcafb3bb33..7f8ddc4dc34 100644 --- a/src/font.c +++ b/src/font.c @@ -348,7 +348,7 @@ font_pixel_size (struct frame *f, Lisp_Object spec) if (FIXNUMP (val)) dpi = XFIXNUM (val); else - dpi = FRAME_RES_Y (f); + dpi = FRAME_RES (f); pixel_size = POINT_TO_PIXEL (point_size, dpi); return pixel_size; } @@ -3023,7 +3023,7 @@ font_find_for_lface (struct frame *f, Lisp_Object *attrs, Lisp_Object spec, int { double pt = XFIXNUM (attrs[LFACE_HEIGHT_INDEX]); - pixel_size = POINT_TO_PIXEL (pt / 10, FRAME_RES_Y (f)); + pixel_size = POINT_TO_PIXEL (pt / 10, FRAME_RES (f)); if (pixel_size < 1) pixel_size = 1; } @@ -3175,13 +3175,13 @@ font_open_for_lface (struct frame *f, Lisp_Object entity, Lisp_Object *attrs, Li } pt /= 10; - size = POINT_TO_PIXEL (pt, FRAME_RES_Y (f)); + size = POINT_TO_PIXEL (pt, FRAME_RES (f)); #ifdef HAVE_NS if (size == 0) { Lisp_Object ffsize = get_frame_param (f, Qfontsize); size = (NUMBERP (ffsize) - ? POINT_TO_PIXEL (XFLOATINT (ffsize), FRAME_RES_Y (f)) + ? POINT_TO_PIXEL (XFLOATINT (ffsize), FRAME_RES (f)) : 0); } #endif @@ -4082,7 +4082,7 @@ DEFUN ("font-face-attributes", Ffont_face_attributes, Sfont_face_attributes, 1, if (FIXNUMP (val)) { Lisp_Object font_dpi = AREF (font, FONT_DPI_INDEX); - int dpi = FIXNUMP (font_dpi) ? XFIXNUM (font_dpi) : FRAME_RES_Y (f); + int dpi = FIXNUMP (font_dpi) ? XFIXNUM (font_dpi) : FRAME_RES (f); plist[n++] = QCheight; plist[n++] = make_fixnum (PIXEL_TO_POINT (XFIXNUM (val) * 10, dpi)); } @@ -4986,7 +4986,7 @@ DEFUN ("open-font", Fopen_font, Sopen_font, 1, 3, 0, { CHECK_NUMBER (size); if (FLOATP (size)) - isize = POINT_TO_PIXEL (XFLOAT_DATA (size), FRAME_RES_Y (f)); + isize = POINT_TO_PIXEL (XFLOAT_DATA (size), FRAME_RES (f)); else if (! integer_to_intmax (size, &isize)) args_out_of_range (font_entity, size); if (! (INT_MIN <= isize && isize <= INT_MAX)) diff --git a/src/frame.h b/src/frame.h index 8ed9c0f37d8..62fc63c7c1f 100644 --- a/src/frame.h +++ b/src/frame.h @@ -981,12 +981,26 @@ #define FRAME_RES_X(f) \ #define FRAME_RES_Y(f) \ (eassert (FRAME_WINDOW_P (f)), FRAME_DISPLAY_INFO (f)->resy) +#ifdef HAVE_ANDROID + +/* Android systems use a font scaling factor independent from the + display DPI. */ + +#define FRAME_RES(f) \ + (eassert (FRAME_WINDOW_P (f)), \ + FRAME_DISPLAY_INFO (f)->font_resolution) + +#else /* !HAVE_ANDROID */ +#define FRAME_RES(f) (FRAME_RES_Y (f)) +#endif /* HAVE_ANDROID */ + #else /* !HAVE_WINDOW_SYSTEM */ /* Defaults when no window system available. */ -#define FRAME_RES_X(f) default_pixels_per_inch_x () -#define FRAME_RES_Y(f) default_pixels_per_inch_y () +#define FRAME_RES_X(f) default_pixels_per_inch_x () +#define FRAME_RES_Y(f) default_pixels_per_inch_y () +#define FRAME_RES(f) default_pixels_per_inch_y () #endif /* HAVE_WINDOW_SYSTEM */ diff --git a/src/w32font.c b/src/w32font.c index 2917fa55f9f..0371b24e1d1 100644 --- a/src/w32font.c +++ b/src/w32font.c @@ -2031,7 +2031,7 @@ w32_to_fc_weight (int n) fill_in_logfont (struct frame *f, LOGFONT *logfont, Lisp_Object font_spec) { Lisp_Object tmp, extra; - int dpi = FRAME_RES_Y (f); + int dpi = FRAME_RES (f); tmp = AREF (font_spec, FONT_DPI_INDEX); if (FIXNUMP (tmp)) diff --git a/src/xfaces.c b/src/xfaces.c index af3428ad995..30487c0e8fb 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -1612,7 +1612,7 @@ DEFUN ("x-family-fonts", Fx_family_fonts, Sx_family_fonts, 0, 2, 0, { Lisp_Object font = AREF (vec, i); int point = PIXEL_TO_POINT (XFIXNUM (AREF (font, FONT_SIZE_INDEX)) * 10, - FRAME_RES_Y (f)); + FRAME_RES (f)); Lisp_Object spacing = Ffont_get (font, QCspacing); Lisp_Object v = CALLN (Fvector, AREF (font, FONT_FAMILY_INDEX), @@ -2173,7 +2173,7 @@ set_lface_from_font (struct frame *f, Lisp_Object lface, if (force_p || UNSPECIFIEDP (LFACE_HEIGHT (lface))) { - int pt = PIXEL_TO_POINT (font->pixel_size * 10, FRAME_RES_Y (f)); + int pt = PIXEL_TO_POINT (font->pixel_size * 10, FRAME_RES (f)); eassert (pt > 0); ASET (lface, LFACE_HEIGHT_INDEX, make_fixnum (pt)); commit af8232a150c934d500bfc231a0b9ac53d6d8415e Merge: 08f90dc0058 47cbecb4748 Author: Po Lu Date: Thu Jul 6 08:34:26 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 08f90dc0058df25d7b405a8f9fca3dad638db44a Author: Po Lu Date: Wed Jul 5 20:16:14 2023 +0800 Fix bug#64445 * doc/emacs/android.texi (Android Environment): Document that Emacs also receives READ_EXTERNAL_STORAGE by default on old versions of Android. * java/AndroidManifest.xml.in: Request READ_EXTERNAL_STORAGE. (bug#64445) diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 85719da518a..3e27c019257 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -382,6 +382,8 @@ Android Environment @item @code{android.permission.SET_WALLPAPER} @item +@code{android.permission.READ_EXTERNAL_STORAGE} +@item @code{android.permission.WRITE_EXTERNAL_STORAGE} @item @code{android.permission.SEND_SMS} diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in index 082c4c9373e..e79fb4e46e7 100644 --- a/java/AndroidManifest.xml.in +++ b/java/AndroidManifest.xml.in @@ -38,6 +38,11 @@ along with GNU Emacs. If not, see . --> + + commit d09fc890e0abc2589a5a184886cb5bf0ad164967 Author: Po Lu Date: Wed Jul 5 16:36:21 2023 +0800 ; * doc/emacs/emacs.texi (Emacs and Android): Fix menu. diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi index 467a9ff0d82..7ce1a53f934 100644 --- a/doc/emacs/emacs.texi +++ b/doc/emacs/emacs.texi @@ -1266,8 +1266,8 @@ Top * What is Android?:: Preamble. * Android Startup:: Starting up Emacs on Android. -* Android Environment:: Running Emacs under Android. * Android File System:: The Android file system. +* Android Environment:: Running Emacs under Android. * Android Windowing:: The Android window system. * Android Fonts:: Font selection under Android. * Android Troubleshooting:: Dealing with problems. commit d373ab1fee5dd68efa945d26e0482dd4f54b01db Author: Po Lu Date: Wed Jul 5 16:34:51 2023 +0800 ; Update Android port * doc/emacs/android.texi (Android): Fix discrepancies between menu and sectioning. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 84da630576c..85719da518a 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -16,8 +16,8 @@ Android @menu * What is Android?:: Preamble. * Android Startup:: Starting up Emacs on Android. -* Android Environment:: Running Emacs under Android. * Android File System:: The Android file system. +* Android Environment:: Running Emacs under Android. * Android Windowing:: The Android window system. * Android Fonts:: Font selection under Android. * Android Troubleshooting:: Dealing with problems. commit 257e49ff0d333cd5b9967613cb14051d54dd054b Author: Po Lu Date: Wed Jul 5 14:57:15 2023 +0800 Fix crash between Android 4.0 and Android 5.1 * java/org/gnu/emacs/EmacsService.java (detectMouse): Don't use function that is not present on Android 4.0. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 820befb52d2..e1fd2dbffda 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -491,6 +491,8 @@ invocation of app_process (through android-emacs) can int i; if (Build.VERSION.SDK_INT + /* Android 4.0 and earlier don't support mouse input events at + all. */ < Build.VERSION_CODES.JELLY_BEAN) return false; @@ -504,8 +506,20 @@ invocation of app_process (through android-emacs) can if (device == null) continue; - if (device.supportsSource (InputDevice.SOURCE_MOUSE)) - return true; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + { + if (device.supportsSource (InputDevice.SOURCE_MOUSE)) + return true; + } + else + { + /* `supportsSource' is only present on API level 21 and + later, but earlier versions provide a bit mask + containing each supported source. */ + + if ((device.getSources () & InputDevice.SOURCE_MOUSE) != 0) + return true; + } } return false; commit 9e9142249725b4785a01734a8c0f50301fccdea7 Author: Po Lu Date: Wed Jul 5 11:39:45 2023 +0800 Update Android port * doc/lispref/commands.texi (Misc Events): Correctly index `set-text-conversion-style'. * lisp/tool-bar.el (tool-bar-event-apply-alt-modifier) (tool-bar-event-apply-super-modifier) (tool-bar-event-apply-hyper-modifier) (tool-bar-event-apply-shift-modifier) (tool-bar-event-apply-control-modifier) (tool-bar-event-apply-meta-modifier): Pass t when restoring text conversion style. * src/keyboard.c (restore_reading_key_sequence): New function. (read_key_sequence): Set `reading_key_sequence' where necessary. * src/keyboard.h: Declare variable. * src/textconv.c (check_postponed_buffers): New function. (Fset_text_conversion_style): New argument. If set, and a key sequence is being read, postpone resetting the IME until the key sequence is read. (syms_of_textconv): Clear new variable and add staticpro. * src/textconv.h: Update prototypes. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index eaf6f3e451b..7a076406bed 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2251,7 +2251,7 @@ Misc Events and make edits followed by @code{text-conversion} events. @end table -@findex disable-text-conversion +@findex set-text-conversion-style Changes to the value of this variable will only take effect upon the next redisplay after the buffer becomes the selected buffer of a frame. If you need to disable text conversion in a way that takes diff --git a/lisp/tool-bar.el b/lisp/tool-bar.el index aeab21f61a0..1ede5c45971 100644 --- a/lisp/tool-bar.el +++ b/lisp/tool-bar.el @@ -444,7 +444,7 @@ tool-bar-event-apply-alt-modifier ;; Re-enable text conversion if necessary. (unless (or (not (fboundp 'set-text-conversion-style)) (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style))))) + (set-text-conversion-style old-text-conversion-style t))))) (defun tool-bar-event-apply-super-modifier (_ignore-prompt) "Like `event-apply-super-modifier'. @@ -480,7 +480,7 @@ tool-bar-event-apply-super-modifier ;; Re-enable text conversion if necessary. (unless (or (not (fboundp 'set-text-conversion-style)) (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style))))) + (set-text-conversion-style old-text-conversion-style t))))) (defun tool-bar-event-apply-hyper-modifier (_ignore-prompt) "Like `event-apply-hyper-modifier'. @@ -516,7 +516,7 @@ tool-bar-event-apply-hyper-modifier ;; Re-enable text conversion if necessary. (unless (or (not (fboundp 'set-text-conversion-style)) (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style))))) + (set-text-conversion-style old-text-conversion-style t))))) (defun tool-bar-event-apply-shift-modifier (_ignore-prompt) "Like `event-apply-shift-modifier'. @@ -552,7 +552,7 @@ tool-bar-event-apply-shift-modifier ;; Re-enable text conversion if necessary. (unless (or (not (fboundp 'set-text-conversion-style)) (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style))))) + (set-text-conversion-style old-text-conversion-style t))))) (defun tool-bar-event-apply-control-modifier (_ignore-prompt) "Like `event-apply-control-modifier'. @@ -588,7 +588,7 @@ tool-bar-event-apply-control-modifier ;; Re-enable text conversion if necessary. (unless (or (not (fboundp 'set-text-conversion-style)) (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style))))) + (set-text-conversion-style old-text-conversion-style t))))) (defun tool-bar-event-apply-meta-modifier (_ignore-prompt) "Like `event-apply-meta-modifier'. @@ -624,7 +624,7 @@ tool-bar-event-apply-meta-modifier ;; Re-enable text conversion if necessary. (unless (or (not (fboundp 'set-text-conversion-style)) (eq old-text-conversion-style text-conversion-style)) - (set-text-conversion-style old-text-conversion-style))))) + (set-text-conversion-style old-text-conversion-style t))))) (define-minor-mode modifier-bar-mode "Toggle display of the modifier bar. diff --git a/src/keyboard.c b/src/keyboard.c index b33fbf8f155..ea07c538aa2 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -115,6 +115,13 @@ Copyright (C) 1985-1989, 1993-1997, 1999-2023 Free Software Foundation, /* True in the single-kboard state, false in the any-kboard state. */ static bool single_kboard; +#ifdef HAVE_TEXT_CONVERSION + +/* True if a key sequence is currently being read. */ +bool reading_key_sequence; + +#endif /* HAVE_TEXT_CONVERSION */ + /* Minimum allowed size of the recent_keys vector. */ #define MIN_NUM_RECENT_KEYS (100) @@ -10102,6 +10109,24 @@ test_undefined (Lisp_Object binding) raw_keybuf_count = 0; } + + +#ifdef HAVE_TEXT_CONVERSION + +static void +restore_reading_key_sequence (int old_reading_key_sequence) +{ + reading_key_sequence = old_reading_key_sequence; + + /* If a key sequence is no longer being read, reset input methods + whose state changes were postponed. */ + + if (!old_reading_key_sequence) + check_postponed_buffers (); +} + +#endif /* HAVE_TEXT_CONVERSION */ + /* Read a sequence of keys that ends with a non prefix character, storing it in KEYBUF, a buffer of size READ_KEY_ELTS. Prompt with PROMPT. @@ -10261,6 +10286,16 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt, keys_start = this_command_key_count; this_single_command_key_start = keys_start; +#ifdef HAVE_TEXT_CONVERSION + /* Set `reading_key_sequence' to true. This variable is used by + Fset_text_conversion_style to determine if it should postpone + resetting the input method until this function completes. */ + + record_unwind_protect_int (restore_reading_key_sequence, + reading_key_sequence); + reading_key_sequence = true; +#endif /* HAVE_TEXT_CONVERSION */ + /* We jump here when we need to reinitialize fkey and keytran; this happens if we switch keyboards between rescans. */ replay_entire_sequence: diff --git a/src/keyboard.h b/src/keyboard.h index 26eecd48b00..9f6e65f9a09 100644 --- a/src/keyboard.h +++ b/src/keyboard.h @@ -246,6 +246,14 @@ kset_window_system (struct kboard *kb, Lisp_Object val) kboard, but doing so requires throwing to wrong_kboard_jmpbuf. */ extern KBOARD *current_kboard; + +#ifdef HAVE_TEXT_CONVERSION + +/* True if a key sequence is currently being read. */ +extern bool reading_key_sequence; + +#endif /* HAVE_TEXT_CONVERSION */ + /* Total number of times read_char has returned, modulo UINTMAX_MAX + 1. */ extern uintmax_t num_input_events; diff --git a/src/textconv.c b/src/textconv.c index 6718568ac98..60f3ba80577 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -2020,10 +2020,67 @@ register_textconv_interface (struct textconv_interface *interface) +/* List of buffers whose text conversion state will be reset after a + key sequence is read. */ +static Lisp_Object postponed_buffers; + +/* Reset the text conversion style of each frame whose selected buffer + is contained inside `postponed_buffers'. Set `postponed_buffers' + to nil. */ + +void +check_postponed_buffers (void) +{ + Lisp_Object buffer, tail, frame; + struct buffer *b; + struct frame *f; + + buffer = postponed_buffers; + postponed_buffers = Qnil; + + if (!text_interface->reset) + return; + + FOR_EACH_TAIL (buffer) + { + b = XBUFFER (XCAR (buffer)); + + /* Continue if this is a dead buffer. */ + + if (!BUFFER_LIVE_P (b)) + continue; + + /* If no windows are displaying B anymore, continue. */ + + if (!buffer_window_count (b)) + continue; + + /* Look for frames which have B selected. */ + + FOR_EACH_FRAME (tail, frame) + { + f = XFRAME (frame); + + if (WINDOW_LIVE_P (f->old_selected_window) + && FRAME_WINDOW_P (f) + /* N.B. that the same frame can't be reset twice as long + as the list of buffers remains unique. */ + && EQ (XWINDOW (f->old_selected_window)->contents, + XCAR (buffer))) + { + block_input (); + reset_frame_state (f); + text_interface->reset (f); + unblock_input (); + } + } + } +} + /* Lisp interface. */ DEFUN ("set-text-conversion-style", Fset_text_conversion_style, - Sset_text_conversion_style, 1, 1, 0, + Sset_text_conversion_style, 1, 2, 0, doc: /* Set the text conversion style in the current buffer. Set `text-conversion-style' to VALUE, then force any input method @@ -2032,8 +2089,17 @@ DEFUN ("set-text-conversion-style", Fset_text_conversion_style, This can lead to a significant amount of time being taken by the input method resetting itself, so you should not use this function lightly; instead, set `text-conversion-style' before your buffer is displayed, -and let redisplay manage the input method appropriately. */) - (Lisp_Object value) +and let redisplay manage the input method appropriately. + +If a key sequence is currently being read (either through the command +loop or by a call to `read-key-sequence') and AFTER-KEY-SEQUENCE is +non-nil, don't perform changes to the input method until the key +sequence is read. This is useful within a function bound to +`input-decode-map' or `local-function-key-map', as it prevents the +input method from being redundantly enabled according to VALUE if the +replacement key sequence returned starts a new key sequence and makes +`read-key-sequence' disable text conversion again. */) + (Lisp_Object value, Lisp_Object after_key_sequence) { Lisp_Object tail, frame; struct frame *f; @@ -2051,6 +2117,20 @@ DEFUN ("set-text-conversion-style", Fset_text_conversion_style, { buffer = Fcurrent_buffer (); + /* Postpone changes to the actual text conversion state if + AFTER_KEY_SEQUENCE is non-nil and a key sequence is being + read. */ + + if (reading_key_sequence && !NILP (after_key_sequence)) + { + if (NILP (Fmemq (buffer, postponed_buffers))) + /* `check_postponed_buffers' will hopefully be called soon + enough to avoid postponed_buffers growing + indefinitely. */ + postponed_buffers = Fcons (buffer, postponed_buffers); + return Qnil; + } + FOR_EACH_FRAME (tail, frame) { f = XFRAME (frame); @@ -2126,4 +2206,7 @@ syms_of_textconv (void) Vtext_conversion_face = Qunderline; defsubr (&Sset_text_conversion_style); + + postponed_buffers = Qnil; + staticpro (&postponed_buffers); } diff --git a/src/textconv.h b/src/textconv.h index 7550388a723..feac5b805af 100644 --- a/src/textconv.h +++ b/src/textconv.h @@ -118,6 +118,8 @@ Copyright (C) 2023 Free Software Foundation, Inc. struct textconv_conversion_text text; }; + + #define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0) extern int textconv_query (struct frame *, struct textconv_callback_struct *, @@ -148,6 +150,7 @@ #define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0) ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *); extern bool conversion_disabled_p (void); +extern void check_postponed_buffers (void); extern void register_textconv_interface (struct textconv_interface *); commit 2443f4ec77ffdb2a5b59e243b50ee04cd4f2eb1f Merge: 8ed8f08ed2f cb674ab40ce Author: Po Lu Date: Wed Jul 5 08:45:06 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 8ed8f08ed2ff297754d6db83983d3f8f2822c599 Author: Po Lu Date: Tue Jul 4 16:40:20 2023 +0800 Implement a tool bar containing modifier keys * doc/emacs/frames.texi (Tool Bars): Describe modifier bars. * doc/lispref/keymaps.texi (Extended Menu Items, Tool Bar): Document changes to tool bar menu item handling and secondary tool bars. * etc/NEWS: Announce changes. * lisp/simple.el (event-apply-modifier): Correctly apply Ctrl and Shift modifiers to lower case ASCII key events that already have other modifiers applied. * lisp/tool-bar.el (tool-bar--cache-key) (tool-bar--secondary-cache-key): New defsubsts. (tool-bar--flush-cache): Flush secondary tool bar cache. (tool-bar-make-keymap): Include secondary tool bar if necessary. (tool-bar-make-keymap-1): New arg MAP. Generate a keymap for that map if specified, else default to tool-bar-map. (set-text-conversion-style, tool-bar-apply-modifiers) (overriding-text-conversion-style) (tool-bar-event-apply-alt-modifier) (tool-bar-event-apply-super-modifier) (tool-bar-event-apply-hyper-modifier) (tool-bar-event-apply-shift-modifier) (tool-bar-event-apply-control-modifier) (tool-bar-event-apply-meta-modifier, modifier-bar-mode): New functions. * src/dispextern.h (enum tool_bar_item_idx): Add TOOL_BAR_ITEM_WRAP. * src/frame.c (make_frame): Clear new field `tool_bar_wraps_p'. * src/frame.h (struct frame): New field `tool_bar_wraps_p'. * src/keyboard.c (parse_tool_bar_item): Handle QCwrap properties in tool bar menu items. (syms_of_keyboard): New defsym QCwrap. * src/xdisp.c (build_desired_tool_bar_string): Clear f->tool_bar_wraps_p and set it appropriately. Insert new line characters in the tool bar string upon encountering a wrap character. (display_tool_bar_line): Stop at EOB, not line end. Reseat on the next line upon encountering EOL characters. (redisplay_tool_bar): Allow rows to be different heights if explicit new lines are present upon the tool bar string. diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi index 4e09c1c3f67..a968c2a97c5 100644 --- a/doc/emacs/frames.texi +++ b/doc/emacs/frames.texi @@ -1333,6 +1333,21 @@ Tool Bars tool bar is hidden when the frame is put into fullscreen, but can be displayed by moving the mouse pointer to the top of the screen. +@vindex modifier-bar-mode +@findex modifier-bar-mode +@cindex displaying modifier keys in the tool bar +@cindex mode, Modifier Bar +@cindex Modifier Bar + Keyboards often lack one or more of the modifier keys that Emacs +might want to use, making it difficult or impossible to input key +sequences that contain them. Emacs can optionally display a list of +buttons that act as substitutes for modifier keys within the tool bar; +these buttons are also referred to as the ``modifier bar''. Clicking +an icon within the modifier bar will cause a modifier key to be +applied to the next keyboard event that is read. The modifier bar is +displayed when the global minor mode @code{modifier-bar-mode} is +enabled; to do so, type @kbd{M-x modifier-bar-mode}. + @node Tab Bars @section Tab Bars @cindex tab bar mode diff --git a/doc/lispref/keymaps.texi b/doc/lispref/keymaps.texi index 6d07ad5be2c..a33806ad1cf 100644 --- a/doc/lispref/keymaps.texi +++ b/doc/lispref/keymaps.texi @@ -2578,6 +2578,12 @@ Extended Menu Items Emacs can call this function at any time that it does redisplay or operates on menu data structures, so you should write it so it can safely be called at any time. + +@item :wrap @var{wrap-p} +If @var{wrap-p} is non-nil inside a tool bar, the menu item is not +displayed, but instead causes subsequent items to be displayed on a +new line. This is not supported when Emacs uses the GTK+ or Nextstep +toolkits. @end table @node Menu Separators @@ -3084,6 +3090,16 @@ Tool Bar @code{tool-bar-add-item-from-menu}. @end defun +@vindex secondary-tool-bar-map +In addition to the tool bar items defined in @code{tool-bar-map}, +Emacs also supports displaying an additional row of ``secondary'' tool +bar items specified in the keymap @code{secondary-tool-bar-map}. +These items are normally displayed below those defined within +@code{tool-bar-map} if the tool bar is positioned at the top of its +frame, but are displayed above them if the tool bar is positioned at +the bottom (@pxref{Layout Parameters}.) They are not displayed if the +tool bar is positioned at the left or right of a frame. + @defvar auto-resize-tool-bars If this variable is non-@code{nil}, the tool bar automatically resizes to show all defined tool bar items---but not larger than a quarter of the diff --git a/etc/NEWS b/etc/NEWS index 2237e08b68d..30523a08903 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -104,6 +104,11 @@ plus, minus, check-mark, start, etc. The 'tool-bar-position' frame parameter can be set to 'bottom' on all window systems other than Nextstep. ++++ +** New global minor mode 'modifier-bar-mode'. +When this minor mode is enabled, buttons representing modifier keys +are displayed along the tool bar. + * Editing Changes in Emacs 30.1 @@ -566,6 +571,11 @@ directory-local variables as safe. ** New variable 'inhibit-auto-fill' to temporarily prevent auto-fill. ++++ +** New variable 'secondary-tool-bar-map'. +If non-nil, this variable contains a keymap of menu items that are +displayed along tool bar items inside 'tool-bar-map'. + ** Functions and variables to transpose sexps +++ diff --git a/etc/images/alt.pbm b/etc/images/alt.pbm new file mode 100644 index 00000000000..7d12a48b552 Binary files /dev/null and b/etc/images/alt.pbm differ diff --git a/etc/images/ctrl.pbm b/etc/images/ctrl.pbm new file mode 100644 index 00000000000..c3ff817dc7a Binary files /dev/null and b/etc/images/ctrl.pbm differ diff --git a/etc/images/hyper.pbm b/etc/images/hyper.pbm new file mode 100644 index 00000000000..fdb79c2f3a9 Binary files /dev/null and b/etc/images/hyper.pbm differ diff --git a/etc/images/meta.pbm b/etc/images/meta.pbm new file mode 100644 index 00000000000..4d4c55c85c7 Binary files /dev/null and b/etc/images/meta.pbm differ diff --git a/etc/images/shift.pbm b/etc/images/shift.pbm new file mode 100644 index 00000000000..53128f56d96 Binary files /dev/null and b/etc/images/shift.pbm differ diff --git a/etc/images/super.pbm b/etc/images/super.pbm new file mode 100644 index 00000000000..aa126755f99 Binary files /dev/null and b/etc/images/super.pbm differ diff --git a/lisp/simple.el b/lisp/simple.el index 4ddede53a14..d78407e05bb 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10271,18 +10271,34 @@ event-apply-modifier LSHIFTBY is the numeric value of this modifier, in keyboard events. PREFIX is the string that represents this modifier in an event type symbol." (if (numberp event) - (cond ((eq symbol 'control) - (if (<= 64 (upcase event) 95) - (- (upcase event) 64) - (logior (ash 1 lshiftby) event))) - ((eq symbol 'shift) - ;; FIXME: Should we also apply this "upcase" behavior of shift - ;; to non-ascii letters? - (if (<= ?a (downcase event) ?z) - (upcase event) - (logior (ash 1 lshiftby) event))) - (t - (logior (ash 1 lshiftby) event))) + ;; Use the base event to determine how the control and shift + ;; modifiers should be applied. + (let* ((base-event (event-basic-type event))) + (cond ((eq symbol 'control) + (if (<= 64 (upcase base-event) 95) + ;; Apply the control modifier... + (logior (- (upcase base-event) 64) + ;; ... and any additional modifiers + ;; specified in the original event... + (logand event (logior ?\M-\0 ?\C-\0 ?\S-\0 + ?\H-\0 ?\s-\0 ?\A-\0)) + ;; ... including any shift modifier that + ;; `event-basic-type' may have removed. + (if (<= ?A event ?Z) ?\S-\0 0)) + (logior (ash 1 lshiftby) event))) + ((eq symbol 'shift) + ;; FIXME: Should we also apply this "upcase" behavior of shift + ;; to non-ascii letters? + (if (<= ?a base-event ?z) + ;; Apply the Shift modifier. + (logior (upcase base-event) + ;; ... and any additional modifiers + ;; specified in the original event. + (logand event (logior ?\M-\0 ?\C-\0 ?\S-\0 + ?\H-\0 ?\s-\0 ?\A-\0))) + (logior (ash 1 lshiftby) event))) + (t + (logior (ash 1 lshiftby) event)))) (if (memq symbol (event-modifiers event)) event (let ((event-type (if (symbolp event) event (car event)))) diff --git a/lisp/tool-bar.el b/lisp/tool-bar.el index 1a0faf3a584..aeab21f61a0 100644 --- a/lisp/tool-bar.el +++ b/lisp/tool-bar.el @@ -83,6 +83,14 @@ tool-bar-map `tool-bar-add-item', `tool-bar-add-item-from-menu' and related functions.") +(defvar secondary-tool-bar-map nil + "Optional secondary keymap for the tool bar. + +If non-nil, tool bar items defined within this map are displayed +in a line below the tool bar if the `tool-bar-position' frame +parameter is set to `top', and above the tool bar it is set to +`bottom'.") + (global-set-key [tool-bar] `(menu-item ,(purecopy "tool bar") ignore :filter tool-bar-make-keymap)) @@ -91,15 +99,21 @@ tool-bar-map (defconst tool-bar-keymap-cache (make-hash-table :test #'equal)) -(defun tool-bar--cache-key () +(defsubst tool-bar--cache-key () (cons (frame-terminal) (sxhash-eq tool-bar-map))) +(defsubst tool-bar--secondary-cache-key () + (cons (frame-terminal) (sxhash-eq secondary-tool-bar-map))) + (defun tool-bar--flush-cache () "Remove all cached entries that refer to the current `tool-bar-map'." (let ((id (sxhash-eq tool-bar-map)) + (secondary-id (and secondary-tool-bar-map + (sxhash-eq secondary-tool-bar-map))) (entries nil)) (maphash (lambda (k _) - (when (equal (cdr k) id) + (when (or (equal (cdr k) id) + (equal (cdr k) secondary-id)) (push k entries))) tool-bar-keymap-cache) (dolist (k entries) @@ -107,14 +121,54 @@ tool-bar--flush-cache (defun tool-bar-make-keymap (&optional _ignore) "Generate an actual keymap from `tool-bar-map'. +If `secondary-tool-bar-map' is non-nil, take it into account as well. Its main job is to figure out which images to use based on the display's color capability and based on the available image libraries." - (or (gethash (tool-bar--cache-key) tool-bar-keymap-cache) - (setf (gethash (tool-bar--cache-key) tool-bar-keymap-cache) - (tool-bar-make-keymap-1)))) - -(defun tool-bar-make-keymap-1 () - "Generate an actual keymap from `tool-bar-map', without caching." + (let* ((key (tool-bar--cache-key)) + (base-keymap + (or (gethash key tool-bar-keymap-cache) + (setf (gethash key tool-bar-keymap-cache) + (tool-bar-make-keymap-1)))) + (secondary-keymap + (and secondary-tool-bar-map + (or (gethash (tool-bar--secondary-cache-key) + tool-bar-keymap-cache) + (setf (gethash (tool-bar--secondary-cache-key) + tool-bar-keymap-cache) + (tool-bar-make-keymap-1 + secondary-tool-bar-map)))))) + (if secondary-keymap + (or (ignore-errors + (progn + ;; Determine the value of the `tool-bar-position' frame + ;; parameter. + (let ((position (frame-parameter nil 'tool-bar-position))) + (cond ((eq position 'top) + ;; Place `base-keymap' above `secondary-keymap'. + (append base-keymap (list (list (gensym) + 'menu-item + "" 'ignore + :wrap t)) + (cdr secondary-keymap))) + ((eq position 'bottom) + ;; Place `secondary-keymap' above `base-keymap'. + (append secondary-keymap (list (list (gensym) + 'menu-item + "" 'ignore + :wrap t)) + (cdr base-keymap))) + ;; If the tool bar position isn't known, don't + ;; display the secondary keymap at all. + (t base-keymap))))) + ;; If combining both keymaps fails, return the base + ;; keymap. + base-keymap) + base-keymap))) + +(defun tool-bar-make-keymap-1 (&optional map) + "Generate an actual keymap from `tool-bar-map', without caching. +MAP is either a keymap to use as a source for menu items, or nil, +in which case the value of `tool-bar-map' is used instead." (mapcar (lambda (bind) (let (image-exp plist) (when (and (eq (car-safe (cdr-safe bind)) 'menu-item) @@ -136,7 +190,7 @@ tool-bar-make-keymap-1 bind)) (plist-put plist :image image)))) bind)) - tool-bar-map)) + (or map tool-bar-map))) ;;;###autoload (defun tool-bar-add-item (icon def key &rest props) @@ -322,6 +376,310 @@ tool-bar-setup (modify-all-frames-parameters (list (cons 'tool-bar-position val)))))) + + +;; Modifier mode. +;; This displays a small tool bar containing modifier keys +;; above or below the main tool bar itself. + +(declare-function set-text-conversion-style "textconv.c") + +;; These functions are very similar to their counterparts in +;; simple.el, but allow combining multiple modifier buttons together. + +(defun tool-bar-apply-modifiers (event modifiers) + "Apply the specified list of MODIFIERS to EVENT. +MODIFIERS must be a list containing only the symbols `alt', +`super', `hyper', `shift', `control' and `meta'. +Return EVENT with the specified modifiers applied." + (dolist (modifier modifiers) + (cond + ((eq modifier 'alt) + (setq event (event-apply-modifier event 'alt 22 "A-"))) + ((eq modifier 'super) + (setq event (event-apply-modifier event 'super 23 "s-"))) + ((eq modifier 'hyper) + (setq event (event-apply-modifier event 'hyper 24 "H-"))) + ((eq modifier 'shift) + (setq event (event-apply-modifier event 'shift 25 "S-"))) + ((eq modifier 'control) + (setq event (event-apply-modifier event 'control 26 "C-"))) + ((eq modifier 'meta) + (setq event (event-apply-modifier event 'meta 27 "M-"))))) + event) + +(defvar overriding-text-conversion-style) + +(defun tool-bar-event-apply-alt-modifier (_ignore-prompt) + "Like `event-apply-alt-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + ;; Save the previously used text conversion style. + (let ((old-text-conversion-style text-conversion-style)) + ;; Disable text conversion. + (when (fboundp 'set-text-conversion-style) + (set-text-conversion-style nil)) + (unwind-protect + (progn + ;; Display the on screen keyboard. + (frame-toggle-on-screen-keyboard nil nil) + (let* ((modifiers '(alt)) event1 + (overriding-text-conversion-style nil) + (event (read-event))) + ;; Combine any more modifier key presses. + (while (eq event 'tool-bar) + (setq event1 (event-basic-type (read-event))) + ;; Reject unknown tool bar events. + (unless (memq event1 '(alt super hyper shift control meta)) + (user-error "Unknown tool-bar event %s" event1)) + ;; If `event' is the name of a modifier key, apply that + ;; modifier key as well. + (unless (memq event1 modifiers) + (push event1 modifiers)) + ;; Read another event. + (setq event (read-event))) + ;; EVENT is a keyboard event to which the specified list of + ;; modifier keys should be applied. + (vector (tool-bar-apply-modifiers event modifiers)))) + ;; Re-enable text conversion if necessary. + (unless (or (not (fboundp 'set-text-conversion-style)) + (eq old-text-conversion-style text-conversion-style)) + (set-text-conversion-style old-text-conversion-style))))) + +(defun tool-bar-event-apply-super-modifier (_ignore-prompt) + "Like `event-apply-super-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + ;; Save the previously used text conversion style. + (let ((old-text-conversion-style text-conversion-style)) + ;; Disable text conversion. + (when (fboundp 'set-text-conversion-style) + (set-text-conversion-style nil)) + (unwind-protect + (progn + ;; Display the on screen keyboard. + (frame-toggle-on-screen-keyboard nil nil) + (let* ((modifiers '(super)) event1 + (overriding-text-conversion-style nil) + (event (read-event))) + ;; Combine any more modifier key presses. + (while (eq event 'tool-bar) + (setq event1 (event-basic-type (read-event))) + ;; Reject unknown tool bar events. + (unless (memq event1 '(alt super hyper shift control meta)) + (user-error "Unknown tool-bar event %s" event1)) + ;; If `event' is the name of a modifier key, apply that + ;; modifier key as well. + (unless (memq event1 modifiers) + (push event1 modifiers)) + ;; Read another event. + (setq event (read-event))) + ;; EVENT is a keyboard event to which the specified list of + ;; modifier keys should be applied. + (vector (tool-bar-apply-modifiers event modifiers)))) + ;; Re-enable text conversion if necessary. + (unless (or (not (fboundp 'set-text-conversion-style)) + (eq old-text-conversion-style text-conversion-style)) + (set-text-conversion-style old-text-conversion-style))))) + +(defun tool-bar-event-apply-hyper-modifier (_ignore-prompt) + "Like `event-apply-hyper-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + ;; Save the previously used text conversion style. + (let ((old-text-conversion-style text-conversion-style)) + ;; Disable text conversion. + (when (fboundp 'set-text-conversion-style) + (set-text-conversion-style nil)) + (unwind-protect + (progn + ;; Display the on screen keyboard. + (frame-toggle-on-screen-keyboard nil nil) + (let* ((modifiers '(hyper)) event1 + (overriding-text-conversion-style nil) + (event (read-event))) + ;; Combine any more modifier key presses. + (while (eq event 'tool-bar) + (setq event1 (event-basic-type (read-event))) + ;; Reject unknown tool bar events. + (unless (memq event1 '(alt super hyper shift control meta)) + (user-error "Unknown tool-bar event %s" event1)) + ;; If `event' is the name of a modifier key, apply that + ;; modifier key as well. + (unless (memq event1 modifiers) + (push event1 modifiers)) + ;; Read another event. + (setq event (read-event))) + ;; EVENT is a keyboard event to which the specified list of + ;; modifier keys should be applied. + (vector (tool-bar-apply-modifiers event modifiers)))) + ;; Re-enable text conversion if necessary. + (unless (or (not (fboundp 'set-text-conversion-style)) + (eq old-text-conversion-style text-conversion-style)) + (set-text-conversion-style old-text-conversion-style))))) + +(defun tool-bar-event-apply-shift-modifier (_ignore-prompt) + "Like `event-apply-shift-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + ;; Save the previously used text conversion style. + (let ((old-text-conversion-style text-conversion-style)) + ;; Disable text conversion. + (when (fboundp 'set-text-conversion-style) + (set-text-conversion-style nil)) + (unwind-protect + (progn + ;; Display the on screen keyboard. + (frame-toggle-on-screen-keyboard nil nil) + (let* ((modifiers '(shift)) event1 + (overriding-text-conversion-style nil) + (event (read-event))) + ;; Combine any more modifier key presses. + (while (eq event 'tool-bar) + (setq event1 (event-basic-type (read-event))) + ;; Reject unknown tool bar events. + (unless (memq event1 '(alt super hyper shift control meta)) + (user-error "Unknown tool-bar event %s" event1)) + ;; If `event' is the name of a modifier key, apply that + ;; modifier key as well. + (unless (memq event1 modifiers) + (push event1 modifiers)) + ;; Read another event. + (setq event (read-event))) + ;; EVENT is a keyboard event to which the specified list of + ;; modifier keys should be applied. + (vector (tool-bar-apply-modifiers event modifiers)))) + ;; Re-enable text conversion if necessary. + (unless (or (not (fboundp 'set-text-conversion-style)) + (eq old-text-conversion-style text-conversion-style)) + (set-text-conversion-style old-text-conversion-style))))) + +(defun tool-bar-event-apply-control-modifier (_ignore-prompt) + "Like `event-apply-control-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + ;; Save the previously used text conversion style. + (let ((old-text-conversion-style text-conversion-style)) + ;; Disable text conversion. + (when (fboundp 'set-text-conversion-style) + (set-text-conversion-style nil)) + (unwind-protect + (progn + ;; Display the on screen keyboard. + (frame-toggle-on-screen-keyboard nil nil) + (let* ((modifiers '(control)) event1 + (overriding-text-conversion-style nil) + (event (read-event))) + ;; Combine any more modifier key presses. + (while (eq event 'tool-bar) + (setq event1 (event-basic-type (read-event))) + ;; Reject unknown tool bar events. + (unless (memq event1 '(alt super hyper shift control meta)) + (user-error "Unknown tool-bar event %s" event1)) + ;; If `event' is the name of a modifier key, apply that + ;; modifier key as well. + (unless (memq event1 modifiers) + (push event1 modifiers)) + ;; Read another event. + (setq event (read-event))) + ;; EVENT is a keyboard event to which the specified list of + ;; modifier keys should be applied. + (vector (tool-bar-apply-modifiers event modifiers)))) + ;; Re-enable text conversion if necessary. + (unless (or (not (fboundp 'set-text-conversion-style)) + (eq old-text-conversion-style text-conversion-style)) + (set-text-conversion-style old-text-conversion-style))))) + +(defun tool-bar-event-apply-meta-modifier (_ignore-prompt) + "Like `event-apply-meta-modifier'. +However, take additional modifier tool bar items into account; +apply any extra modifiers bound to subsequent `tool-bar' events." + ;; Save the previously used text conversion style. + (let ((old-text-conversion-style text-conversion-style)) + ;; Disable text conversion. + (when (fboundp 'set-text-conversion-style) + (set-text-conversion-style nil)) + (unwind-protect + (progn + ;; Display the on screen keyboard. + (frame-toggle-on-screen-keyboard nil nil) + (let* ((modifiers '(meta)) event1 + (overriding-text-conversion-style nil) + (event (read-event))) + ;; Combine any more modifier key presses. + (while (eq event 'tool-bar) + (setq event1 (event-basic-type (read-event))) + ;; Reject unknown tool bar events. + (unless (memq event1 '(alt super hyper shift control meta)) + (user-error "Unknown tool-bar event %s" event1)) + ;; If `event' is the name of a modifier key, apply that + ;; modifier key as well. + (unless (memq event1 modifiers) + (push event1 modifiers)) + ;; Read another event. + (setq event (read-event))) + ;; EVENT is a keyboard event to which the specified list of + ;; modifier keys should be applied. + (vector (tool-bar-apply-modifiers event modifiers)))) + ;; Re-enable text conversion if necessary. + (unless (or (not (fboundp 'set-text-conversion-style)) + (eq old-text-conversion-style text-conversion-style)) + (set-text-conversion-style old-text-conversion-style))))) + +(define-minor-mode modifier-bar-mode + "Toggle display of the modifier bar. + +When enabled, a small tool bar will be displayed next to the tool +bar containing items bound to +`tool-bar-event-apply-control-modifier' and its related commands, +which see." + :init-value nil + :global t + :group 'tool-bar + (if modifier-bar-mode + (progn + (setq secondary-tool-bar-map + ;; The commands specified in the menu items here are not + ;; used. Instead, Emacs relies on each of the tool bar + ;; events being specified in `input-decode-map'. + `(keymap (control menu-item "Control Key" + event-apply-control-modifier + :help "Add Control modifier to the following event" + :image ,(tool-bar--image-expression "ctrl")) + (shift menu-item "Shift Key" + event-apply-shift-modifier + :help "Add Shift modifier to the following event" + :image ,(tool-bar--image-expression "shift")) + (meta menu-item "Meta Key" + event-apply-meta-modifier + :help "Add Meta modifier to the following event" + :image ,(tool-bar--image-expression "meta")) + (alt menu-item "Alt Key" + event-apply-alt-modifier + :help "Add Alt modifier to the following event" + :image ,(tool-bar--image-expression "alt")) + (super menu-item "Super Key" + event-apply-super-modifier + :help "Add Super modifier to the following event" + :image ,(tool-bar--image-expression "super")) + (hyper menu-item "Hyper Key" + event-apply-hyper-modifier + :help "Add Hyper modifier to the following event" + :image ,(tool-bar--image-expression "hyper")))) + (define-key input-decode-map [tool-bar control] + #'tool-bar-event-apply-control-modifier) + (define-key input-decode-map [tool-bar shift] + #'tool-bar-event-apply-shift-modifier) + (define-key input-decode-map [tool-bar meta] + #'tool-bar-event-apply-meta-modifier) + (define-key input-decode-map [tool-bar alt] + #'tool-bar-event-apply-alt-modifier) + (define-key input-decode-map [tool-bar super] + #'tool-bar-event-apply-super-modifier) + (define-key input-decode-map [tool-bar hyper] + #'tool-bar-event-apply-hyper-modifier)) + (setq secondary-tool-bar-map nil)) + (force-mode-line-update t)) (provide 'tool-bar) diff --git a/src/dispextern.h b/src/dispextern.h index 402972d33d9..cf67121809f 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -3364,9 +3364,13 @@ #define DEFAULT_TAB_BAR_IMAGE_HEIGHT 18 /* If we shall show the label only below the icon and not beside it. */ TOOL_BAR_ITEM_VERT_ONLY, + /* Whether or not this tool bar item is hidden and should cause + subsequent items to be displayed on a new line. */ + TOOL_BAR_ITEM_WRAP, + /* Sentinel = number of slots in tool_bar_items occupied by one tool-bar item. */ - TOOL_BAR_ITEM_NSLOTS + TOOL_BAR_ITEM_NSLOTS, }; diff --git a/src/frame.c b/src/frame.c index 65711106671..35881ce6de1 100644 --- a/src/frame.c +++ b/src/frame.c @@ -986,6 +986,7 @@ make_frame (bool mini_p) f->last_tab_bar_item = -1; #ifndef HAVE_EXT_TOOL_BAR f->last_tool_bar_item = -1; + f->tool_bar_wraps_p = false; #endif #ifdef NS_IMPL_COCOA f->ns_appearance = ns_appearance_system_default; diff --git a/src/frame.h b/src/frame.h index 8142dec456b..8ed9c0f37d8 100644 --- a/src/frame.h +++ b/src/frame.h @@ -344,6 +344,10 @@ #define EMACS_FRAME_H /* Set to true to minimize tool-bar height even when auto-resize-tool-bar is set to grow-only. */ bool_bf minimize_tool_bar_window_p : 1; + + /* Whether or not the tool bar contains a ``new line'' item. If + true, tool bar rows will be allowed to differ in height. */ + bool_bf tool_bar_wraps_p : 1; #endif #ifdef HAVE_EXT_TOOL_BAR diff --git a/src/keyboard.c b/src/keyboard.c index 19fdbd11724..b33fbf8f155 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -9325,7 +9325,13 @@ set_prop (ptrdiff_t idx, Lisp_Object val) - `:label LABEL-STRING'. - A text label to show with the tool bar button if labels are enabled. */ + A text label to show with the tool bar button if labels are + enabled. + + - `:wrap WRAP' + + WRAP specifies whether to hide this item but display subsequent + tool bar items on a new line. */ static bool parse_tool_bar_item (Lisp_Object key, Lisp_Object item) @@ -9333,7 +9339,15 @@ parse_tool_bar_item (Lisp_Object key, Lisp_Object item) Lisp_Object filter = Qnil; Lisp_Object caption; int i; - bool have_label = false; + bool have_label; +#ifndef HAVE_EXT_TOOL_BAR + bool is_wrap; +#endif /* HAVE_EXT_TOOL_BAR */ + + have_label = false; +#ifndef HAVE_EXT_TOOL_BAR + is_wrap = false; +#endif /* HAVE_EXT_TOOL_BAR */ /* Definition looks like `(menu-item CAPTION BINDING PROPS...)'. Rule out items that aren't lists, don't start with @@ -9469,6 +9483,20 @@ parse_tool_bar_item (Lisp_Object key, Lisp_Object item) else if (EQ (ikey, QCrtl)) /* ':rtl STRING' */ set_prop (TOOL_BAR_ITEM_RTL_IMAGE, value); + else if (EQ (ikey, QCwrap)) + { +#ifndef HAVE_EXT_TOOL_BAR + /* This specifies whether the tool bar item should be hidden + but cause subsequent items to be displayed on a new + line. */ + set_prop (TOOL_BAR_ITEM_WRAP, value); + is_wrap = !NILP (value); +#else /* HAVE_EXT_TOOL_BAR */ + /* Line wrapping isn't supported on builds utilizing + external tool bars. */ + return false; +#endif /* !HAVE_EXT_TOOL_BAR */ + } } @@ -9529,6 +9557,15 @@ parse_tool_bar_item (Lisp_Object key, Lisp_Object item) if (CONSP (get_keymap (PROP (TOOL_BAR_ITEM_BINDING), 0, 1))) return 0; + +#ifndef HAVE_EXT_TOOL_BAR + /* If the menu item is actually a line wrap, make sure it isn't + visible or enabled. */ + + if (is_wrap) + set_prop (TOOL_BAR_ITEM_ENABLED_P, Qnil); +#endif /* !HAVE_EXT_TOOL_BAR */ + /* If there is a key binding, add it to the help, which will be displayed as a tooltip for this entry. */ Lisp_Object binding = PROP (TOOL_BAR_ITEM_BINDING); @@ -12482,6 +12519,7 @@ syms_of_keyboard (void) DEFSYM (Qhelp_echo, "help-echo"); DEFSYM (Qhelp_echo_inhibit_substitution, "help-echo-inhibit-substitution"); DEFSYM (QCrtl, ":rtl"); + DEFSYM (QCwrap, ":wrap"); staticpro (&item_properties); item_properties = Qnil; diff --git a/src/xdisp.c b/src/xdisp.c index 43c628b73d8..d09116b3fb5 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -15000,7 +15000,10 @@ update_tool_bar (struct frame *f, bool save_match_data) /* Set F->desired_tool_bar_string to a Lisp string representing frame F's desired tool-bar contents. F->tool_bar_items must have - been set up previously by calling prepare_menu_bars. */ + been set up previously by calling prepare_menu_bars. + + Also set F->tool_bar_wraps_p to whether or not the tool bar + contains explicit line breaking items. */ static void build_desired_tool_bar_string (struct frame *f) @@ -15022,9 +15025,11 @@ build_desired_tool_bar_string (struct frame *f) size_needed = f->n_tool_bar_items; /* Reuse f->desired_tool_bar_string, if possible. */ + if (size < size_needed || NILP (f->desired_tool_bar_string)) - fset_desired_tool_bar_string - (f, Fmake_string (make_fixnum (size_needed), make_fixnum (' '), Qnil)); + /* Don't initialize the contents of this string yet, as they will + be set within the loop below. */ + fset_desired_tool_bar_string (f, make_uninit_string (size_needed)); else { AUTO_LIST4 (props, Qdisplay, Qnil, Qmenu_item, Qnil); @@ -15032,6 +15037,8 @@ build_desired_tool_bar_string (struct frame *f) props, f->desired_tool_bar_string); } + f->tool_bar_wraps_p = false; + /* Put a `display' property on the string for the images to display, put a `menu_item' property on tool-bar items with a value that is the index of the item in F's tool-bar item vector. */ @@ -15044,6 +15051,21 @@ #define PROP(IDX) \ bool selected_p = !NILP (PROP (TOOL_BAR_ITEM_SELECTED_P)); int hmargin, vmargin, relief, idx, end; + if (!NILP (PROP (TOOL_BAR_ITEM_WRAP))) + { + /* This is a line wrap. Instead of building a tool bar + item, display a new line character instead. */ + SSET (f->desired_tool_bar_string, i, '\n'); + + /* Set F->tool_bar_wraps_p. This tells redisplay_tool_bar + to allow individual rows to be different heights. */ + f->tool_bar_wraps_p = true; + continue; + } + + /* Replace this with a space character. */ + SSET (f->desired_tool_bar_string, i, ' '); + /* If image is a vector, choose the image according to the button state. */ image = PROP (TOOL_BAR_ITEM_IMAGES); @@ -15155,6 +15177,16 @@ #define PROP(IDX) \ props, f->desired_tool_bar_string); #undef PROP } + + /* Now replace each character between i and the end of the tool bar + string with spaces, to prevent stray newlines from accumulating + when the number of tool bar items decreases. `size' is 0 if the + tool bar string is new, but in that case the string will have + been completely initialized anyway. */ + + for (; i < size; ++i) + /* Replace this with a space character. */ + SSET (f->desired_tool_bar_string, i, ' '); } @@ -15168,7 +15200,10 @@ #define PROP(IDX) \ If HEIGHT is -1, we are counting needed tool-bar lines, so don't count a final empty row in case the tool-bar width exactly matches the window width. -*/ + + HEIGHT may also be -1 if there is an explicit line wrapping item + inside the tool bar; in that case, allow individual rows of the + tool bar to differ in height. */ static void display_tool_bar_line (struct it *it, int height) @@ -15232,8 +15267,18 @@ display_tool_bar_line (struct it *it, int height) ++i; } - /* Stop at line end. */ + /* Stop at the end of the iterator, and move to the next line + upon a '\n' appearing in the tool bar string. Tool bar + strings may contain multiple new line characters when + explicit wrap items are encountered. */ + if (ITERATOR_AT_END_OF_LINE_P (it)) + { + reseat_at_next_visible_line_start (it, false); + break; + } + + if (ITERATOR_AT_END_P (it)) break; set_iterator_to_next (it, true); @@ -15260,7 +15305,8 @@ display_tool_bar_line (struct it *it, int height) last->left_box_line_p = true; /* Make line the desired height and center it vertically. */ - if ((height -= it->max_ascent + it->max_descent) > 0) + if (height != -1 + && (height -= it->max_ascent + it->max_descent) > 0) { /* Don't add more than one line height. */ height %= FRAME_LINE_HEIGHT (it->f); @@ -15294,6 +15340,7 @@ display_tool_bar_line (struct it *it, int height) /* Value is the number of pixels needed to make all tool-bar items of frame F visible. The actual number of glyph rows needed is returned in *N_ROWS if non-NULL. */ + static int tool_bar_height (struct frame *f, int *n_rows, bool pixelwise) { @@ -15371,7 +15418,9 @@ redisplay_tool_bar (struct frame *f) struct window *w; struct it it; struct glyph_row *row; + bool change_height_p; + change_height_p = false; f->tool_bar_redisplayed = true; /* If frame hasn't a tool-bar window or if it is zero-height, don't @@ -15455,18 +15504,39 @@ redisplay_tool_bar (struct frame *f) border = 0; rows = f->n_tool_bar_rows; - height = max (1, (it.last_visible_y - border) / rows); - extra = it.last_visible_y - border - height * rows; - while (it.current_y < it.last_visible_y) + if (f->tool_bar_wraps_p) { - int h = 0; - if (extra > 0 && rows-- > 0) + /* If the tool bar contains explicit line wrapping items, + don't force each row to have a fixed height. */ + + while (!ITERATOR_AT_END_P (&it)) + display_tool_bar_line (&it, -1); + + /* Because changes to individual tool bar items may now + change the height of the tool bar, adjust the height of + the tool bar window if it is different from the tool bar + height in any way. */ + + if (it.current_y != it.last_visible_y) + change_height_p = true; + } + else + { + height = max (1, (it.last_visible_y - border) / rows); + extra = it.last_visible_y - border - height * rows; + + while (it.current_y < it.last_visible_y) { - h = (extra + rows - 1) / rows; - extra -= h; + int h = 0; + if (extra > 0 && rows-- > 0) + { + h = (extra + rows - 1) / rows; + extra -= h; + } + + display_tool_bar_line (&it, height + h); } - display_tool_bar_line (&it, height + h); } } else @@ -15482,8 +15552,6 @@ redisplay_tool_bar (struct frame *f) if (!NILP (Vauto_resize_tool_bars)) { - bool change_height_p = false; - /* If we couldn't display everything, change the tool-bar's height if there is room for more. */ if (IT_STRING_CHARPOS (it) < it.end_charpos) commit 2963924d803e2d0247169f477c31d4c91281928b Author: Po Lu Date: Tue Jul 4 08:53:37 2023 +0800 ; * src/sfnt.c (sfnt_decompose_compound_glyph): Pacify warning. diff --git a/src/sfnt.c b/src/sfnt.c index b803ada8955..da4cb3cc783 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -2651,7 +2651,7 @@ sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph, bool need_free; struct sfnt_compound_glyph_component *component; sfnt_fixed x, y, xtemp, ytemp; - size_t point, point2, index; + size_t point UNINIT, point2 UNINIT, index; uint16_t last_point, number_of_contours; sfnt_fixed *x_base, *y_base; size_t *contour_base; commit 79207a055fbc923b416253d9ecda21b349e00f68 Merge: a273b95fa50 4d49b9bad4f Author: Po Lu Date: Tue Jul 4 08:52:29 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit a273b95fa505182dfa4686782c464165e14813ba Merge: 61a38b470de d679f9e388c Author: Po Lu Date: Mon Jul 3 14:30:12 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 61a38b470de2f9796ccf8273eca09622abc43368 Merge: 2baf2c5fd9a a5bf0ae6614 Author: Po Lu Date: Mon Jul 3 08:05:55 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 2baf2c5fd9abe465a53e035131667e06ef553123 Merge: 3b75148a304 9b9dcc146ba Author: Po Lu Date: Sun Jul 2 08:26:54 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 3b75148a304f4978c5e43aecf5a2748ecf203ca7 Merge: 522c5720750 952692643e9 Author: Po Lu Date: Sat Jul 1 08:33:13 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 522c572075010f04c94fe8b7773bf40d0928a2ff Author: Po Lu Date: Fri Jun 30 16:20:02 2023 +0800 * src/android.c (android_query_tree): Correctly return children. diff --git a/src/android.c b/src/android.c index da5e8da3be1..2a2d134c3c8 100644 --- a/src/android.c +++ b/src/android.c @@ -5631,7 +5631,9 @@ android_query_tree (android_window handle, android_window *root_return, android_exception_check_nonnull (shorts, array); for (i = 1; i < nelements; ++i) - children[i] = shorts[i]; + /* Subtract one from the index into children, since the parent is + not included. */ + children[i - 1] = shorts[i]; /* Finally, return the parent and other values. */ *root_return = 0; commit 714c425385861d2a9e7b6bc95c27c3f9b1eca0aa Merge: df761843f00 361bf8a1132 Author: Po Lu Date: Fri Jun 30 08:36:53 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit df761843f00966d34d452b54c2f4d92adad1b697 Merge: 8f87af4237d d6bcb39ba9d Author: Po Lu Date: Thu Jun 29 08:28:41 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 8f87af4237df02355cc4b4e9d0f73bf1c90210fa Merge: 93d431f0048 28b7745c677 Author: Po Lu Date: Wed Jun 28 08:29:27 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 93d431f0048edf277a6521acbc7048e3957db4b4 Author: Po Lu Date: Tue Jun 27 20:31:27 2023 +0800 ; * doc/emacs/android.texi (Android Environment): Improve wording. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 99593f25e8b..84da630576c 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -305,9 +305,9 @@ Android Environment One side effect of the mechanism by which process tracing is carried out is that job control facilities inside inferior shells -(@pxref{Interactive Subshell}) will not be able to stop its -subprocesses, and sending the @code{SIGSTOP} signal to a subprocess -created by Emacs will appear to have no effect. +(@pxref{Interactive Shell}) will not be able to stop processes, and +sending the @code{SIGSTOP} signal to a subprocess created by Emacs +will appear to have no effect. In addition, Android 12 also terminates subprocesses which are consuming CPU while Emacs itself is in the background. The system commit 47a36690f998a5d4d29fa9df2129d21c38432150 Author: Po Lu Date: Tue Jun 27 20:25:35 2023 +0800 ; * doc/emacs/android.texi (Android Environment): Fix typos. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index f13b3e640ae..99593f25e8b 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -288,7 +288,7 @@ Android Environment @vindex android-use-exec-loader Android 10 and later also prohibit Emacs itself from running executables inside the app data directory, obstensibly for security -readers. On these systems, Emacs normally applies a workaround; +reasons. On these systems, Emacs normally applies a workaround; however, this workaround requires running all sub-processes through another subprocess which implements an executable loader and applies process tracing to all its children, which may prove to be problematic @@ -304,9 +304,10 @@ Android Environment @code{process-id} for other purposes will not. One side effect of the mechanism by which process tracing is carried -out is that job control facilities will not be able to stop -subprocesses, and the @code{SIGSTOP} signal will appear to have no -effect. +out is that job control facilities inside inferior shells +(@pxref{Interactive Subshell}) will not be able to stop its +subprocesses, and sending the @code{SIGSTOP} signal to a subprocess +created by Emacs will appear to have no effect. In addition, Android 12 also terminates subprocesses which are consuming CPU while Emacs itself is in the background. The system commit 3079e15f546de2c4b36259521eb960bfd248603e Author: Po Lu Date: Tue Jun 27 15:33:05 2023 +0800 Update Android port * src/android.c (android_exception_check) (android_exception_check_1) (android_exception_check_2) (android_exception_check_nonnull) (android_exception_check_nonnull_1): Tell the compiler to expect that the object is non-NULL, or that no exception has been thrown. diff --git a/src/android.c b/src/android.c index e45aa82fc3c..da5e8da3be1 100644 --- a/src/android.c +++ b/src/android.c @@ -6565,8 +6565,8 @@ android_build_jstring (const char *text) Typically, you use these functions by calling them immediately after a JNI function which allocates memory, passing it any local - references that are already valid but are not used after leaving - the current scope. For example, to allocate foo and then make + references that are already valid but should be deleted after + leaving the current scope. For example, to allocate foo, make global_foo its global reference, and then release foo, you write: jobject foo, global_foo; @@ -6585,22 +6585,28 @@ android_build_jstring (const char *text) if global_foo cannot be allocated, and after the global reference is created. */ +#if __GNUC__ >= 3 +#define likely(cond) __builtin_expect ((cond), 1) +#else /* __GNUC__ < 3 */ +#define likely(cond) (cond) +#endif /* __GNUC__ >= 3 */ + /* Check for JNI exceptions and call memory_full in that situation. */ void android_exception_check (void) { - if ((*android_java_env)->ExceptionCheck (android_java_env)) - { - __android_log_print (ANDROID_LOG_WARN, __func__, - "Possible out of memory error. " - " The Java exception follows: "); - /* Describe exactly what went wrong. */ - (*android_java_env)->ExceptionDescribe (android_java_env); - (*android_java_env)->ExceptionClear (android_java_env); - memory_full (0); - } + if (likely (!(*android_java_env)->ExceptionCheck (android_java_env))) + return; + + __android_log_print (ANDROID_LOG_WARN, __func__, + "Possible out of memory error. " + " The Java exception follows: "); + /* Describe exactly what went wrong. */ + (*android_java_env)->ExceptionDescribe (android_java_env); + (*android_java_env)->ExceptionClear (android_java_env); + memory_full (0); } /* Check for JNI exceptions. If there is one such exception, clear @@ -6610,17 +6616,17 @@ android_exception_check (void) void android_exception_check_1 (jobject object) { - if ((*android_java_env)->ExceptionCheck (android_java_env)) - { - __android_log_print (ANDROID_LOG_WARN, __func__, - "Possible out of memory error. " - " The Java exception follows: "); - /* Describe exactly what went wrong. */ - (*android_java_env)->ExceptionDescribe (android_java_env); - (*android_java_env)->ExceptionClear (android_java_env); - ANDROID_DELETE_LOCAL_REF (object); - memory_full (0); - } + if (likely (!(*android_java_env)->ExceptionCheck (android_java_env))) + return; + + __android_log_print (ANDROID_LOG_WARN, __func__, + "Possible out of memory error. " + " The Java exception follows: "); + /* Describe exactly what went wrong. */ + (*android_java_env)->ExceptionDescribe (android_java_env); + (*android_java_env)->ExceptionClear (android_java_env); + ANDROID_DELETE_LOCAL_REF (object); + memory_full (0); } /* Like android_exception_check_1, except it takes more than one local @@ -6629,18 +6635,18 @@ android_exception_check_1 (jobject object) void android_exception_check_2 (jobject object, jobject object1) { - if ((*android_java_env)->ExceptionCheck (android_java_env)) - { - __android_log_print (ANDROID_LOG_WARN, __func__, - "Possible out of memory error. " - " The Java exception follows: "); - /* Describe exactly what went wrong. */ - (*android_java_env)->ExceptionDescribe (android_java_env); - (*android_java_env)->ExceptionClear (android_java_env); - ANDROID_DELETE_LOCAL_REF (object); - ANDROID_DELETE_LOCAL_REF (object1); - memory_full (0); - } + if (likely (!(*android_java_env)->ExceptionCheck (android_java_env))) + return; + + __android_log_print (ANDROID_LOG_WARN, __func__, + "Possible out of memory error. " + " The Java exception follows: "); + /* Describe exactly what went wrong. */ + (*android_java_env)->ExceptionDescribe (android_java_env); + (*android_java_env)->ExceptionClear (android_java_env); + ANDROID_DELETE_LOCAL_REF (object); + ANDROID_DELETE_LOCAL_REF (object1); + memory_full (0); } /* Check for JNI problems based on the value of OBJECT. @@ -6655,7 +6661,7 @@ android_exception_check_2 (jobject object, jobject object1) void android_exception_check_nonnull (void *object, jobject object1) { - if (object) + if (likely (object != NULL)) return; if (object1) @@ -6673,7 +6679,7 @@ android_exception_check_nonnull (void *object, jobject object1) android_exception_check_nonnull_1 (void *object, jobject object1, jobject object2) { - if (object) + if (likely (object != NULL)) return; if (object1) commit 32352bec968ddcde7d1421f35a05ce6d1e42c348 Author: Po Lu Date: Tue Jun 27 09:53:04 2023 +0800 ; * exec/loader-mips64el.s (rest_of_exec): Fix typo in comment. diff --git a/exec/loader-mips64el.s b/exec/loader-mips64el.s index 00a2765a9b6..f4a6f918497 100644 --- a/exec/loader-mips64el.s +++ b/exec/loader-mips64el.s @@ -164,7 +164,7 @@ dnl syscall # syscall .rest_of_exec: move $s1, $s2 # original SP ld $t0, ($s1) # argc - dsll $t0, $t0, 3 # argc *= 3 + dsll $t0, $t0, 3 # argc *= 8 DADDI2( $t0, 16) # argc += 16 dadd $s1, $s1, $t0 # s1 = start of envp .skipenv: commit 822bc6afd65738dc20f455a17b42eb27741f4fd3 Merge: d95c5680f96 3a50773ab00 Author: Po Lu Date: Tue Jun 27 08:43:07 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit d95c5680f96133437e0b113e91ab394191a14676 Author: Po Lu Date: Mon Jun 26 09:19:07 2023 +0800 ; * doc/lispref/commands.texi (Touchscreen Events): Fix typo. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index edeea9ab27c..eaf6f3e451b 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2022,7 +2022,7 @@ Touchscreen Events @item (touchscreen-update @var{points}) This event is sent when a point on the touchscreen has changed position. @var{points} is a list of touch points containing the -up-to-date positions of each touch point currently on the touchscxcompile/reen. +up-to-date positions of each touch point currently on the touchscreen. @cindex @code{touchscreen-end} event @item (touchscreen-end @var{point}) commit c3fadfd10f353dcc8bdb0f4958a3e9fb7c4589bb Author: Po Lu Date: Mon Jun 26 09:11:48 2023 +0800 Update Android port * lisp/calc/calc.el (calc-mode, calc): Make sure the on-screen keyboard is not hidden when a Calc buffer is created or a Calc Trail window is being created for the first time. * lisp/touch-screen.el (touch-screen-window-selection-changed): Take touch-screen-display-keyboard in to account. * src/sfnt.c (sfnt_decompose_compound_glyph) (sfnt_interpret_compound_glyph_1): Reset `defer_offsets' before processing each component. (sfnt_lerp_half): Avoid undefined shift of negative value. (sfnt_compute_tuple_scale): Pacify compiler warning. diff --git a/lisp/calc/calc.el b/lisp/calc/calc.el index a1545edba19..c0e68ceab0c 100644 --- a/lisp/calc/calc.el +++ b/lisp/calc/calc.el @@ -1357,7 +1357,10 @@ calc-mode (calc-set-mode-line) (calc-check-defines) (if calc-buffer-list (setq calc-stack (copy-sequence calc-stack))) - (add-to-list 'calc-buffer-list (current-buffer) t)) + (add-to-list 'calc-buffer-list (current-buffer) t) + ;; While Calc buffers are read only, the on screen keyboard should + ;; be displayed in order to accept user input. + (setq-local touch-screen-display-keyboard t)) (defvar calc-check-defines 'calc-check-defines) ; Suitable for run-hooks. (defun calc-check-defines () @@ -1451,49 +1454,54 @@ calc (calc-grab-region (region-beginning) (region-end) nil) (when (= (prefix-numeric-value arg) -2) (calc-keypad)))) - (when (get-buffer-window "*Calc Keypad*") - (calc-keypad) - (set-buffer (window-buffer))) - (if (derived-mode-p 'calc-mode) - (calc-quit) - (calc-create-buffer) - (setq calc-was-keypad-mode nil) - (if (or (eq full-display t) - (and (null full-display) calc-full-mode)) - (switch-to-buffer (current-buffer) t) - (if (get-buffer-window (current-buffer)) - (select-window (get-buffer-window (current-buffer))) - (if calc-window-hook - (run-hooks 'calc-window-hook) - (let ((w (get-largest-window))) - (if (and pop-up-windows - (> (window-height w) - (+ window-min-height calc-window-height 2))) - (progn - (setq w (split-window w - (- (window-height w) - calc-window-height 2) - nil)) - (set-window-buffer w (current-buffer)) - (select-window w)) - (pop-to-buffer (current-buffer))))))) - (with-current-buffer (calc-trail-buffer) - (and calc-display-trail - (calc-trail-display 1 t))) - (message (substitute-command-keys - (concat "Welcome to the GNU Emacs Calculator! \\" - "Press \\[calc-help] or \\[calc-help-prefix] for help, \\[calc-quit] to quit"))) - (run-hooks 'calc-start-hook) - (and (windowp full-display) - (window-point full-display) - (select-window full-display)) - (and calc-make-windows-dedicated - (set-window-dedicated-p nil t)) - (calc-check-defines) - (when (and calc-said-hello interactive) - (sit-for 2) - (message "")) - (setq calc-said-hello t)))) + ;; If the selected window changes here, Emacs may think that the + ;; selected window is read only, and no on screen keyboard should + ;; be displayed. Make sure that any active on screen keyboard is + ;; not hidden by accident. + (let ((touch-screen-display-buffer t)) + (when (get-buffer-window "*Calc Keypad*") + (calc-keypad) + (set-buffer (window-buffer))) + (if (derived-mode-p 'calc-mode) + (calc-quit) + (calc-create-buffer) + (setq calc-was-keypad-mode nil) + (if (or (eq full-display t) + (and (null full-display) calc-full-mode)) + (switch-to-buffer (current-buffer) t) + (if (get-buffer-window (current-buffer)) + (select-window (get-buffer-window (current-buffer))) + (if calc-window-hook + (run-hooks 'calc-window-hook) + (let ((w (get-largest-window))) + (if (and pop-up-windows + (> (window-height w) + (+ window-min-height calc-window-height 2))) + (progn + (setq w (split-window w + (- (window-height w) + calc-window-height 2) + nil)) + (set-window-buffer w (current-buffer)) + (select-window w)) + (pop-to-buffer (current-buffer))))))) + (with-current-buffer (calc-trail-buffer) + (and calc-display-trail + (calc-trail-display 1 t))) + (message (substitute-command-keys + (concat "Welcome to the GNU Emacs Calculator! \\" + "Press \\[calc-help] or \\[calc-help-prefix] for help, \\[calc-quit] to quit"))) + (run-hooks 'calc-start-hook) + (and (windowp full-display) + (window-point full-display) + (select-window full-display)) + (and calc-make-windows-dedicated + (set-window-dedicated-p nil t)) + (calc-check-defines) + (when (and calc-said-hello interactive) + (sit-for 2) + (message "")) + (setq calc-said-hello t))))) ;;;###autoload (defun full-calc (&optional interactive) diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index ba55374d090..242ea4fcd9b 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -373,8 +373,11 @@ touch-screen-window-selection-changed Otherwise, cancel any timer that is supposed to hide the keyboard in response to the minibuffer being closed." (with-selected-frame frame - (if (or buffer-read-only - (get-text-property (point) 'read-only)) + (if (and (or buffer-read-only + (get-text-property (point) 'read-only)) + ;; Don't hide the on-screen keyboard if it's always + ;; supposed to be displayed. + (not touch-screen-display-keyboard)) (frame-toggle-on-screen-keyboard (selected-frame) t) ;; Prevent hiding the minibuffer from hiding the on screen ;; keyboard. diff --git a/src/sfnt.c b/src/sfnt.c index 7ef56a47469..b803ada8955 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -2672,9 +2672,6 @@ sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph, if (recursion_count > 16) return 1; - /* Don't defer offsets. */ - defer_offsets = false; - for (j = 0; j < glyph->compound->num_components; ++j) { /* Look up the associated subglyph. */ @@ -2685,6 +2682,12 @@ sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph, if (!subglyph) return 1; + /* Don't defer offsets. This variable is set if the component + glyph is a compound glyph that is anchored to a previously + decomposed point, and needs its coordinates adjusted after + decomposition completes. */ + defer_offsets = false; + /* Record the size of the point array before expansion. This will be the base to apply to all points coming from this subglyph. */ @@ -2922,8 +2925,8 @@ sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph, sfnt_lerp_half (struct sfnt_point *control1, struct sfnt_point *control2, struct sfnt_point *result) { - result->x = control1->x + ((control2->x - control1->x) >> 1); - result->y = control1->y + ((control2->y - control1->y) >> 1); + result->x = control1->x + ((control2->x - control1->x) / 2); + result->y = control1->y + ((control2->y - control1->y) / 2); } /* Decompose contour data inside X, Y and FLAGS, between the indices @@ -11624,9 +11627,6 @@ sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph, if (recursion_count > 16) return "Overly deep recursion in compound glyph data"; - /* Don't defer offsets. */ - defer_offsets = false; - /* Pacify -Wmaybe-uninitialized. */ point = point2 = 0; @@ -11640,6 +11640,12 @@ sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph, if (!subglyph) return "Failed to obtain component glyph"; + /* Don't defer offsets. This variable is set if the component + glyph is a compound glyph that is anchored to a previously + decomposed point, and needs its coordinates adjusted after + decomposition completes. */ + defer_offsets = false; + /* Record the size of the point array before expansion. This will be the base to apply to all points coming from this subglyph. */ @@ -13783,7 +13789,7 @@ sfnt_compute_tuple_scale (struct sfnt_blend *blend, bool intermediate_p, sfnt_f2dot14 *intermediate_end) { int i; - sfnt_fixed coord, start, end; + sfnt_fixed coord, start UNINIT, end UNINIT; sfnt_fixed scale; /* scale is initially 1.0. */ @@ -13794,6 +13800,9 @@ sfnt_compute_tuple_scale (struct sfnt_blend *blend, bool intermediate_p, /* Load values for this axis, scaled up to sfnt_fixed. */ coord = coords[i] * 4; + /* GCC warns about start and end being used when uninitialized, + but they are used only if intermediate_p. */ + if (intermediate_p) { start = intermediate_start[i] * 4; commit 259bec95de1f2786c0e642ba5b9efb4a750bc432 Merge: 7b5d32fa871 a6de0d22e42 Author: Po Lu Date: Mon Jun 26 08:16:23 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 7b5d32fa871b0ef5c0860612b9c1ac9382c62672 Merge: f5d142f6637 188c90c7c11 Author: Po Lu Date: Sun Jun 25 08:16:34 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit f5d142f66370b29af58360faeea90d1112756bc5 Merge: a5ee9a69ae7 77c2f05d773 Author: Po Lu Date: Sat Jun 24 09:20:14 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit a5ee9a69ae73843bf3e620dbe474d5cdaaff5f3a Author: Po Lu Date: Fri Jun 23 11:54:56 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): * java/org/gnu/emacs/EmacsSdk7FontDriver.java (Sdk7FontEntity): (hasChar): Clean up dead stores. diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java index b54b031b56b..e1261b4a2d2 100644 --- a/java/org/gnu/emacs/EmacsDrawRectangle.java +++ b/java/org/gnu/emacs/EmacsDrawRectangle.java @@ -72,7 +72,6 @@ public final class EmacsDrawRectangle + clipBitmap.getWidth ()), (gc.clip_y_origin + clipBitmap.getHeight ())); - clipBitmap = gc.clip_mask.bitmap; if (!maskRect.setIntersect (dstRect, maskRect)) /* There is no intersection between the clip mask and the diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java index 9122b46458a..97969585d16 100644 --- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java +++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java @@ -169,8 +169,6 @@ protected static final class Sdk7FontEntity extends FontEntity public Sdk7FontEntity (Sdk7Typeface typeface) { - float width; - foundry = "Google"; family = typeface.familyName; adstyle = null; @@ -363,7 +361,7 @@ protected final class Sdk7FontObject extends FontObject public int hasChar (FontSpec font, char charCode) { - float missingGlyphWidth, emGlyphWidth, width; + float missingGlyphWidth, width; Rect rect1, rect2; Paint paint; Sdk7FontObject fontObject; @@ -382,7 +380,6 @@ protected final class Sdk7FontObject extends FontObject return 1; missingGlyphWidth = paint.measureText (TOFU_STRING); - emGlyphWidth = paint.measureText (EM_STRING); width = paint.measureText ("" + charCode); if (width == 0f) commit 866a937540c090ff7b740d7732e46c2e8b30a36d Author: Po Lu Date: Fri Jun 23 11:39:57 2023 +0800 ; * src/android.c (android_wc_lookup_string): Fix typo. diff --git a/src/android.c b/src/android.c index ff78e4c289a..e45aa82fc3c 100644 --- a/src/android.c +++ b/src/android.c @@ -5827,7 +5827,7 @@ android_wc_lookup_string (android_key_pressed_event *event, /* Now return this input method string. */ characters = (*android_java_env)->GetStringChars (android_java_env, string, NULL); - android_exception_check_nonnull (characters, string); + android_exception_check_nonnull ((void *) characters, string); /* Figure out how big the string is. */ size = (*android_java_env)->GetStringLength (android_java_env, commit 1a19ebdd28f5ba5840d5e2d713098df7bcb13385 Author: Po Lu Date: Fri Jun 23 11:39:36 2023 +0800 Correctly check result of string lookup * src/android.c (android_wc_lookup_string): Check that GetStringChars returns non-NULL, not if it throws an exception. diff --git a/src/android.c b/src/android.c index d6a56dfe0b8..ff78e4c289a 100644 --- a/src/android.c +++ b/src/android.c @@ -5827,7 +5827,7 @@ android_wc_lookup_string (android_key_pressed_event *event, /* Now return this input method string. */ characters = (*android_java_env)->GetStringChars (android_java_env, string, NULL); - android_exception_check_1 (string); + android_exception_check_nonnull (characters, string); /* Figure out how big the string is. */ size = (*android_java_env)->GetStringLength (android_java_env, commit 55c14c72526edf64dfdf29cd8728b226d28aedf0 Merge: a61c5fb9a21 c31688cb061 Author: Po Lu Date: Fri Jun 23 08:25:30 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit a61c5fb9a212ca0bcfcca5d1652bd9d7841d95bd Merge: bdaeecd1759 72f1c12e58e Author: Po Lu Date: Thu Jun 22 09:02:19 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit bdaeecd1759e6aee5ad8bf04ba6ed9bb8fb4453d Author: Po Lu Date: Wed Jun 21 10:31:05 2023 +0800 Update Android port * src/androidfns.c (android_set_tool_bar_position): (frame_geometry): * src/androidterm.c (android_flash): (android_clear_under_internal_border): Synchronize with X. diff --git a/src/androidfns.c b/src/androidfns.c index 46ff073438a..11ed2e6c00a 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -254,15 +254,26 @@ android_set_tool_bar_position (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) { - Lisp_Object choice = list4 (Qleft, Qright, Qtop, Qbottom); + if (!EQ (new_value, Qtop) && !EQ (new_value, Qbottom)) + error ("Tool bar position must be either `top' or `bottom'"); - if (!NILP (Fmemq (new_value, choice))) - { - if (!EQ (new_value, Qtop)) - error ("The only supported tool bar position is top"); - } - else - wrong_choice (choice, new_value); + if (EQ (new_value, old_value)) + return; + + /* Set the tool bar position. */ + fset_tool_bar_position (f, new_value); + + /* Now reconfigure frame glyphs to place the tool bar at the + bottom. While the inner height has not changed, call + `resize_frame_windows' to place each of the windows at its + new position. */ + + adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_position); + adjust_frame_glyphs (f); + SET_FRAME_GARBAGED (f); + + if (FRAME_ANDROID_WINDOW (f)) + android_clear_under_internal_border (f); } void @@ -1470,7 +1481,7 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) tab_bar_height = FRAME_TAB_BAR_HEIGHT (f); tab_bar_width = (tab_bar_height - ? native_width - 2 * internal_border_width + ? native_width - 2 * internal_border_width : 0); inner_top += tab_bar_height; @@ -1478,7 +1489,14 @@ frame_geometry (Lisp_Object frame, Lisp_Object attribute) tool_bar_width = (tool_bar_height ? native_width - 2 * internal_border_width : 0); - inner_top += tool_bar_height; + + /* Subtract or add to the inner dimensions based on the tool bar + position. */ + + if (EQ (FRAME_TOOL_BAR_POSITION (f), Qtop)) + inner_top += tool_bar_height; + else + inner_bottom -= tool_bar_height; /* Construct list. */ if (EQ (attribute, Qouter_edges)) diff --git a/src/androidterm.c b/src/androidterm.c index 29076981a47..aed8e24b9fa 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -181,7 +181,8 @@ android_flash (struct frame *f) android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, flash_left, (height - flash_height - - FRAME_INTERNAL_BORDER_WIDTH (f)), + - FRAME_INTERNAL_BORDER_WIDTH (f) + - FRAME_BOTTOM_MARGIN_HEIGHT (f)), width, flash_height); } @@ -234,7 +235,8 @@ android_flash (struct frame *f) android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, flash_left, (height - flash_height - - FRAME_INTERNAL_BORDER_WIDTH (f)), + - FRAME_INTERNAL_BORDER_WIDTH (f) + - FRAME_BOTTOM_MARGIN_HEIGHT (f)), width, flash_height); } else @@ -4213,14 +4215,16 @@ android_clear_under_internal_border (struct frame *f) int width = FRAME_PIXEL_WIDTH (f); int height = FRAME_PIXEL_HEIGHT (f); int margin = FRAME_TOP_MARGIN_HEIGHT (f); - int face_id = - (FRAME_PARENT_FRAME (f) - ? (!NILP (Vface_remapping_alist) - ? lookup_basic_face (NULL, f, CHILD_FRAME_BORDER_FACE_ID) - : CHILD_FRAME_BORDER_FACE_ID) - : (!NILP (Vface_remapping_alist) - ? lookup_basic_face (NULL, f, INTERNAL_BORDER_FACE_ID) - : INTERNAL_BORDER_FACE_ID)); + int bottom_margin = FRAME_BOTTOM_MARGIN_HEIGHT (f); + int face_id = (FRAME_PARENT_FRAME (f) + ? (!NILP (Vface_remapping_alist) + ? lookup_basic_face (NULL, f, + CHILD_FRAME_BORDER_FACE_ID) + : CHILD_FRAME_BORDER_FACE_ID) + : (!NILP (Vface_remapping_alist) + ? lookup_basic_face (NULL, f, + INTERNAL_BORDER_FACE_ID) + : INTERNAL_BORDER_FACE_ID)); struct face *face = FACE_FROM_ID_OR_NULL (f, face_id); if (face) @@ -4236,7 +4240,8 @@ android_clear_under_internal_border (struct frame *f) android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, width - border, 0, border, height); android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, - height - border, width, border); + height - bottom_margin - border, + width, border); android_set_foreground (gc, FRAME_FOREGROUND_PIXEL (f)); } else @@ -4248,7 +4253,8 @@ android_clear_under_internal_border (struct frame *f) android_clear_area (FRAME_ANDROID_DRAWABLE (f), width - border, 0, border, height); android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, - height - border, width, border); + height - bottom_margin - border, + width, border); } } } commit 70cf0d7c6f39a53103cb3f6222cedb8345f5d920 Merge: af13157653b db6de49f231 Author: Po Lu Date: Wed Jun 21 10:12:18 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit af13157653b05cbd98ea3d7f9b461b37fd69a6e4 Merge: d7abe9cdb72 bc6068fe945 Author: Po Lu Date: Wed Jun 21 08:52:25 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit d7abe9cdb721ec1b1edeb4671c83d1ed4d6d64ed Author: Po Lu Date: Tue Jun 20 10:13:09 2023 +0800 ; * src/androidfns.c (android_frame_parm_handlers): Fix typo. diff --git a/src/androidfns.c b/src/androidfns.c index c76e2c0075d..46ff073438a 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -2947,7 +2947,7 @@ android_set_no_accept_focus (struct frame *f, Lisp_Object new_value, gui_set_visibility, android_set_tab_bar_lines, android_set_tool_bar_lines, - android_set_tool_bar_position, + NULL, NULL, gui_set_screen_gamma, gui_set_line_spacing, @@ -2958,7 +2958,7 @@ android_set_no_accept_focus (struct frame *f, Lisp_Object new_value, gui_set_font_backend, android_set_alpha, NULL, - NULL, + android_set_tool_bar_position, NULL, NULL, android_set_parent_frame, commit c8aca918f798e8131f5075b41d0c957e77721e31 Author: Po Lu Date: Tue Jun 20 10:06:34 2023 +0800 Synchronize tool bar position code with X * src/androidfns.c (android_set_tool_bar_position): New function. (android_frame_parm_handlers): Add new frame param handler. diff --git a/src/androidfns.c b/src/androidfns.c index cc48de1a359..c76e2c0075d 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -249,6 +249,22 @@ android_set_tool_bar_lines (struct frame *f, Lisp_Object value, android_change_tool_bar_height (f, nlines * FRAME_LINE_HEIGHT (f)); } +static void +android_set_tool_bar_position (struct frame *f, + Lisp_Object new_value, + Lisp_Object old_value) +{ + Lisp_Object choice = list4 (Qleft, Qright, Qtop, Qbottom); + + if (!NILP (Fmemq (new_value, choice))) + { + if (!EQ (new_value, Qtop)) + error ("The only supported tool bar position is top"); + } + else + wrong_choice (choice, new_value); +} + void android_change_tool_bar_height (struct frame *f, int height) { @@ -2931,7 +2947,7 @@ android_set_no_accept_focus (struct frame *f, Lisp_Object new_value, gui_set_visibility, android_set_tab_bar_lines, android_set_tool_bar_lines, - NULL, + android_set_tool_bar_position, NULL, gui_set_screen_gamma, gui_set_line_spacing, commit 8f3fee7dffadfce25c8f47fb71674f77417b42e5 Merge: d067b2fca00 1e13610b757 Author: Po Lu Date: Tue Jun 20 09:18:27 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit d067b2fca0005f3032d82f80f5c4d88750395dfc Author: Po Lu Date: Mon Jun 19 16:41:58 2023 +0800 * lib-src/Makefile.in (seccomp-filter$(EXEEXT)): Link with Gnulib. diff --git a/lib-src/Makefile.in b/lib-src/Makefile.in index 8a1922703de..8a5ce019ca2 100644 --- a/lib-src/Makefile.in +++ b/lib-src/Makefile.in @@ -481,7 +481,7 @@ emacsclient.res: ifeq ($(SECCOMP_FILTER),1) seccomp-filter$(EXEEXT): $(srcdir)/seccomp-filter.c $(config_h) $(AM_V_CCLD)$(CC) $(ALL_CFLAGS) $(LIBSECCOMP_CFLAGS) $< \ - $(LIBSECCOMP_LIBS) -o $@ + $(LIBSECCOMP_LIBS) $(LOADLIBES) -o $@ seccomp-filter.bpf seccomp-filter.pfc seccomp-filter-exec.bpf seccomp-filter-exec.pfc: seccomp-filter$(EXEEXT) $(AM_V_GEN)./seccomp-filter$(EXEEXT) \ commit 405d14402f21df3404ce9c5aa3c7f942e6deb3e3 Author: Po Lu Date: Mon Jun 19 15:26:07 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsView.java (EmacsView, dimensionsLock): New field. (): Create new lock object. (handleDirtyBitmap, onLayout, onAttachedToWindow): Make sure measuredWidth and measuredHeight are extracted and set atomically. Send Expose upon layout changes if the view has grown. diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 0cabefdf385..bb4dace655a 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -88,6 +88,9 @@ public final class EmacsView extends ViewGroup /* The last measured width and height. */ private int measuredWidth, measuredHeight; + /* Object acting as a lock for those values. */ + private Object dimensionsLock; + /* The serial of the last clip rectangle change. */ private long lastClipSerial; @@ -144,12 +147,23 @@ public final class EmacsView extends ViewGroup /* Add this view as its own global layout listener. */ getViewTreeObserver ().addOnGlobalLayoutListener (this); + + /* Create an object used as a lock. */ + this.dimensionsLock = new Object (); } private void handleDirtyBitmap () { Bitmap oldBitmap; + int measuredWidth, measuredHeight; + + synchronized (dimensionsLock) + { + /* Load measuredWidth and measuredHeight. */ + measuredWidth = this.measuredWidth; + measuredHeight = this.measuredHeight; + } if (measuredWidth == 0 || measuredHeight == 0) return; @@ -171,7 +185,7 @@ public final class EmacsView extends ViewGroup /* Save the old bitmap. */ oldBitmap = bitmap; - /* Recreate the front and back buffer bitmaps. */ + /* Recreate the back buffer bitmap. */ bitmap = Bitmap.createBitmap (measuredWidth, measuredHeight, @@ -249,8 +263,11 @@ public final class EmacsView extends ViewGroup public void prepareForLayout (int wantedWidth, int wantedHeight) { - measuredWidth = wantedWidth; - measuredHeight = wantedWidth; + synchronized (dimensionsLock) + { + measuredWidth = wantedWidth; + measuredHeight = wantedWidth; + } } @Override @@ -294,19 +311,39 @@ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST onLayout (boolean changed, int left, int top, int right, int bottom) { - int count, i; + int count, i, oldMeasuredWidth, oldMeasuredHeight; View child; Rect windowRect; + boolean needExpose; count = getChildCount (); + needExpose = false; - measuredWidth = right - left; - measuredHeight = bottom - top; + synchronized (dimensionsLock) + { + /* Load measuredWidth and measuredHeight. */ + oldMeasuredWidth = measuredWidth; + oldMeasuredHeight = measuredHeight; - /* Dirty the back buffer. */ + /* Set measuredWidth and measuredHeight. */ + measuredWidth = right - left; + measuredHeight = bottom - top; + } - if (changed) - explicitlyDirtyBitmap (); + /* Dirty the back buffer if the layout change resulted in the view + being resized. */ + + if (changed && (right - left != oldMeasuredWidth + || bottom - top != oldMeasuredHeight)) + { + explicitlyDirtyBitmap (); + + /* Expose the window upon a change in the view's size. */ + + if (right - left > oldMeasuredWidth + || bottom - top > oldMeasuredHeight) + needExpose = true; + } for (i = 0; i < count; ++i) { @@ -336,6 +373,10 @@ else if (child.getVisibility () != GONE) mustReportLayout = false; window.viewLayout (left, top, right, bottom); } + + if (needExpose) + EmacsNative.sendExpose (this.window.handle, 0, 0, + right - left, bottom - top); } public void @@ -579,9 +620,12 @@ else if (child.getVisibility () != GONE) was called. */ bitmapDirty = true; - /* Now expose the view contents again. */ - EmacsNative.sendExpose (this.window.handle, 0, 0, - measuredWidth, measuredHeight); + synchronized (dimensionsLock) + { + /* Now expose the view contents again. */ + EmacsNative.sendExpose (this.window.handle, 0, 0, + measuredWidth, measuredHeight); + } super.onAttachedToWindow (); } commit 0bdeb217fce90f17f6b67cf581f2f5f0ebf7e168 Author: Po Lu Date: Mon Jun 19 08:56:49 2023 +0800 ; * exec/Makefile.in (clean): Add `exec1'. diff --git a/exec/Makefile.in b/exec/Makefile.in index b2f134e85e5..00e59771337 100644 --- a/exec/Makefile.in +++ b/exec/Makefile.in @@ -116,7 +116,7 @@ exec1: .PHONY: clean distclean maintainer-clean extraclean bootstrap-clean clean: - rm -f *.o *.a loader test *.s.s + rm -f *.o *.a loader test exec1 *.s.s ifeq ($(AUTO_DEPEND),yes) rm -rf deps/*.d endif commit 83eeebe9ef21e7278f5da3c233171f60bdf07cee Merge: 273c65f9e90 edb0862f5e6 Author: Po Lu Date: Mon Jun 19 08:55:42 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 273c65f9e90b395e7513b802fd0938a096dec811 Author: Po Lu Date: Sun Jun 18 11:30:54 2023 +0800 Update Android port * src/window.h (GCALIGNED_STRUCT): Improve documentation of `last_mark'. * src/xdisp.c (mark_window_display_accurate_1): Don't set `last_mark' to -1 if the mark is inactive. diff --git a/src/window.h b/src/window.h index 6b151efbe60..5a620c958de 100644 --- a/src/window.h +++ b/src/window.h @@ -302,7 +302,7 @@ #define WINDOW_H_INCLUDED #endif /* Value of mark in the selected window at the time of the last - redisplay. */ + redisplay. -1 if the mark is not valid or active. */ ptrdiff_t last_mark; /* Line number and position of a line somewhere above the top of the diff --git a/src/xdisp.c b/src/xdisp.c index d0b0720b005..117df85e97e 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -17468,7 +17468,7 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p) struct buffer *b = XBUFFER (w->contents); #ifdef HAVE_TEXT_CONVERSION ptrdiff_t prev_point, prev_mark; -#endif +#endif /* HAVE_TEXT_CONVERSION */ w->last_modified = accurate_p ? BUF_MODIFF (b) : 0; w->last_overlay_modified = accurate_p ? BUF_OVERLAY_MODIFF (b) : 0; @@ -17501,14 +17501,19 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p) #ifdef HAVE_TEXT_CONVERSION prev_point = w->last_point; prev_mark = w->last_mark; -#endif +#endif /* HAVE_TEXT_CONVERSION */ if (w == XWINDOW (selected_window)) w->last_point = BUF_PT (b); else w->last_point = marker_position (w->pointm); - if (XMARKER (BVAR (b, mark))->buffer == b) + /* w->last_mark is recorded for text conversion purposes. + Input methods aren't interested in the value of the mark + if it is inactive, so set it to -1 if it's not. */ + + if (XMARKER (BVAR (b, mark))->buffer == b + && !NILP (BVAR (b, mark_active))) w->last_mark = marker_position (BVAR (b, mark)); else w->last_mark = -1; @@ -17536,7 +17541,7 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p) && FRAME_WINDOW_P (WINDOW_XFRAME (w)) && w == XWINDOW (WINDOW_XFRAME (w)->selected_window)) report_point_change (WINDOW_XFRAME (w), w, b); -#endif +#endif /* HAVE_TEXT_CONVERSION */ w->window_end_valid = true; w->update_mode_line = false; commit 3f90818be6a1169ed9f73bd28954974919422ebf Author: Po Lu Date: Sun Jun 18 10:26:13 2023 +0800 Enable text conversion in conf-modes * lisp/textmodes/conf-mode.el (conf-mode-initialize): Set text-conversion-style. diff --git a/lisp/textmodes/conf-mode.el b/lisp/textmodes/conf-mode.el index d15fba9c43a..6bf1c0d4f6f 100644 --- a/lisp/textmodes/conf-mode.el +++ b/lisp/textmodes/conf-mode.el @@ -435,6 +435,7 @@ conf-mode-initialize (setq-local comment-start comment) (setq-local comment-start-skip (concat (regexp-quote comment-start) "+\\s *")) + (setq-local text-conversion-style t) (if font-lock (setq-local font-lock-defaults `(,font-lock nil t nil nil)))) commit 8412581f08762135944eb737143df45349b20b0a Merge: bf3bea44604 1b0348d9593 Author: Po Lu Date: Sun Jun 18 08:56:54 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit bf3bea4460418b82c6de4668659447118fb2de2b Merge: 797c30b7abc b51be64a715 Author: Po Lu Date: Sat Jun 17 17:18:41 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 797c30b7abc165d5ebe65474c7398ccad0e3023c Author: Po Lu Date: Sat Jun 17 12:07:40 2023 +0800 Initialize signal mask earlier * java/org/gnu/emacs/EmacsService.java (onCreate, run): Don't initialize signal mask here. * java/org/gnu/emacs/EmacsApplication.java (onCreate): Do it here instead. * src/android.c (JNICALL): Restore previous signal masks. diff --git a/java/org/gnu/emacs/EmacsApplication.java b/java/org/gnu/emacs/EmacsApplication.java index d8b77acdf9e..8afa5bcedb4 100644 --- a/java/org/gnu/emacs/EmacsApplication.java +++ b/java/org/gnu/emacs/EmacsApplication.java @@ -78,7 +78,15 @@ public final class EmacsApplication extends Application public void onCreate () { + /* Block signals which don't interest the current thread and its + descendants created by the system. The original signal mask + will be restored for the Emacs thread in `initEmacs'. */ + EmacsNative.setupSystemThread (); + + /* Locate a suitable dump file. */ findDumpFile (this); + + /* Start the rest of the application. */ super.onCreate (); } }; diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 2fe4e8c4146..820befb52d2 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -25,7 +25,6 @@ import java.util.List; -import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicInteger; import android.graphics.Matrix; @@ -212,7 +211,6 @@ public final class EmacsService extends Service final String filesDir, libDir, cacheDir, classPath; final double pixelDensityX; final double pixelDensityY; - final Semaphore signalSemaphore; SERVICE = this; handler = new Handler (Looper.getMainLooper ()); @@ -222,7 +220,6 @@ public final class EmacsService extends Service pixelDensityX = metrics.xdpi; pixelDensityY = metrics.ydpi; resolver = getContentResolver (); - signalSemaphore = new Semaphore (0); try { @@ -251,33 +248,11 @@ invocation of app_process (through android-emacs) can cacheDir, (float) pixelDensityX, (float) pixelDensityY, classPath, EmacsService.this); - - /* Wait for the signal mask to be set up in the UI - thread. */ - - while (true) - { - try - { - signalSemaphore.acquire (); - break; - } - catch (InterruptedException e) - { - ;; - } - } } }, extraStartupArgument, /* If any file needs to be opened, open it now. */ EmacsOpenActivity.fileToOpen); thread.start (); - - /* Now that the thread has been started, block signals which - don't interest the current thread. */ - - EmacsNative.setupSystemThread (); - signalSemaphore.release (); } catch (IOException exception) { diff --git a/src/android.c b/src/android.c index ccc2da95f03..d6a56dfe0b8 100644 --- a/src/android.c +++ b/src/android.c @@ -261,6 +261,14 @@ #define ANDROID_MAX_ASSET_FD 65535 #endif /* __i386__ */ +/* Whether or not the default signal mask has been changed. If so, + the signal mask must be restored before calling + android_emacs_init. */ +static bool signal_mask_changed_p; + +/* The signal mask at the time Emacs was started. */ +static sigset_t startup_signal_mask; + /* Event handling functions. Events are stored on a (circular) queue @@ -2562,7 +2570,15 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, ANDROID_DELETE_LOCAL_REF (argv); ANDROID_DELETE_LOCAL_REF (dump_file_object); + /* Restore the signal mask at the time of startup if it was changed + to block unwanted signals from reaching system threads. */ + + if (signal_mask_changed_p) + pthread_sigmask (SIG_SETMASK, &startup_signal_mask, NULL); + + /* Now start Emacs proper. */ android_emacs_init (nelements, c_argv, dump_file); + /* android_emacs_init should never return. */ emacs_abort (); } @@ -3128,9 +3144,14 @@ NATIVE_NAME (setupSystemThread) (void) sigdelset (&sigset, SIGSEGV); sigdelset (&sigset, SIGBUS); - if (pthread_sigmask (SIG_BLOCK, &sigset, NULL)) + /* Save the signal mask that was previously used. It will be + restored in `initEmacs'. */ + + if (pthread_sigmask (SIG_BLOCK, &sigset, &startup_signal_mask)) __android_log_print (ANDROID_LOG_WARN, __func__, "pthread_sigmask: %s", strerror (errno)); + else + signal_mask_changed_p = true; } #ifdef __clang__ commit fa821ed18639aa1b0275c2938af9987c0c71b581 Author: Po Lu Date: Sat Jun 17 11:24:54 2023 +0800 ; * java/README: More documentation. diff --git a/java/README b/java/README index 96271279c28..a6adb805b9e 100644 --- a/java/README +++ b/java/README @@ -906,3 +906,144 @@ of memory. Otherwise, it applies the specified window attributes and returns the handle of the new window. } + + + +DRAWABLES, CURSORS AND HANDLES + +Each widget created by Emacs corresponds to a single ``window'', which +has its own backing store. This arrangement is quite similar to X. + +C code does not directly refer to the EmacsView widgets that implement +the UI logic behind windows. Instead, its handles refer to +EmacsWindow structures, which contain the state necessary to interact +with the widgets in an orderly and synchronized manner. + +Like X, both pixmaps and windows are drawable resources, and the same +graphics operations can be applied to both. Thus, a separate +EmacsPixmap structure is used to wrap around Android Bitmap resources, +and the Java-level graphics operation functions are capable of +operating on them both. + +Finally, graphics contexts are maintained on both the C and Java +levels; the C state recorded in `struct android_gc' is kept in sync +with the Java state in the GContext handle's corresponding EmacsGC +structure, and cursors are used through handles that refer to +EmacsCursor structures that hold system PointerIcons. + +In all cases, the interfaces provided are identical to X. + + + +EVENT LOOP + +In a typical Android application, the event loop is managed by the +operating system, and callbacks (implemented through overriding +separate functions in widgets) are run by the event loop wherever +necessary. The thread which runs the event loop is also the only +thread capable of creating and manipulating widgets and activities, +and is referred to as the ``UI thread''. + +These callbacks are used by Emacs to write representations of X-like +events to a separate event queue, which are then read from Emacs's own +event loop running in a separate thread. This is accomplished through +replacing `select' by a function which waits for the event queue to be +occupied, in addition to any file descriptors that `select' would +normally wait for. + +Conversely, Emacs's event loop sometimes needs to send events to the +UI thread. These events are implemented as tiny fragments of code, +which are run as they are received by the main thread. + +A typical example is `displayToast', which is implemented in +EmacsService.java: + + public void + displayToast (final String string) + { + runOnUiThread (new Runnable () { + @Override + public void + run () + { + Toast toast; + + toast = Toast.makeText (getApplicationContext (), + string, Toast.LENGTH_SHORT); + toast.show (); + } + }); + } + +Here, the variable `string' is used by a nested function. This nested +function contains a copy of that variable, and is run on the main +thread using the function `runOnUiThread', in order to display a short +status message on the display. + +When Emacs needs to wait for the nested function to finish, it uses a +mechanism implemented in `syncRunnable'. This mechanism first calls a +deadlock avoidance mechanism, then runs a nested function on the UI +thread, which is expected to signal itself as a condition variable +upon completion. It is typically used to allocate resources that can +only be allocated from the UI thread, or to obtain non-thread-safe +information. The following function is an example; it returns a new +EmacsView widget corresponding to the provided window: + + public EmacsView + getEmacsView (final EmacsWindow window, final int visibility, + final boolean isFocusedByDefault) + { + Runnable runnable; + final EmacsHolder view; + + view = new EmacsHolder (); + + runnable = new Runnable () { + public void + run () + { + synchronized (this) + { + view.thing = new EmacsView (window); + view.thing.setVisibility (visibility); + + /* The following function is only present on Android 26 + or later. */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + view.thing.setFocusedByDefault (isFocusedByDefault); + + notify (); + } + } + }; + + syncRunnable (runnable); + return view.thing; + } + +As no value can be directly returned from the nested function, a +separate container object is used to hold the result after the +function finishes execution. Note the type name inside the angle +brackets: this type is substituted into the class definition as it is +used; a definition such as: + +public class Foo +{ + T bar; +}; + +can not be used alone: + + Foo holder; /* Error! */ + +but must have a type specified: + + Foo holder; + +in which case the effective definition is: + +public class Foo +{ + Object bar; +}; + commit 6999067034b7e2e79326db7c2243150831f0fe99 Merge: 9ee639db23a 8657afac774 Author: Po Lu Date: Sat Jun 17 08:12:00 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 9ee639db23a8b86f5451c55eb43e424b3fc996dc Author: Po Lu Date: Fri Jun 16 15:50:56 2023 +0800 Fix quitting after changes to signal delivery * src/android.c (android_write_event, JNICALL) (android_run_in_emacs_thread): Don't rely on raise to call deliver_process_signal. diff --git a/src/android.c b/src/android.c index 873d821361c..ccc2da95f03 100644 --- a/src/android.c +++ b/src/android.c @@ -698,7 +698,7 @@ android_write_event (union android_event *event) IME. */ case ANDROID_KEY_PRESS: case ANDROID_WINDOW_ACTION: - raise (SIGIO); + kill (getpid (), SIGIO); break; default: @@ -2580,10 +2580,13 @@ NATIVE_NAME (quit) (JNIEnv *env, jobject object) { JNI_STACK_ALIGNMENT_PROLOGUE; + __android_log_print (ANDROID_LOG_VERBOSE, __func__, + "Sending SIGIO and setting Vquit_flag"); + /* Raise sigio to interrupt anything that could be reading input. */ Vquit_flag = Qt; - raise (SIGIO); + kill (getpid (), SIGIO); } JNIEXPORT jlong JNICALL @@ -3122,8 +3125,8 @@ NATIVE_NAME (setupSystemThread) (void) used by the runtime. */ sigfillset (&sigset); - sigaddset (&sigset, SIGSEGV); - sigaddset (&sigset, SIGBUS); + sigdelset (&sigset, SIGSEGV); + sigdelset (&sigset, SIGBUS); if (pthread_sigmask (SIG_BLOCK, &sigset, NULL)) __android_log_print (ANDROID_LOG_WARN, __func__, @@ -7327,7 +7330,7 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) continue processing queries as soon as possible. */ if (__atomic_load_n (&android_urgent_query, __ATOMIC_ACQUIRE)) - raise (SIGIO); + kill (getpid (), SIGIO); again: rc = sem_timedwait (&android_query_sem, &timeout); @@ -7354,7 +7357,7 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) Normally, the main thread waits for the keyboard loop to be entered before responding, in order to avoid responding with inaccurate results taken during command executioon. */ - raise (SIGIO); + kill (getpid (), SIGIO); /* Wait for the query to complete. `android_urgent_query' is only cleared by either `android_select' or commit 377a3ebbb55a9b944551394e00d24c445e3ff4a1 Author: Po Lu Date: Fri Jun 16 12:59:44 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsActivity.java (EmacsActivity): * java/org/gnu/emacs/EmacsApplication.java (findDumpFile): * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) (addSubmenu, display): * java/org/gnu/emacs/EmacsDocumentsProvider.java (getNotificationUri, queryChildDocuments, deleteDocument): * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): * java/org/gnu/emacs/EmacsFillRectangle.java (perform): * java/org/gnu/emacs/EmacsOpenActivity.java (readEmacsClientLog) (checkReadableOrCopy): * java/org/gnu/emacs/EmacsSdk7FontDriver.java (EmacsSdk7FontDriver): * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): * java/org/gnu/emacs/EmacsView.java (EmacsView): * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow, onKeyUp): * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (EmacsWindowAttachmentManager): Remove various unused arguments and variables, dead stores, and make minor cleanups and performance improvements. * src/androidmenu.c (FIND_METHOD_STATIC, android_menu_show): Adjust accordingly. diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 7ba268ba42d..fa9bff39bb0 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -55,7 +55,7 @@ public class EmacsActivity extends Activity private FrameLayout layout; /* List of activities with focus. */ - public static List focusedActivities; + public static final List focusedActivities; /* The last activity to have been focused. */ public static EmacsActivity lastFocusedActivity; diff --git a/java/org/gnu/emacs/EmacsApplication.java b/java/org/gnu/emacs/EmacsApplication.java index 10099721744..d8b77acdf9e 100644 --- a/java/org/gnu/emacs/EmacsApplication.java +++ b/java/org/gnu/emacs/EmacsApplication.java @@ -60,6 +60,9 @@ public final class EmacsApplication extends Application } }); + if (allFiles == null) + return; + /* Now try to find the right dump file. */ for (i = 0; i < allFiles.length; ++i) { diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index d69d0263b93..eb83016c849 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -131,20 +131,18 @@ private static final class Item implements MenuItem.OnMenuItemClickListener }; public List menuItems; - public String title; private EmacsContextMenu parent; /* Create a context menu with no items inside and the title TITLE, which may be NULL. */ public static EmacsContextMenu - createContextMenu (String title) + createContextMenu () { EmacsContextMenu menu; menu = new EmacsContextMenu (); menu.menuItems = new ArrayList (); - menu.title = title; return menu; } @@ -197,7 +195,7 @@ private static final class Item implements MenuItem.OnMenuItemClickListener item name. */ public EmacsContextMenu - addSubmenu (String itemName, String title, String tooltip) + addSubmenu (String itemName, String tooltip) { EmacsContextMenu submenu; Item item; @@ -206,7 +204,7 @@ private static final class Item implements MenuItem.OnMenuItemClickListener item.itemID = 0; item.itemName = itemName; item.tooltip = tooltip; - item.subMenu = createContextMenu (title); + item.subMenu = createContextMenu (); item.subMenu.parent = this; menuItems.add (item); @@ -334,6 +332,7 @@ private static final class Item implements MenuItem.OnMenuItemClickListener final EmacsHolder rc; rc = new EmacsHolder (); + rc.thing = false; runnable = new Runnable () { @Override diff --git a/java/org/gnu/emacs/EmacsDocumentsProvider.java b/java/org/gnu/emacs/EmacsDocumentsProvider.java index b4ac4624829..96dc2bc6e14 100644 --- a/java/org/gnu/emacs/EmacsDocumentsProvider.java +++ b/java/org/gnu/emacs/EmacsDocumentsProvider.java @@ -131,9 +131,7 @@ public final class EmacsDocumentsProvider extends DocumentsProvider getNotificationUri (File file) { Uri updatedUri; - Context context; - context = getContext (); updatedUri = buildChildDocumentsUri ("org.gnu.emacs", file.getAbsolutePath ()); @@ -294,6 +292,7 @@ else if (file.canWrite ()) { MatrixCursor result; File directory; + File[] files; Context context; if (projection == null) @@ -305,9 +304,15 @@ else if (file.canWrite ()) requested. */ directory = new File (parentDocumentId); - /* Now add each child. */ - for (File child : directory.listFiles ()) - queryDocument1 (result, child); + /* Look up each child. */ + files = directory.listFiles (); + + if (files != null) + { + /* Now add each child. */ + for (File child : files) + queryDocument1 (result, child); + } context = getContext (); @@ -406,12 +411,10 @@ else if (file.canWrite ()) { File file, parent; File[] children; - Context context; /* Java makes recursively deleting a file hard. File name encoding issues also prevent easily calling into C... */ - context = getContext (); file = new File (documentId); parent = file.getParentFile (); diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java index 3bd5779c54e..b54b031b56b 100644 --- a/java/org/gnu/emacs/EmacsDrawRectangle.java +++ b/java/org/gnu/emacs/EmacsDrawRectangle.java @@ -36,7 +36,6 @@ public final class EmacsDrawRectangle Paint maskPaint, paint; Canvas maskCanvas; Bitmap maskBitmap; - Rect rect; Rect maskRect, dstRect; Canvas canvas; Bitmap clipBitmap; @@ -52,7 +51,6 @@ public final class EmacsDrawRectangle paint = gc.gcPaint; paint.setStyle (Paint.Style.STROKE); - rect = new Rect (x, y, x + width, y + height); if (gc.clip_mask == null) /* Use canvas.drawRect with a RectF. That seems to reliably diff --git a/java/org/gnu/emacs/EmacsFillRectangle.java b/java/org/gnu/emacs/EmacsFillRectangle.java index 4a0478b446f..461fd3c639c 100644 --- a/java/org/gnu/emacs/EmacsFillRectangle.java +++ b/java/org/gnu/emacs/EmacsFillRectangle.java @@ -61,6 +61,7 @@ public final class EmacsFillRectangle /* Drawing with a clip mask involves calculating the intersection of the clip mask with the dst rect, and extrapolating the corresponding part of the src rect. */ + clipBitmap = gc.clip_mask.bitmap; dstRect = new Rect (x, y, x + width, y + height); maskRect = new Rect (gc.clip_x_origin, @@ -69,7 +70,6 @@ public final class EmacsFillRectangle + clipBitmap.getWidth ()), (gc.clip_y_origin + clipBitmap.getHeight ())); - clipBitmap = gc.clip_mask.bitmap; if (!maskRect.setIntersect (dstRect, maskRect)) /* There is no intersection between the clip mask and the diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index 6af2b2d2e94..9411f85d434 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -146,7 +146,7 @@ private class EmacsClientThread extends Thread FileReader reader; char[] buffer; int rc; - String what; + StringBuilder builder; /* Because the ProcessBuilder functions necessary to redirect process output are not implemented on Android 7 and earlier, @@ -160,7 +160,8 @@ private class EmacsClientThread extends Thread cache = getCacheDir (); file = new File (cache, "emacsclient.log"); - what = ""; + builder = new StringBuilder (); + reader = null; try { @@ -168,13 +169,25 @@ private class EmacsClientThread extends Thread buffer = new char[2048]; while ((rc = reader.read (buffer, 0, 2048)) != -1) - what += String.valueOf (buffer, 0, 2048); + builder.append (buffer, 0, rc); reader.close (); - return what; + return builder.toString (); } catch (IOException exception) { + /* Close the reader if it's already been opened. */ + + try + { + if (reader != null) + reader.close (); + } + catch (IOException e) + { + /* Not sure what to do here. */ + } + return ("Couldn't read emacsclient.log: " + exception.toString ()); } @@ -248,11 +261,16 @@ private class EmacsClientThread extends Thread /* inFile is now the file being written to. */ inFile = new File (getCacheDir (), inFile.getName ()); buffer = new byte[4098]; - outStream = new FileOutputStream (inFile); - stream = new FileInputStream (fd.getFileDescriptor ()); + + /* Initialize both streams to NULL. */ + outStream = null; + stream = null; try { + outStream = new FileOutputStream (inFile); + stream = new FileInputStream (fd.getFileDescriptor ()); + while ((read = stream.read (buffer)) >= 0) outStream.write (buffer, 0, read); } @@ -263,8 +281,12 @@ private class EmacsClientThread extends Thread Keep in mind that execution is transferred to ``finally'' even if an exception happens inside the while loop above. */ - stream.close (); - outStream.close (); + + if (stream != null) + stream.close (); + + if (outStream != null) + outStream.close (); } return inFile.getCanonicalPath (); diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java index 6df102f18a2..9122b46458a 100644 --- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java +++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java @@ -252,6 +252,10 @@ protected final class Sdk7FontObject extends FontObject systemFontsDirectory = new File ("/system/fonts"); fontFamilyList = systemFontsDirectory.list (); + + /* If that returned null, replace it with an empty array. */ + fontFamilyList = new String[0]; + typefaceList = new Sdk7Typeface[fontFamilyList.length + 3]; /* It would be nice to avoid opening each and every font upon diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index 738b1a99eef..5b3e05eb9f4 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -39,10 +39,6 @@ public final class EmacsSurfaceView extends View { private static final String TAG = "EmacsSurfaceView"; - /* The EmacsView representing the window that this surface is - displaying. */ - private EmacsView view; - /* The complete buffer contents at the time of the last draw. */ private Bitmap frontBuffer; @@ -71,7 +67,6 @@ public final class EmacsSurfaceView extends View { super (view.getContext ()); - this.view = view; this.bitmap = new WeakReference (null); } diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index aba1184b0c2..0cabefdf385 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -68,9 +68,6 @@ public final class EmacsView extends ViewGroup /* The damage region. */ public Region damageRegion; - /* The paint. */ - public Paint paint; - /* The associated surface view. */ private EmacsSurfaceView surfaceView; @@ -128,7 +125,6 @@ public final class EmacsView extends ViewGroup this.window = window; this.damageRegion = new Region (); - this.paint = new Paint (); setFocusable (true); setFocusableInTouchMode (true); diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 68a18ec2aa7..739a1f43b7d 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -103,11 +103,10 @@ private static class Coordinate public int lastButtonState, lastModifiers; /* Whether or not the window is mapped. */ - private boolean isMapped; + private volatile boolean isMapped; - /* Whether or not to ask for focus upon being mapped, and whether or - not the window should be focusable. */ - private boolean dontFocusOnMap, dontAcceptFocus; + /* Whether or not to ask for focus upon being mapped. */ + private boolean dontFocusOnMap; /* Whether or not the window is override-redirect. An override-redirect window always has its own system window. */ @@ -464,7 +463,7 @@ private static class Coordinate } } - public void + public synchronized void unmapWindow () { if (!isMapped) @@ -618,7 +617,7 @@ private static class Coordinate onKeyUp (int keyCode, KeyEvent event) { int state, state_1; - long time, serial; + long time; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) state = event.getModifiers (); @@ -645,12 +644,11 @@ private static class Coordinate state_1 = state & ~(KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK); - serial - = EmacsNative.sendKeyRelease (this.handle, - event.getEventTime (), - state, keyCode, - getEventUnicodeChar (event, - state_1)); + EmacsNative.sendKeyRelease (this.handle, + event.getEventTime (), + state, keyCode, + getEventUnicodeChar (event, + state_1)); lastModifiers = state; if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) @@ -1155,8 +1153,6 @@ else if (EmacsWindow.this.isMapped) public synchronized void setDontAcceptFocus (final boolean dontAcceptFocus) { - this.dontAcceptFocus = dontAcceptFocus; - /* Update the view's focus state. */ EmacsService.SERVICE.runOnUiThread (new Runnable () { @Override diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index 4fda48616f0..bc96de7fe1a 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -53,9 +53,11 @@ public final class EmacsWindowAttachmentManager { - public static EmacsWindowAttachmentManager MANAGER; private final static String TAG = "EmacsWindowAttachmentManager"; + /* The single window attachment manager ``object''. */ + public static final EmacsWindowAttachmentManager MANAGER; + static { MANAGER = new EmacsWindowAttachmentManager (); @@ -69,7 +71,10 @@ public interface WindowConsumer public void destroy (); }; + /* List of currently attached window consumers. */ public List consumers; + + /* List of currently attached windows. */ public List windows; public diff --git a/src/androidmenu.c b/src/androidmenu.c index f74e7ca6d99..75710486c75 100644 --- a/src/androidmenu.c +++ b/src/androidmenu.c @@ -101,13 +101,12 @@ #define FIND_METHOD_STATIC(c_name, name, signature) \ eassert (menu_class.c_name); FIND_METHOD_STATIC (create_context_menu, "createContextMenu", - "(Ljava/lang/String;)" - "Lorg/gnu/emacs/EmacsContextMenu;"); + "()Lorg/gnu/emacs/EmacsContextMenu;"); FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ" "Ljava/lang/String;Z)V"); FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;" - "Ljava/lang/String;Ljava/lang/String;)" + "Ljava/lang/String;)" "Lorg/gnu/emacs/EmacsContextMenu;"); FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V"); FIND_METHOD (parent, "parent", "()Lorg/gnu/emacs/EmacsContextMenu;"); @@ -271,18 +270,11 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, android_push_local_frame (); /* Push the first local frame for the context menu. */ - title_string = (!NILP (title) - ? (jobject) android_build_string (title) - : NULL); method = menu_class.create_context_menu; current_context_menu = context_menu = (*android_java_env)->CallStaticObjectMethod (android_java_env, menu_class.class, - method, - title_string); - - if (title_string) - ANDROID_DELETE_LOCAL_REF (title_string); + method); /* Push the second local frame for temporaries. */ count1 = SPECPDL_INDEX (); @@ -391,7 +383,7 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, = (*android_java_env)->CallObjectMethod (android_java_env, current_context_menu, menu_class.add_submenu, - title_string, NULL, + title_string, help_string); android_exception_check (); commit 7f0342a1bd1db835bcdbbb66c8baa97e833e45fc Merge: 363e293cc91 f2aae8b879b Author: Po Lu Date: Fri Jun 16 08:40:33 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 363e293cc919ab02c40bd9a8fa4875c2e5644b2d Author: Po Lu Date: Thu Jun 15 12:36:50 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsInputConnection.java (EmacsInputConnection, beginBatchEdit, reset, endBatchEdit): Keep track of the number of batch edits and return an appropriate value. (takeSnapshot): Implement function. * java/org/gnu/emacs/EmacsNative.java (takeSnapshot): New function. * java/org/gnu/emacs/EmacsService.java (resetIC): Improve debugging output. * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): Call `reset' to clear the UI side batch edit count. * src/androidterm.c (struct android_get_surrounding_text_context): New fields `conversion_start' and `conversion_end'. (android_get_surrounding_text): Return the conversion region. (android_get_surrounding_text_internal, NATIVE_NAME): Factor out `getSurroundingText'. (takeSnapshot): New function. diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index 1bcc9a62a81..f8dce5dfa79 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -50,6 +50,11 @@ public final class EmacsInputConnection implements InputConnection /* The handle ID associated with that view's window. */ private short windowHandle; + /* Number of batch edits currently underway. Used to avoid + synchronizing with the Emacs thread after each + `endBatchEdit'. */ + private int batchEditCount; + /* Whether or not to synchronize and call `updateIC' with the selection position after committing text. @@ -110,6 +115,10 @@ public final class EmacsInputConnection implements InputConnection Log.d (TAG, "beginBatchEdit"); EmacsNative.beginBatchEdit (windowHandle); + + /* Keep a record of the number of outstanding batch edits here as + well. */ + batchEditCount++; return true; } @@ -125,7 +134,14 @@ public final class EmacsInputConnection implements InputConnection Log.d (TAG, "endBatchEdit"); EmacsNative.endBatchEdit (windowHandle); - return true; + + /* Subtract one from the UI thread record of the number of batch + edits currently under way. */ + + if (batchEditCount > 0) + batchEditCount -= 1; + + return batchEditCount > 0; } public boolean @@ -584,21 +600,50 @@ public final class EmacsInputConnection implements InputConnection return text; } - - /* Override functions which are not implemented. */ - @Override - public Handler - getHandler () + public TextSnapshot + takeSnapshot () { - return null; + TextSnapshot snapshot; + + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return null; + + snapshot = EmacsNative.takeSnapshot (windowHandle); + + if (EmacsService.DEBUG_IC) + Log.d (TAG, ("takeSnapshot: " + + snapshot.getSurroundingText ().getText () + + " @ " + snapshot.getCompositionEnd () + + ", " + snapshot.getCompositionStart ())); + + return snapshot; } @Override public void closeConnection () { + batchEditCount = 0; + } + + + public void + reset () + { + batchEditCount = 0; + } + + + /* Override functions which are not implemented. */ + + @Override + public Handler + getHandler () + { + return null; } @Override @@ -616,13 +661,6 @@ public final class EmacsInputConnection implements InputConnection return false; } - @Override - public TextSnapshot - takeSnapshot () - { - return null; - } - @Override public boolean clearMetaKeyStates (int states) diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 2fcbf8b94ef..9e87c419f95 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -26,6 +26,7 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.SurroundingText; +import android.view.inputmethod.TextSnapshot; public final class EmacsNative { @@ -230,6 +231,7 @@ public static native ExtractedText getExtractedText (short window, public static native SurroundingText getSurroundingText (short window, int left, int right, int flags); + public static native TextSnapshot takeSnapshot (short window); /* Return the current value of the selection, or -1 upon diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 96216e51cf4..2fe4e8c4146 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -767,8 +767,31 @@ invocation of app_process (through android-emacs) can public void resetIC (EmacsWindow window, int icMode) { + int oldMode; + if (DEBUG_IC) - Log.d (TAG, "resetIC: " + window); + Log.d (TAG, "resetIC: " + window + ", " + icMode); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU + && (oldMode = window.view.getICMode ()) == icMode + /* Don't do this if there is currently no input + connection. */ + && oldMode != IC_MODE_NULL) + { + if (DEBUG_IC) + Log.d (TAG, "resetIC: calling invalidateInput"); + + /* Android 33 and later allow the IM reset to be optimized out + and replaced by a call to `invalidateInput', which is much + faster, as it does not involve resetting the input + connection. */ + + icBeginSynchronous (); + window.view.imManager.invalidateInput (window.view); + icEndSynchronous (); + + return; + } window.view.setICMode (icMode); diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 278c6025902..aba1184b0c2 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -681,6 +681,9 @@ else if (child.getVisibility () != GONE) if (inputConnection == null) inputConnection = new EmacsInputConnection (this); + else + /* Clear several pieces of state in the input connection. */ + inputConnection.reset (); /* Return the input connection. */ return inputConnection; diff --git a/src/androidterm.c b/src/androidterm.c index 191ff65199b..29076981a47 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5668,6 +5668,10 @@ NATIVE_NAME (clearInputFlags) (JNIEnv *env, jobject object, /* Offsets into that text. */ ptrdiff_t offset, start, end; + /* The start and end indices of the conversion region. + -1 if it does not exist. */ + ptrdiff_t conversion_start, conversion_end; + /* The window. */ android_window window; }; @@ -5706,22 +5710,47 @@ android_get_surrounding_text (void *data) request->start = request->end; request->end = temp; } + + /* Retrieve the conversion region. */ + + request->conversion_start = -1; + request->conversion_end = -1; + + if (MARKERP (f->conversion.compose_region_start)) + { + request->conversion_start + = marker_position (f->conversion.compose_region_start) - 1; + request->conversion_end + = marker_position (f->conversion.compose_region_end) - 1; + } } -JNIEXPORT jobject JNICALL -NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject ignored_object, - jshort window, jint before_length, - jint after_length, jint flags) -{ - JNI_STACK_ALIGNMENT_PROLOGUE; +/* Return a local reference to a `SurroundingText' object describing + WINDOW's surrounding text. ENV should be a valid JNI environment + for the current thread. - static jclass class; - static jmethodID constructor; + BEFORE_LENGTH and AFTER_LENGTH specify the number of characters + around point and mark to return. + Return the conversion region (or -1) in *CONVERSION_START and + *CONVERSION_END if non-NULL. + + Value is the object upon success, else NULL. */ + +static jobject +android_get_surrounding_text_internal (JNIEnv *env, jshort window, + jint before_length, + jint after_length, + ptrdiff_t *conversion_start, + ptrdiff_t *conversion_end) +{ struct android_get_surrounding_text_context context; jstring string; jobject object; + static jclass class; + static jmethodID constructor; + /* Initialize CLASS if it has not yet been initialized. */ if (!class) @@ -5745,7 +5774,9 @@ NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject ignored_object, class = (*env)->NewGlobalRef (env, class); if (!class) - return NULL; + /* Clear class to prevent a local reference from remaining in + `class'. */ + return (class = NULL); /* Now look for its constructor. */ constructor = (*env)->GetMethodID (env, class, "", @@ -5787,6 +5818,86 @@ NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject ignored_object, if (!object) return NULL; + /* Now return the conversion region if that was requested. */ + + if (conversion_start) + { + *conversion_start = context.conversion_start; + *conversion_end = context.conversion_start; + } + + return object; +} + +JNIEXPORT jobject JNICALL +NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject object, + jshort window, jint before_length, + jint after_length, jint flags) +{ + JNI_STACK_ALIGNMENT_PROLOGUE; + + return android_get_surrounding_text_internal (env, window, before_length, + after_length, NULL, NULL); +} + +JNIEXPORT jobject JNICALL +NATIVE_NAME (takeSnapshot) (JNIEnv *env, jobject object, jshort window) +{ + JNI_STACK_ALIGNMENT_PROLOGUE; + + jobject text; + ptrdiff_t start, end; + + static jclass class; + static jmethodID constructor; + + /* First, obtain the surrounding text and conversion region. */ + text = android_get_surrounding_text_internal (env, window, 600, 600, + &start, &end); + + /* If that fails, return NULL. */ + + if (!text) + return NULL; + + /* Next, initialize the TextSnapshot class. */ + + if (!class) + { + class + = (*env)->FindClass (env, ("android/view/inputmethod" + "/TextSnapshot")); +#if __ANDROID_API__ < 33 + /* If CLASS cannot be found, the version of Android currently + running is too old. */ + + if (!class) + { + (*env)->ExceptionClear (env); + return NULL; + } +#else /* __ANDROID_API__ >= 33 */ + assert (class); +#endif /* __ANDROID_API__ < 33 */ + + class = (*env)->NewGlobalRef (env, class); + if (!class) + /* Clear class to prevent a local reference from remaining in + `class'. */ + return (class = NULL); + + constructor = (*env)->GetMethodID (env, class, "", + "(Landroid/view/inputmethod" + "/SurroundingText;III)V"); + assert (constructor); + } + + /* Try to create a TextSnapshot object. */ + eassert (start <= end); + object = (*env)->NewObject (env, class, constructor, text, + (jint) min (start, TYPE_MAXIMUM (jint)), + (jint) min (end, TYPE_MAXIMUM (jint)), + (jint) 0); return object; } commit ca120044ac11d38ca1e8cac7903be38d5ca15d2b Merge: 90ae3cc3875 020fd630184 Author: Po Lu Date: Thu Jun 15 08:25:45 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 90ae3cc387530229e5aca32c00d35495ab680e21 Author: Po Lu Date: Wed Jun 14 15:37:47 2023 +0800 Improve IM synchronization on Android * java/org/gnu/emacs/EmacsInputConnection.java (EmacsInputConnection): Reimplement as an InputConnection, not BaseInputConnection. * src/androidterm.c (performEditorAction): Sync prior to sending keyboard events. diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index 73c93c67ac7..1bcc9a62a81 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -19,26 +19,35 @@ package org.gnu.emacs; -import android.view.inputmethod.BaseInputConnection; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; + +import android.view.KeyEvent; + import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputContentInfo; import android.view.inputmethod.SurroundingText; +import android.view.inputmethod.TextAttribute; import android.view.inputmethod.TextSnapshot; -import android.view.KeyEvent; - -import android.os.Build; - import android.util.Log; /* Android input methods, take number six. See textconv.c for more details; this is more-or-less a thin wrapper around that file. */ -public final class EmacsInputConnection extends BaseInputConnection +public final class EmacsInputConnection implements InputConnection { private static final String TAG = "EmacsInputConnection"; + + /* View associated with this input connection. */ private EmacsView view; + + /* The handle ID associated with that view's window. */ private short windowHandle; /* Whether or not to synchronize and call `updateIC' with the @@ -77,15 +86,18 @@ public final class EmacsInputConnection extends BaseInputConnection extractAbsoluteOffsets = true; }; + public EmacsInputConnection (EmacsView view) { - super (view, true); - this.view = view; this.windowHandle = view.window.handle; } + + /* The functions below are called by input methods whenever they + need to perform an edit. */ + @Override public boolean beginBatchEdit () @@ -116,7 +128,6 @@ public final class EmacsInputConnection extends BaseInputConnection return true; } - @Override public boolean commitCompletion (CompletionInfo info) { @@ -133,6 +144,19 @@ public final class EmacsInputConnection extends BaseInputConnection return true; } + @Override + public boolean + commitCorrection (CorrectionInfo info) + { + /* The input method calls this function not to commit text, but to + indicate that a subsequent edit will consist of a correction. + Emacs has no use for this information. + + Of course this completely contradicts the provided + documentation, but this is how Android actually behaves. */ + return false; + } + @Override public boolean commitText (CharSequence text, int newCursorPosition) @@ -170,6 +194,14 @@ public final class EmacsInputConnection extends BaseInputConnection return true; } + @Override + public boolean + commitText (CharSequence text, int newCursorPosition, + TextAttribute textAttribute) + { + return commitText (text, newCursorPosition); + } + @Override public boolean deleteSurroundingText (int leftLength, int rightLength) @@ -187,6 +219,16 @@ public final class EmacsInputConnection extends BaseInputConnection return true; } + @Override + public boolean + deleteSurroundingTextInCodePoints (int leftLength, int rightLength) + { + /* Emacs returns characters which cannot be represented in a Java + `char' as NULL characters, so code points always reflect + characters themselves. */ + return deleteSurroundingText (leftLength, rightLength); + } + @Override public boolean finishComposingText () @@ -277,6 +319,14 @@ public final class EmacsInputConnection extends BaseInputConnection return true; } + @Override + public boolean + setComposingText (CharSequence text, int newCursorPosition, + TextAttribute textAttribute) + { + return setComposingText (text, newCursorPosition); + } + @Override public boolean setComposingRegion (int start, int end) @@ -292,6 +342,13 @@ public final class EmacsInputConnection extends BaseInputConnection return true; } + @Override + public boolean + setComposingRegion (int start, int end, TextAttribute textAttribute) + { + return setComposingRegion (start, end); + } + @Override public boolean performEditorAction (int editorAction) @@ -430,6 +487,8 @@ public final class EmacsInputConnection extends BaseInputConnection } @Override + /* ACTION_MULTIPLE is apparently obsolete. */ + @SuppressWarnings ("deprecation") public boolean sendKeyEvent (KeyEvent key) { @@ -440,20 +499,33 @@ public final class EmacsInputConnection extends BaseInputConnection if (EmacsService.DEBUG_IC) Log.d (TAG, "sendKeyEvent: " + key); - return super.sendKeyEvent (key); - } + /* Use the standard API if possible. */ - @Override - public boolean - deleteSurroundingTextInCodePoints (int beforeLength, int afterLength) - { - /* Return if the input connection is out of date. */ - if (view.icSerial < view.icGeneration) - return false; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + view.imManager.dispatchKeyEventFromInputMethod (view, key); + else + { + /* Fall back to dispatching the event manually if not. */ + + switch (key.getAction ()) + { + case KeyEvent.ACTION_DOWN: + view.onKeyDown (key.getKeyCode (), key); + break; + + case KeyEvent.ACTION_UP: + view.onKeyUp (key.getKeyCode (), key); + break; + + case KeyEvent.ACTION_MULTIPLE: + view.onKeyMultiple (key.getKeyCode (), + key.getRepeatCount (), + key); + break; + } + } - /* This can be implemented the same way as - deleteSurroundingText. */ - return this.deleteSurroundingText (beforeLength, afterLength); + return true; } @Override @@ -471,6 +543,16 @@ public final class EmacsInputConnection extends BaseInputConnection return true; } + @Override + public boolean + requestCursorUpdates (int cursorUpdateMode, int filter) + { + if (filter != 0) + return false; + + return requestCursorUpdates (cursorUpdateMode); + } + @Override public SurroundingText getSurroundingText (int beforeLength, int afterLength, @@ -505,11 +587,74 @@ public final class EmacsInputConnection extends BaseInputConnection /* Override functions which are not implemented. */ + @Override + public Handler + getHandler () + { + return null; + } + + @Override + public void + closeConnection () + { + + } + + @Override + public boolean + commitContent (InputContentInfo inputContentInfo, int flags, + Bundle opts) + { + return false; + } + + @Override + public boolean + setImeConsumesInput (boolean imeConsumesInput) + { + return false; + } + @Override public TextSnapshot takeSnapshot () { - Log.d (TAG, "takeSnapshot"); return null; } + + @Override + public boolean + clearMetaKeyStates (int states) + { + return false; + } + + @Override + public boolean + reportFullscreenMode (boolean enabled) + { + return false; + } + + @Override + public boolean + performSpellCheck () + { + return false; + } + + @Override + public boolean + performPrivateCommand (String action, Bundle data) + { + return false; + } + + @Override + public int + getCursorCapsMode (int reqModes) + { + return 0; + } } diff --git a/src/androidterm.c b/src/androidterm.c index f08536c02ab..191ff65199b 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5193,6 +5193,13 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, union android_event event; + /* It's a good idea to call `android_sync_edit' before sending the + key event. Otherwise, if RET causes the current window to be + changed, any text previously committed might end up in the newly + selected window. */ + + android_sync_edit (); + /* Undocumented behavior: performEditorAction is apparently expected to finish composing any text. */ commit 87b8f8769e6cd4563f82747c279c858617ce1b2b Merge: 5268f8476fc 4c975111af2 Author: Po Lu Date: Wed Jun 14 08:26:57 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 5268f8476fc3e1bb2ead05a75390dbe9ef852d09 Author: Po Lu Date: Tue Jun 13 16:20:58 2023 +0800 Improve behavior of Gnus on Android * etc/NEWS: Fix typo. * lisp/gnus/gnus-score.el (gnus-read-char): New function. (gnus-summary-increase-score): Use it to display a dialog box on Android, where input methods have trouble with plain old read-char. diff --git a/etc/NEWS b/etc/NEWS index 1ed492b2e47..efe480b5be0 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -102,7 +102,6 @@ plus, minus, check-mark, start, etc. Many touch screen gestures are now implemented, as is support for tapping buttons and opening menus. - --- ** On X, Emacs now supports input methods which perform "string conversion". This means an input method can now ask Emacs to delete text diff --git a/lisp/gnus/gnus-score.el b/lisp/gnus/gnus-score.el index 05459ffae88..8bdfccf7eb8 100644 --- a/lisp/gnus/gnus-score.el +++ b/lisp/gnus/gnus-score.el @@ -517,6 +517,35 @@ gnus-summary-mode-map "t" #'gnus-score-find-trace "w" #'gnus-score-find-favorite-words)) + + +;; Touch screen ``character reading'' routines for +;; `gnus-summary-increase-score' and friends. + +(defun gnus-read-char (prompt options) + "Read a character from the keyboard. + +On Android, if `use-dialog-box-p' returns non-nil, display a +dialog box containing PROMPT, with buttons representing each of +item in the list of characters OPTIONS instead. + +Value is the character read, as with `read-char', or nil upon +failure." + (if (and (display-graphic-p) (featurep 'android) + (use-dialog-box-p)) + ;; Set up the dialog box. + (let ((dialog (cons prompt ; Message displayed in dialog box. + (mapcar (lambda (arg) + (cons (char-to-string arg) + arg)) + options)))) + ;; Display the dialog box. + (x-popup-dialog t dialog)) + ;; Fall back to read-char. + (read-char))) + + + ;; Summary score file commands ;; Much modification of the kill (ahem, score) code and lots of the @@ -588,21 +617,23 @@ gnus-summary-increase-score (aref (symbol-name gnus-score-default-type) 0))) (pchar (and gnus-score-default-duration (aref (symbol-name gnus-score-default-duration) 0))) - entry temporary type match extra) + entry temporary type match extra header-string) (unwind-protect (progn - + (setq header-string + (format "%s header (%s?): " (if increase "Increase" "Lower") + (mapconcat (lambda (s) (char-to-string (car s))) + char-to-header ""))) ;; First we read the header to score. (while (not hchar) (if mimic (progn (sit-for 1) (message "%c-" prefix)) - (message "%s header (%s?): " (if increase "Increase" "Lower") - (mapconcat (lambda (s) (char-to-string (car s))) - char-to-header ""))) - (setq hchar (read-char)) + (message header-string)) + (setq hchar (gnus-read-char header-string + (mapcar #'car char-to-header))) (when (or (= hchar ??) (= hchar ?\C-h)) (setq hchar nil) (gnus-score-insert-help "Match on header" char-to-header 1))) @@ -625,17 +656,20 @@ gnus-summary-increase-score (nth 3 s)) s nil)) char-to-type)))) + (setq header-string + (format "%s header `%s' with match type (%s?): " + (if increase "Increase" "Lower") + (nth 1 entry) + (mapconcat (lambda (s) (char-to-string (car s))) + legal-types ""))) ;; We continue reading - the type. (while (not tchar) (if mimic (progn (sit-for 1) (message "%c %c-" prefix hchar)) - (message "%s header `%s' with match type (%s?): " - (if increase "Increase" "Lower") - (nth 1 entry) - (mapconcat (lambda (s) (char-to-string (car s))) - legal-types ""))) - (setq tchar (read-char)) + (message header-string)) + (setq tchar (gnus-read-char header-string + (mapcar #'car legal-types))) (when (or (= tchar ??) (= tchar ?\C-h)) (setq tchar nil) (gnus-score-insert-help "Match type" legal-types 2))) @@ -651,15 +685,19 @@ gnus-summary-increase-score (message "")) (setq pchar (or pchar ?t))) + (setq header-string + (format "%s permanence (%s?): " (if increase "Increase" "Lower") + (mapconcat (lambda (s) (char-to-string (car s))) + char-to-perm ""))) + ;; We continue reading. (while (not pchar) (if mimic (progn (sit-for 1) (message "%c %c %c-" prefix hchar tchar)) - (message "%s permanence (%s?): " (if increase "Increase" "Lower") - (mapconcat (lambda (s) (char-to-string (car s))) - char-to-perm ""))) - (setq pchar (read-char)) + (message header-string)) + (setq pchar (gnus-read-char header-string + (mapcar #'car char-to-perm))) (when (or (= pchar ??) (= pchar ?\C-h)) (setq pchar nil) (gnus-score-insert-help "Match permanence" char-to-perm 2))) commit 32c627a5ac314ff0ca97ac99d2357f84565f2581 Merge: 1cd514c297f 410aac81832 Author: Po Lu Date: Tue Jun 13 08:35:32 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 1cd514c297fb570b35b257ee9880b729e35a1068 Author: Po Lu Date: Mon Jun 12 20:09:09 2023 +0800 Improve appearance of custom dialog buttons on Android * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Resolve dialog button style and use it instead. diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java index 3f8fe0109c0..5f48a9a5f9f 100644 --- a/java/org/gnu/emacs/EmacsDialog.java +++ b/java/org/gnu/emacs/EmacsDialog.java @@ -27,6 +27,10 @@ import android.content.Context; import android.content.DialogInterface; +import android.content.res.Resources.NotFoundException; +import android.content.res.Resources.Theme; +import android.content.res.TypedArray; + import android.os.Build; import android.provider.Settings; @@ -148,13 +152,17 @@ private final class EmacsButton implements View.OnClickListener, toAlertDialog (Context context) { AlertDialog dialog; - int size; + int size, styleId; + int[] attrs; EmacsButton button; EmacsDialogButtonLayout layout; Button buttonView; ViewGroup.LayoutParams layoutParams; + Theme theme; + TypedArray attributes; size = buttons.size (); + styleId = -1; if (size <= 3) { @@ -193,19 +201,10 @@ private final class EmacsButton implements View.OnClickListener, else { /* There are more than 3 buttons. Add them all to a special - container widget that handles wrapping. */ + container widget that handles wrapping. First, create the + layout. */ layout = new EmacsDialogButtonLayout (context); - - for (EmacsButton emacsButton : buttons) - { - buttonView = new Button (context); - buttonView.setText (emacsButton.name); - buttonView.setOnClickListener (emacsButton); - buttonView.setEnabled (emacsButton.enabled); - layout.addView (buttonView); - } - layoutParams = new FrameLayout.LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); @@ -223,6 +222,55 @@ private final class EmacsButton implements View.OnClickListener, if (title != null) dialog.setTitle (title); + + /* Now that the dialog has been created, set the style of each + custom button to match the usual dialog buttons found on + Android 5 and later. */ + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + { + /* Obtain the Theme associated with the dialog. */ + theme = dialog.getContext ().getTheme (); + + /* Resolve the dialog button style. */ + attrs + = new int [] { android.R.attr.buttonBarNeutralButtonStyle, }; + + try + { + attributes = theme.obtainStyledAttributes (attrs); + + /* Look for the style ID. Default to -1 if it could + not be found. */ + styleId = attributes.getResourceId (0, -1); + + /* Now clean up the TypedAttributes object. */ + attributes.recycle (); + } + catch (NotFoundException e) + { + /* Nothing to do here. */ + } + } + + /* Create each button and add it to the layout. Set the style + if necessary. */ + + for (EmacsButton emacsButton : buttons) + { + if (styleId == -1) + /* No specific style... */ + buttonView = new Button (context); + else + /* Use the given styleId. */ + buttonView = new Button (context, null, 0, styleId); + + /* Set the text and on click handler. */ + buttonView.setText (emacsButton.name); + buttonView.setOnClickListener (emacsButton); + buttonView.setEnabled (emacsButton.enabled); + layout.addView (buttonView); + } } return dialog; commit 3b08bb1318cd0bf6bc1811b520f9c6934b1aa3bd Author: Po Lu Date: Mon Jun 12 14:19:01 2023 +0800 Fix deadlocks * java/org/gnu/emacs/EmacsView.java (EmacsView) (showOnScreenKeyboard, hideOnScreenKeyboard): Don't synchronize. * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow) (toggleOnScreenKeyboard): Revert to calling IMM functions from the main thread. * src/android.c (struct android_event_container) (android_pselect_nfds, android_pselect_readfds) (android_pselect_writefds, android_pselect_exceptfds) (android_pselect_timeout): Don't make volatile. (android_wait_event): Run queries if necessary. diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 7fd672233f2..278c6025902 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -590,16 +590,17 @@ else if (child.getVisibility () != GONE) super.onAttachedToWindow (); } - public synchronized void + public void showOnScreenKeyboard () { /* Specifying no flags at all tells the system the user asked for the input method to be displayed. */ + imManager.showSoftInput (this, 0); isCurrentlyTextEditor = true; } - public synchronized void + public void hideOnScreenKeyboard () { imManager.hideSoftInputFromWindow (this.getWindowToken (), diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index f5e40e9a2d9..68a18ec2aa7 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -1201,16 +1201,25 @@ else if (EmacsWindow.this.isMapped) } public void - toggleOnScreenKeyboard (boolean on) + toggleOnScreenKeyboard (final boolean on) { - /* InputMethodManager functions are thread safe. Call - `showOnScreenKeyboard' etc from the Emacs thread in order to - keep the calls in sync with updates to the input context. */ + /* Even though InputMethodManager functions are thread safe, + `showOnScreenKeyboard' etc must be called from the UI thread in + order to avoid deadlocks if the calls happen in tandem with a + call to a synchronizing function within + `onCreateInputConnection'. */ - if (on) - view.showOnScreenKeyboard (); - else - view.hideOnScreenKeyboard (); + EmacsService.SERVICE.runOnUiThread (new Runnable () { + @Override + public void + run () + { + if (on) + view.showOnScreenKeyboard (); + else + view.hideOnScreenKeyboard (); + } + }); } public String diff --git a/src/android.c b/src/android.c index 4414d465107..873d821361c 100644 --- a/src/android.c +++ b/src/android.c @@ -272,7 +272,7 @@ #define ANDROID_MAX_ASSET_FD 65535 struct android_event_container { /* The next and last events in this queue. */ - struct android_event_container *volatile next, *last; + struct android_event_container *next, *last; /* The event itself. */ union android_event event; @@ -301,11 +301,11 @@ #define ANDROID_MAX_ASSET_FD 65535 }; /* Arguments to pselect used by the select thread. */ -static volatile int android_pselect_nfds; -static fd_set *volatile android_pselect_readfds; -static fd_set *volatile android_pselect_writefds; -static fd_set *volatile android_pselect_exceptfds; -static struct timespec *volatile android_pselect_timeout; +static int android_pselect_nfds; +static fd_set *android_pselect_readfds; +static fd_set *android_pselect_writefds; +static fd_set *android_pselect_exceptfds; +static struct timespec *android_pselect_timeout; /* Value of pselect. */ static int android_pselect_rc; @@ -569,12 +569,20 @@ android_pending (void) return i; } +/* Forward declaration. */ + +static void android_check_query (void); + /* Wait for events to become available synchronously. Return once an - event arrives. */ + event arrives. Also, reply to the UI thread whenever it requires a + response. */ void android_wait_event (void) { + /* Run queries from the UI thread to the Emacs thread. */ + android_check_query (); + pthread_mutex_lock (&event_queue.mutex); /* Wait for events to appear if there are none available to @@ -584,6 +592,10 @@ android_wait_event (void) &event_queue.mutex); pthread_mutex_unlock (&event_queue.mutex); + + /* Check for queries again. If a query is sent after the call to + `android_check_query' above, `read_var' will be signaled. */ + android_check_query (); } void @@ -701,10 +713,6 @@ android_write_event (union android_event *event) should answer the query ASAP. */ static bool android_urgent_query; -/* Forward declaration. */ - -static void android_check_query (void); - int android_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timespec *timeout) commit e3196835ed08a1d1a675b933a53d1a397defd561 Merge: aad78abfd29 b6d48190609 Author: Po Lu Date: Mon Jun 12 08:07:48 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit aad78abfd2944298715c2954614db509eec1c6b3 Author: Po Lu Date: Sun Jun 11 17:58:47 2023 +0800 Update Android port * lisp/net/tramp.el (tramp-encoding-shell): * lisp/obsolete/terminal.el (terminal-emulator): * lisp/term.el (term-exec-1): * lisp/textmodes/artist.el (artist-figlet-get-font-list): * src/android.c (JNICALL): Where /bin/sh was previously used, use /system/bin/sh on Android. diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 8c26f533bb8..7849f81aebe 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -148,7 +148,12 @@ tramp-auto-save-directory ;; Suppress `shell-file-name' for w32 systems. (defcustom tramp-encoding-shell (let (shell-file-name) - (or (tramp-compat-funcall 'w32-shell-name) "/bin/sh")) + (or (tramp-compat-funcall 'w32-shell-name) + (if (eq system-type 'android) + ;; The shell is located at /system/bin/sh on Android + ;; systems. + "/system/bin/sh" + "/bin/sh"))) "Use this program for encoding and decoding commands on the local host. This shell is used to execute the encoding and decoding command on the local host, so if you want to use \"~\" in those commands, you should diff --git a/lisp/obsolete/terminal.el b/lisp/obsolete/terminal.el index 4a2ef680b09..4e23fc3c710 100644 --- a/lisp/obsolete/terminal.el +++ b/lisp/obsolete/terminal.el @@ -1095,7 +1095,9 @@ terminal-emulator (or explicit-shell-file-name (getenv "ESHELL") (getenv "SHELL") - "/bin/sh")) + (if (eq system-type 'android) + "/system/bin/sh" + "/bin/sh"))) (s (read-string (format "Run program in emulator (default %s): " default-s)))) diff --git a/lisp/term.el b/lisp/term.el index 8fad9705c98..631d22c6006 100644 --- a/lisp/term.el +++ b/lisp/term.el @@ -1726,7 +1726,12 @@ term-exec-1 (push (format "EMACS=%s (term:%s)" emacs-version term-protocol-version) process-environment)) (apply #'start-process name buffer - "/bin/sh" "-c" + ;; On Android, /bin doesn't exist, and the default shell is + ;; found as /system/bin/sh. + (if (eq system-type 'android) + "/system/bin/sh" + "/bin/sh") + "-c" (format "stty -nl echo rows %d columns %d sane 2>%s;\ if [ $1 = .. ]; then shift; fi; exec \"$@\"" term-height term-width null-device) diff --git a/lisp/textmodes/artist.el b/lisp/textmodes/artist.el index e0f311552d0..08613db600c 100644 --- a/lisp/textmodes/artist.el +++ b/lisp/textmodes/artist.el @@ -2800,7 +2800,9 @@ artist-figlet-run (defun artist-figlet-get-font-list () "Read fonts in with the shell command. Returns a list of strings." - (let* ((cmd-interpreter "/bin/sh") + (let* ((cmd-interpreter (if (eq system-type 'android) + "/system/bin/sh" + "/bin/sh")) (ls-cmd artist-figlet-list-fonts-command) (result (artist-system cmd-interpreter ls-cmd nil)) (exit-code (elt result 0)) diff --git a/src/android.c b/src/android.c index b88d072e303..4414d465107 100644 --- a/src/android.c +++ b/src/android.c @@ -2518,6 +2518,10 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, /* Set TMPDIR to the temporary files directory. */ setenv ("TMPDIR", android_cache_dir, 1); + /* And finally set "SHELL" to /system/bin/sh. Otherwise, some + programs will look for /bin/sh, which is problematic. */ + setenv ("SHELL", "/system/bin/sh", 1); + /* Set the cwd to that directory as well. */ if (chdir (android_files_dir)) __android_log_print (ANDROID_LOG_WARN, __func__, commit 24f25fc2f8823b1999fa66e4b21601ee4000f321 Author: Po Lu Date: Sun Jun 11 14:35:13 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): Document member variables. (onDraw): Use separate Paint object on the UI thread. * src/textconv.c (really_commit_text, really_set_composing_text) (really_delete_surrounding_text): Run modification hooks when deleting text. diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index 0deb930c2c2..738b1a99eef 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -38,19 +38,40 @@ own back buffers, which use too much memory (up to 96 MB for a public final class EmacsSurfaceView extends View { private static final String TAG = "EmacsSurfaceView"; + + /* The EmacsView representing the window that this surface is + displaying. */ private EmacsView view; + + /* The complete buffer contents at the time of the last draw. */ private Bitmap frontBuffer; + + /* Canvas representing the front buffer. */ private Canvas bitmapCanvas; + + /* Reference to the last bitmap copied to the front buffer. */ private WeakReference bitmap; - private Paint bitmapPaint; + + /* Paint objects used on the main and UI threads, respectively. */ + private static final Paint bitmapPaint, uiThreadPaint; + + static + { + /* Create two different Paint objects; one is used on the main + thread for buffer swaps, while the other is used from the UI + thread in `onDraw'. This is necessary because Paint objects + are not thread-safe, even if their uses are interlocked. */ + + bitmapPaint = new Paint (); + uiThreadPaint = new Paint (); + }; public - EmacsSurfaceView (final EmacsView view) + EmacsSurfaceView (EmacsView view) { super (view.getContext ()); this.view = view; - this.bitmapPaint = new Paint (); this.bitmap = new WeakReference (null); } @@ -161,6 +182,6 @@ else if (bitmap != null) now. */ if (frontBuffer != null) - canvas.drawBitmap (frontBuffer, 0f, 0f, bitmapPaint); + canvas.drawBitmap (frontBuffer, 0f, 0f, uiThreadPaint); } }; diff --git a/src/textconv.c b/src/textconv.c index d86877b5515..6718568ac98 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -617,7 +617,7 @@ really_commit_text (struct frame *f, EMACS_INT position, /* Now delete whatever needs to go. */ - del_range (start, end); + del_range_1 (start, end, true, false); record_buffer_change (start, start, Qt); /* Don't record changes if TEXT is empty. */ @@ -821,7 +821,7 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, if (end != start) { - del_range (start, end); + del_range_1 (start, end, true, false); set_point (start); record_buffer_change (start, start, Qt); } @@ -841,7 +841,7 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, its end. */ start = marker_position (f->conversion.compose_region_start); end = marker_position (f->conversion.compose_region_end); - del_range (start, end); + del_range_1 (start, end, true, false); set_point (start); if (start != end) @@ -1041,7 +1041,7 @@ really_delete_surrounding_text (struct frame *f, ptrdiff_t left, start = max (BEGV, lstart - left); end = min (ZV, rstart + right); - text = del_range_1 (start, end, false, true); + text = del_range_1 (start, end, true, true); record_buffer_change (start, start, text); } else @@ -1051,14 +1051,14 @@ really_delete_surrounding_text (struct frame *f, ptrdiff_t left, start = rstart; end = min (ZV, rstart + right); - text = del_range_1 (start, end, false, true); + text = del_range_1 (start, end, true, true); record_buffer_change (start, start, Qnil); /* Now delete what must be deleted on the left. */ start = max (BEGV, lstart - left); end = lstart; - text = del_range_1 (start, end, false, true); + text = del_range_1 (start, end, true, true); record_buffer_change (start, start, text); } commit 5abc977bbb2d38f3c607f1575e02aa7a6c483db0 Author: Po Lu Date: Sun Jun 11 08:52:10 2023 +0800 Avoid extraneous calls to the UI thread * java/org/gnu/emacs/EmacsView.java (EmacsView) (showOnScreenKeyboard, hideOnScreenKeyboard) (onCheckIsTextEditor): Make synchronized. * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow) (toggleOnScreenKeyboard): Don't post to the main thread. diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index d432162132d..7fd672233f2 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -590,7 +590,7 @@ else if (child.getVisibility () != GONE) super.onAttachedToWindow (); } - public void + public synchronized void showOnScreenKeyboard () { /* Specifying no flags at all tells the system the user asked for @@ -599,7 +599,7 @@ else if (child.getVisibility () != GONE) isCurrentlyTextEditor = true; } - public void + public synchronized void hideOnScreenKeyboard () { imManager.hideSoftInputFromWindow (this.getWindowToken (), @@ -686,7 +686,7 @@ else if (child.getVisibility () != GONE) } @Override - public boolean + public synchronized boolean onCheckIsTextEditor () { /* If value is true, then the system will display the on screen diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index c14bf16b96e..f5e40e9a2d9 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -1201,19 +1201,16 @@ else if (EmacsWindow.this.isMapped) } public void - toggleOnScreenKeyboard (final boolean on) + toggleOnScreenKeyboard (boolean on) { - EmacsService.SERVICE.runOnUiThread (new Runnable () { - @Override - public void - run () - { - if (on) - view.showOnScreenKeyboard (); - else - view.hideOnScreenKeyboard (); - } - }); + /* InputMethodManager functions are thread safe. Call + `showOnScreenKeyboard' etc from the Emacs thread in order to + keep the calls in sync with updates to the input context. */ + + if (on) + view.showOnScreenKeyboard (); + else + view.hideOnScreenKeyboard (); } public String commit f9cede52faf790dcf925e409e7a9588509f076a2 Merge: 3fcaad585ad acfd261a61a Author: Po Lu Date: Sun Jun 11 08:16:42 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 3fcaad585ad4cb071401f3d1a9890547d24a966c Author: Po Lu Date: Sat Jun 10 16:32:00 2023 +0800 ; Update Android port * src/keyboard.c (handle_input_available_signal): Don't generate instructions not available in arm mode. diff --git a/src/keyboard.c b/src/keyboard.c index eea37fa833f..169756d9f2f 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -7990,11 +7990,11 @@ handle_input_available_signal (int sig) /* Make all writes from the Android UI thread visible. If `android_urgent_query' has been set, preceding writes to query related variables should become observable here on as well. */ -#if defined __aarch64__ || defined __arm__ +#if defined __aarch64__ asm ("dmb ishst"); -#else /* defined __aarch64__ || defined __arm__ */ +#else /* !defined __aarch64__ */ __atomic_thread_fence (__ATOMIC_SEQ_CST); -#endif /* !defined __aarch64__ && !defined __arm__ */ +#endif /* defined __aarch64__ */ #endif /* HAVE_ANDROID && !ANDROID_STUBIFY */ pending_signals = true; commit 41acfaea1c4c947e26031917bf21c98ec047b32c Author: Po Lu Date: Sat Jun 10 16:10:46 2023 +0800 Update Android port * src/android.c (android_select, android_check_query) (android_check_query_urgent, android_answer_query) (android_answer_query_spin, android_begin_query, android_end_query) (android_run_in_emacs_thread): Use finer grained memory synchronization semantics. * src/androidterm.c (android_get_selection): Use the current selection, not its value at the time of the last redisplay. * src/keyboard.c (handle_input_available_signal): Place memory barrier. diff --git a/src/android.c b/src/android.c index cfc777c3caa..b88d072e303 100644 --- a/src/android.c +++ b/src/android.c @@ -717,7 +717,7 @@ android_select (int nfds, fd_set *readfds, fd_set *writefds, /* Since Emacs is reading keyboard input again, signify that queries from input methods are no longer ``urgent''. */ - __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST); + __atomic_clear (&android_urgent_query, __ATOMIC_RELEASE); /* Check for and run anything the UI thread wants to run on the main thread. */ @@ -7072,13 +7072,22 @@ android_display_toast (const char *text) the UI thread, but is not possible the other way around. To avoid such deadlocks, an atomic counter is provided. This - counter is incremented every time a query starts, and is set to - zerp every time one ends. If the UI thread tries to make a query - and sees that the counter is non-zero, it simply returns so that - its event loop can proceed to perform and respond to the query. If - the Emacs thread sees the same thing, then it stops to service all - queries being made by the input method, then proceeds to make its - query. */ + counter is set to two every time a query starts from the main + thread, and is set to zero every time one ends. If the UI thread + tries to make a query and sees that the counter is two, it simply + returns so that its event loop can proceed to perform and respond + to the query. If the Emacs thread sees that the counter is one, + then it stops to service all queries being made by the input + method, then proceeds to make its query with the counter set to + 2. + + The memory synchronization is simple: all writes to + `android_query_context' and `android_query_function' are depended + on by writes to the atomic counter. Loads of the new value from + the counter are then guaranteed to make those writes visible. The + separate flag `android_urgent_query' does not depend on anything + itself; however, the input signal handler executes a memory fence + to ensure that all query related writes become visible. */ /* Run any function that the UI thread has asked to run, and then signal its completion. */ @@ -7089,12 +7098,12 @@ android_check_query (void) void (*proc) (void *); void *closure; - if (!__atomic_load_n (&android_servicing_query, __ATOMIC_SEQ_CST)) + if (!__atomic_load_n (&android_servicing_query, __ATOMIC_ACQUIRE)) return; /* First, load the procedure and closure. */ - __atomic_load (&android_query_context, &closure, __ATOMIC_SEQ_CST); - __atomic_load (&android_query_function, &proc, __ATOMIC_SEQ_CST); + closure = android_query_context; + proc = android_query_function; if (!proc) return; @@ -7102,10 +7111,10 @@ android_check_query (void) proc (closure); /* Finish the query. */ - __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST); - __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST); - __atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST); - __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST); + android_query_context = NULL; + android_query_function = NULL; + __atomic_store_n (&android_servicing_query, 0, __ATOMIC_RELEASE); + __atomic_clear (&android_urgent_query, __ATOMIC_RELEASE); /* Signal completion. */ sem_post (&android_query_sem); @@ -7124,18 +7133,18 @@ android_check_query_urgent (void) void (*proc) (void *); void *closure; - if (!__atomic_load_n (&android_urgent_query, __ATOMIC_SEQ_CST)) + if (!__atomic_load_n (&android_urgent_query, __ATOMIC_ACQUIRE)) return; __android_log_print (ANDROID_LOG_VERBOSE, __func__, "Responding to urgent query..."); - if (!__atomic_load_n (&android_servicing_query, __ATOMIC_SEQ_CST)) + if (!__atomic_load_n (&android_servicing_query, __ATOMIC_ACQUIRE)) return; /* First, load the procedure and closure. */ - __atomic_load (&android_query_context, &closure, __ATOMIC_SEQ_CST); - __atomic_load (&android_query_function, &proc, __ATOMIC_SEQ_CST); + closure = android_query_context; + proc = android_query_function; if (!proc) return; @@ -7145,9 +7154,9 @@ android_check_query_urgent (void) /* Finish the query. Don't clear `android_urgent_query'; instead, do that the next time Emacs enters the keyboard loop. */ - __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST); - __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST); - __atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST); + android_query_context = NULL; + android_query_function = NULL; + __atomic_store_n (&android_servicing_query, 0, __ATOMIC_RELEASE); /* Signal completion. */ sem_post (&android_query_sem); @@ -7163,12 +7172,13 @@ android_answer_query (void) void (*proc) (void *); void *closure; - eassert (__atomic_load_n (&android_servicing_query, __ATOMIC_SEQ_CST) + eassert (__atomic_load_n (&android_servicing_query, + __ATOMIC_ACQUIRE) == 1); /* First, load the procedure and closure. */ - __atomic_load (&android_query_context, &closure, __ATOMIC_SEQ_CST); - __atomic_load (&android_query_function, &proc, __ATOMIC_SEQ_CST); + closure = android_query_context; + proc = android_query_function; if (!proc) return; @@ -7176,9 +7186,9 @@ android_answer_query (void) proc (closure); /* Finish the query. */ - __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST); - __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST); - __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST); + android_query_context = NULL; + android_query_function = NULL; + __atomic_clear (&android_urgent_query, __ATOMIC_RELEASE); /* Signal completion. */ sem_post (&android_query_sem); @@ -7193,7 +7203,7 @@ android_answer_query_spin (void) int n; while (!(n = __atomic_load_n (&android_servicing_query, - __ATOMIC_SEQ_CST))) + __ATOMIC_ACQUIRE))) eassert (!n); /* Note that this function is supposed to be called before @@ -7212,11 +7222,11 @@ android_begin_query (void) { char old; - /* Load the previous value of `android_servicing_query' and upgrade + /* Load the previous value of `android_servicing_query' and then set it to 2. */ old = __atomic_exchange_n (&android_servicing_query, - 2, __ATOMIC_SEQ_CST); + 2, __ATOMIC_ACQ_REL); /* See if a query was previously in progress. */ if (old == 1) @@ -7235,8 +7245,8 @@ android_begin_query (void) static void android_end_query (void) { - __atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST); - __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST); + __atomic_store_n (&android_servicing_query, 0, __ATOMIC_RELEASE); + __atomic_clear (&android_urgent_query, __ATOMIC_RELEASE); } /* Synchronously ask the Emacs thread to run the specified PROC with @@ -7264,8 +7274,8 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) event.xaction.action = 0; /* Set android_query_function and android_query_context. */ - __atomic_store_n (&android_query_context, closure, __ATOMIC_SEQ_CST); - __atomic_store_n (&android_query_function, proc, __ATOMIC_SEQ_CST); + android_query_context = closure; + android_query_function = proc; /* Don't allow deadlocks to happen; make sure the Emacs thread is not waiting for something to be done (in that case, @@ -7273,13 +7283,15 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) old = 0; if (!__atomic_compare_exchange_n (&android_servicing_query, &old, - 1, false, __ATOMIC_SEQ_CST, - __ATOMIC_SEQ_CST)) + 1, false, __ATOMIC_ACQ_REL, + __ATOMIC_ACQUIRE)) { - __atomic_store_n (&android_query_context, NULL, - __ATOMIC_SEQ_CST); - __atomic_store_n (&android_query_function, NULL, - __ATOMIC_SEQ_CST); + android_query_context = NULL; + android_query_function = NULL; + + /* The two variables above may still be non-NULL from the POV of + the main thread, as no happens-before constraint is placed on + those stores wrt a future load from `android_servicing_query'. */ return 1; } @@ -7302,7 +7314,7 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) keyboard loop in between. When that happens, raise SIGIO to continue processing queries as soon as possible. */ - if (__atomic_load_n (&android_urgent_query, __ATOMIC_SEQ_CST)) + if (__atomic_load_n (&android_urgent_query, __ATOMIC_ACQUIRE)) raise (SIGIO); again: @@ -7321,7 +7333,8 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) /* The query timed out. At this point, set `android_urgent_query' to true. */ - __atomic_store_n (&android_urgent_query, true, __ATOMIC_SEQ_CST); + __atomic_store_n (&android_urgent_query, true, + __ATOMIC_RELEASE); /* And raise SIGIO. Now that the query is considered urgent, the main thread will reply while reading async input. @@ -7331,7 +7344,10 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) inaccurate results taken during command executioon. */ raise (SIGIO); - /* Wait for the query to complete. */ + /* Wait for the query to complete. `android_urgent_query' is + only cleared by either `android_select' or + `android_check_query', so there's no need to worry about the + flag being cleared before the query is processed. */ while (sem_wait (&android_query_sem) < 0) ;; } @@ -7341,9 +7357,9 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) query. */ eassert (!__atomic_load_n (&android_servicing_query, - __ATOMIC_SEQ_CST) + __ATOMIC_ACQUIRE) || (__atomic_load_n (&android_servicing_query, - __ATOMIC_SEQ_CST) == 2)); + __ATOMIC_ACQUIRE) == 2)); return 0; } diff --git a/src/androidterm.c b/src/androidterm.c index 704ff5f5d85..f08536c02ab 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5128,18 +5128,22 @@ android_get_selection (void *data) { w = XWINDOW (f->selected_window); - /* Return W's point at the time of the last redisplay. This is - rather important to keep the input method consistent with the - contents of the display. */ - context->point = w->ephemeral_last_point; + /* Return W's point as it is now. Then, set + W->ephemeral_last_point to match the current point. */ + context->point = window_point (w); + w->ephemeral_last_point = context->point; /* Default context->mark to w->last_point too. */ context->mark = context->point; - /* If the mark is active, then set it properly. */ + /* If the mark is active, then set it properly. Also, adjust + w->last_mark to match. */ b = XBUFFER (w->contents); - if (!NILP (BVAR (b, mark_active)) && w->last_mark != -1) - context->mark = w->last_mark; + if (!NILP (BVAR (b, mark_active))) + { + context->mark = marker_position (BVAR (b, mark)); + w->last_mark = context->mark; + } } } diff --git a/src/keyboard.c b/src/keyboard.c index 523718cdbaa..eea37fa833f 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -7986,6 +7986,16 @@ totally_unblock_input (void) void handle_input_available_signal (int sig) { +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + /* Make all writes from the Android UI thread visible. If + `android_urgent_query' has been set, preceding writes to query + related variables should become observable here on as well. */ +#if defined __aarch64__ || defined __arm__ + asm ("dmb ishst"); +#else /* defined __aarch64__ || defined __arm__ */ + __atomic_thread_fence (__ATOMIC_SEQ_CST); +#endif /* !defined __aarch64__ && !defined __arm__ */ +#endif /* HAVE_ANDROID && !ANDROID_STUBIFY */ pending_signals = true; if (input_available_clear_time) commit a0b8f60ce79f93f37242a20d7b9a3e3b7ddc46a1 Author: Po Lu Date: Sat Jun 10 09:57:41 2023 +0800 Inherit surrounding text properties when inserting conversion text * src/textconv.c (really_commit_text) (really_set_composing_text): Improve behavior of certain fontification mechanisms by inheriting text properties from surrounding text. diff --git a/src/textconv.c b/src/textconv.c index 3303ca246de..d86877b5515 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -624,7 +624,13 @@ really_commit_text (struct frame *f, EMACS_INT position, if (SCHARS (text)) { - Finsert (1, &text); + /* Insert the new text. Make sure to inherit text + properties from the surroundings: if this doesn't happen, + CC Mode fontification can get thrown off and become very + slow. */ + + insert_from_string (text, 0, 0, SCHARS (text), + SBYTES (text), true); record_buffer_change (start, PT, text); } @@ -686,7 +692,14 @@ really_commit_text (struct frame *f, EMACS_INT position, if (SCHARS (text)) { - Finsert (1, &text); + /* Insert the new text. Make sure to inherit text + properties from the surroundings: if this doesn't happen, + CC Mode fontification can get thrown off and become very + slow. */ + + insert_from_string (text, 0, 0, SCHARS (text), + SBYTES (text), true); + record_buffer_change (wanted, PT, text); } @@ -835,8 +848,12 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, record_buffer_change (start, start, Qt); } - /* Insert the new text. */ - Finsert (1, &text); + /* Insert the new text. Make sure to inherit text properties from + the surroundings: if this doesn't happen, CC Mode fontification + can get thrown off and become very slow. */ + + insert_from_string (text, 0, 0, SCHARS (text), + SBYTES (text), true); if (start != PT) record_buffer_change (start, PT, Qt); commit b36dc0815ba753bc98b1e0b0fd21bb50ba90d8ae Merge: 674373bed86 3c01060c023 Author: Po Lu Date: Sat Jun 10 09:22:53 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 674373bed8632093ab8ed118618b05e085ffd5cd Author: Po Lu Date: Sat Jun 10 09:21:48 2023 +0800 Prevent hangs from IM requests with the main thread busy * src/android.c (android_select): Clear `android_urgent_query'. (android_check_query): Make static. Clear `android_urgent_query'. (android_check_query_urgent): New function; work like `android_check_query', but only answer urgent queries. (android_answer_query, android_end_query): Clear urgent query flag. (android_run_in_emacs_thread): Initially wait two seconds for the query to run from the keyboard loop; upon a timeout, set `android_urgent_query' to true and wait for it to run while reading async input. * src/android.h: Update prototypes. * src/keyboard.c (handle_async_input): Call `android_check_query_urgent'. diff --git a/src/android.c b/src/android.c index f45e0abbea6..cfc777c3caa 100644 --- a/src/android.c +++ b/src/android.c @@ -29,6 +29,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include #include +#include #include #include @@ -693,6 +694,17 @@ android_write_event (union android_event *event) } } + + +/* Whether or not the UI thread has been waiting for a significant + amount of time for a function to run in the main thread, and Emacs + should answer the query ASAP. */ +static bool android_urgent_query; + +/* Forward declaration. */ + +static void android_check_query (void); + int android_select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timespec *timeout) @@ -702,6 +714,11 @@ android_select (int nfds, fd_set *readfds, fd_set *writefds, static char byte; #endif + /* Since Emacs is reading keyboard input again, signify that queries + from input methods are no longer ``urgent''. */ + + __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST); + /* Check for and run anything the UI thread wants to run on the main thread. */ android_check_query (); @@ -7066,7 +7083,7 @@ android_display_toast (const char *text) /* Run any function that the UI thread has asked to run, and then signal its completion. */ -void +static void android_check_query (void) { void (*proc) (void *); @@ -7088,6 +7105,49 @@ android_check_query (void) __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST); __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST); __atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST); + __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST); + + /* Signal completion. */ + sem_post (&android_query_sem); +} + +/* Run any function that the UI thread has asked to run, if the UI + thread has been waiting for more than two seconds. + + Call this from `process_pending_signals' to ensure that the UI + thread always receives an answer within a reasonable amount of + time. */ + +void +android_check_query_urgent (void) +{ + void (*proc) (void *); + void *closure; + + if (!__atomic_load_n (&android_urgent_query, __ATOMIC_SEQ_CST)) + return; + + __android_log_print (ANDROID_LOG_VERBOSE, __func__, + "Responding to urgent query..."); + + if (!__atomic_load_n (&android_servicing_query, __ATOMIC_SEQ_CST)) + return; + + /* First, load the procedure and closure. */ + __atomic_load (&android_query_context, &closure, __ATOMIC_SEQ_CST); + __atomic_load (&android_query_function, &proc, __ATOMIC_SEQ_CST); + + if (!proc) + return; + + proc (closure); + + /* Finish the query. Don't clear `android_urgent_query'; instead, + do that the next time Emacs enters the keyboard loop. */ + + __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST); + __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST); + __atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST); /* Signal completion. */ sem_post (&android_query_sem); @@ -7118,6 +7178,7 @@ android_answer_query (void) /* Finish the query. */ __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST); __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST); + __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST); /* Signal completion. */ sem_post (&android_query_sem); @@ -7175,6 +7236,7 @@ android_begin_query (void) android_end_query (void) { __atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST); + __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST); } /* Synchronously ask the Emacs thread to run the specified PROC with @@ -7193,6 +7255,8 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) { union android_event event; char old; + int rc; + struct timespec timeout; event.xaction.type = ANDROID_WINDOW_ACTION; event.xaction.serial = ++event_serial; @@ -7227,9 +7291,50 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) time it is entered. */ android_write_event (&event); - /* Start waiting for the function to be executed. */ - while (sem_wait (&android_query_sem) < 0) - ;; + /* Start waiting for the function to be executed. First, wait two + seconds for the query to execute normally. */ + + timeout.tv_sec = 2; + timeout.tv_nsec = 0; + timeout = timespec_add (current_timespec (), timeout); + + /* See if an urgent query was recently answered without entering the + keyboard loop in between. When that happens, raise SIGIO to + continue processing queries as soon as possible. */ + + if (__atomic_load_n (&android_urgent_query, __ATOMIC_SEQ_CST)) + raise (SIGIO); + + again: + rc = sem_timedwait (&android_query_sem, &timeout); + + if (rc < 0) + { + if (errno == EINTR) + goto again; + + eassert (errno == ETIMEDOUT); + + __android_log_print (ANDROID_LOG_VERBOSE, __func__, + "Timed out waiting for response" + " from main thread..."); + + /* The query timed out. At this point, set + `android_urgent_query' to true. */ + __atomic_store_n (&android_urgent_query, true, __ATOMIC_SEQ_CST); + + /* And raise SIGIO. Now that the query is considered urgent, + the main thread will reply while reading async input. + + Normally, the main thread waits for the keyboard loop to be + entered before responding, in order to avoid responding with + inaccurate results taken during command executioon. */ + raise (SIGIO); + + /* Wait for the query to complete. */ + while (sem_wait (&android_query_sem) < 0) + ;; + } /* At this point, `android_servicing_query' should either be zero if the query was answered or two if the main thread has started a diff --git a/src/android.h b/src/android.h index c748d99a09a..8634ba01a3d 100644 --- a/src/android.h +++ b/src/android.h @@ -185,7 +185,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. /* Event loop functions. */ -extern void android_check_query (void); +extern void android_check_query_urgent (void); extern int android_run_in_emacs_thread (void (*) (void *), void *); extern void android_write_event (union android_event *); diff --git a/src/keyboard.c b/src/keyboard.c index f31f717195b..523718cdbaa 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -47,7 +47,11 @@ Copyright (C) 1985-1989, 1993-1997, 1999-2023 Free Software Foundation, #ifdef HAVE_TEXT_CONVERSION #include "textconv.h" -#endif +#endif /* HAVE_TEXT_CONVERSION */ + +#ifdef HAVE_ANDROID +#include "android.h" +#endif /* HAVE_ANDROID */ #include @@ -7906,6 +7910,14 @@ tty_read_avail_input (struct terminal *terminal, static void handle_async_input (void) { +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + /* Check and respond to an ``urgent'' query from the UI thread. + A query becomes urgent once the UI thread has been waiting + for more than two seconds. */ + + android_check_query_urgent (); +#endif /* HAVE_ANDROID && !ANDROID_STUBIFY */ + #ifndef DOS_NT while (1) { commit 8cbe35a84621edf4dd7cc71d6a6ae7e55699fc5a Author: Po Lu Date: Fri Jun 9 18:18:12 2023 +0800 ; Fix typos * src/textconv.c (really_commit_text): (handle_pending_conversion_events): Fix minor typos. diff --git a/src/textconv.c b/src/textconv.c index 92d44fe2e66..3303ca246de 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -687,7 +687,7 @@ really_commit_text (struct frame *f, EMACS_INT position, if (SCHARS (text)) { Finsert (1, &text); - record_buffer_change (start, PT, text); + record_buffer_change (wanted, PT, text); } if (position <= 0) @@ -1340,7 +1340,7 @@ handle_pending_conversion_events (void) static int inside; specpdl_ref count; ptrdiff_t last_point; - struct window *w, *w1; + struct window *w; handled = false; commit 01bea42cbff84ca76f339088f19a42cddfe83cbb Author: Po Lu Date: Fri Jun 9 18:05:26 2023 +0800 Avoid responding to input method queries asynchronously * src/androidterm.c (handle_one_android_event): Don't answer queries here; just rely on the event interrupting android_select. This avoids exposing buffer contents to input methods while a command is being executed. * src/textconv.c (TEXTCONV_DEBUG, really_commit_text) (really_finish_composing_text, really_set_composing_text) (really_set_composing_region, really_delete_surrounding_text) (really_set_point_and_mark, get_extracted_text): Add debugging printouts. diff --git a/src/androidterm.c b/src/androidterm.c index 77f2bd1c7a0..704ff5f5d85 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -1053,12 +1053,12 @@ handle_one_android_event (struct android_display_info *dpyinfo, used to make Android run stuff. */ if (!event->xaction.window && !event->xaction.action) - { - /* Check for and run anything the UI thread wants to run on the main - thread. */ - android_check_query (); - goto OTHER; - } + /* Don't run queries here, as it may run inside editor + commands, which can expose an inconsistent view of buffer + contents to the input method during command execution. + + Instead, wait for Emacs to return to `android_select'. */ + goto OTHER; f = any; diff --git a/src/textconv.c b/src/textconv.c index 1161b781b6a..92d44fe2e66 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -40,6 +40,24 @@ Copyright (C) 2023 Free Software Foundation, Inc. +/* Define debugging macros. */ + +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY +#if 0 +#include + +#define TEXTCONV_DEBUG(fmt, ...) \ + __android_log_print (ANDROID_LOG_VERBOSE, "EmacsInputConnection", \ + "%s: " fmt, __func__, ## __VA_ARGS__) +#endif /* 0 */ +#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ + +#ifndef TEXTCONV_DEBUG +#define TEXTCONV_DEBUG(...) ((void) 0) +#endif /* TEXTCONV_DEBUG */ + + + /* The window system's text conversion interface. NULL when the window system has not set up text conversion. */ @@ -701,6 +719,10 @@ really_commit_text (struct frame *f, EMACS_INT position, /* This should deactivate the mark. */ call0 (Qdeactivate_mark); + /* Print some debugging information. */ + TEXTCONV_DEBUG ("text inserted: %s, point now: %zd", + SSDATA (text), PT); + /* Update the ephemeral last point. */ w = XWINDOW (selected_window); w->ephemeral_last_point = PT; @@ -730,6 +752,8 @@ really_finish_composing_text (struct frame *f, bool update) if (!NILP (f->conversion.compose_region_overlay)) Fdelete_overlay (f->conversion.compose_region_overlay); + + TEXTCONV_DEBUG ("conversion region removed"); } /* Set the composing text on F to TEXT. Then, move point to an @@ -876,6 +900,13 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, w = XWINDOW (selected_window); w->ephemeral_last_point = PT; + if (SCHARS (text)) + TEXTCONV_DEBUG ("conversion region set to: %td %td", + marker_position (f->conversion.compose_region_start), + marker_position (f->conversion.compose_region_end)); + else + TEXTCONV_DEBUG ("conversion region removed; PT is now: %td", PT); + unbind_to (count, Qnil); } @@ -927,6 +958,9 @@ really_set_composing_region (struct frame *f, ptrdiff_t start, make_fixnum (end), Qnil); sync_overlay (f); + TEXTCONV_DEBUG ("composing region set to: %td, %td; point is: %td", + start, end, PT); + /* Update the ephemeral last point. */ w = XWINDOW (selected_window); w->ephemeral_last_point = PT; @@ -1011,6 +1045,9 @@ really_delete_surrounding_text (struct frame *f, ptrdiff_t left, record_buffer_change (start, start, text); } + TEXTCONV_DEBUG ("deleted surrounding text: %td, %td; PT is now %td", + left, right, PT); + /* if the mark is now equal to start, deactivate it. */ if (get_mark () == PT) @@ -1093,6 +1130,9 @@ really_set_point_and_mark (struct frame *f, ptrdiff_t point, w = XWINDOW (selected_window); w->ephemeral_last_point = PT; + TEXTCONV_DEBUG ("set point and mark: %td %td", + PT, get_mark ()); + unbind_to (count, Qnil); } @@ -1727,6 +1767,9 @@ get_extracted_text (struct frame *f, ptrdiff_t n, *length = end - start; *bytes = end_byte - start_byte; + TEXTCONV_DEBUG ("get_extracted_text: PT, mark, start: %td, %td, %td", + PT, mark, start); + finish: unbind_to (count, Qnil); return buffer; commit a5b74e2ff62faccea19c00783e62bc328c30f92a Author: Po Lu Date: Fri Jun 9 15:55:19 2023 +0800 Initialize text conversion hooks for each C Mode buffer * lisp/progmodes/cc-mode.el (c-initialize-cc-mode): Always add text conversion hooks. diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index 1364117bdc8..88e0aebb9a2 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -251,13 +251,14 @@ c-initialize-cc-mode (when (fboundp 'electric-indent-local-mode) (add-hook 'electric-indent-mode-hook 'c-electric-indent-mode-hook) (add-hook 'electric-indent-local-mode-hook - 'c-electric-indent-local-mode-hook)) - ;; Set up text conversion, for Emacs >= 30.0 - (when (boundp 'post-text-conversion-hook) - (add-hook 'post-text-conversion-hook #'c-post-text-conversion))) + 'c-electric-indent-local-mode-hook))) ;; Will try initialization hooks again if they failed. (put 'c-initialize-cc-mode initprop c-initialization-ok)))) + ;; Set up text conversion, for Emacs >= 30.0 + (when (boundp 'post-text-conversion-hook) + (add-hook 'post-text-conversion-hook #'c-post-text-conversion)) + (unless new-style-init (c-init-language-vars-for 'c-mode))) commit 41a99ea993e2c20253639e01637153a8259c5b89 Author: Po Lu Date: Fri Jun 9 14:16:08 2023 +0800 ; * src/android.c (android_get_gc_values): Remove redundancy. diff --git a/src/android.c b/src/android.c index 681723124ee..f45e0abbea6 100644 --- a/src/android.c +++ b/src/android.c @@ -3929,11 +3929,6 @@ android_get_gc_values (struct android_gc *gc, enum android_gc_value_mask mask, struct android_gc_values *values) { - jobject gcontext; - - gcontext = android_resolve_handle (gc->gcontext, - ANDROID_HANDLE_GCONTEXT); - if (mask & ANDROID_GC_FOREGROUND) /* GCs never have 32 bit colors, so we don't have to worry about sign extension here. */ commit c321eea5af535d102c5e1d2d7e95029ce6fee427 Author: Po Lu Date: Fri Jun 9 14:03:50 2023 +0800 Block profiling signals in the Android UI thread * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New function `setupSystemThread'. * java/org/gnu/emacs/EmacsService.java (onCreate): Block all signals except for SIGBUS and SIGSEGV in the UI thread. * src/android.c (setupSystemThread): New function. diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index cb89cf6808a..2fcbf8b94ef 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -188,6 +188,10 @@ public static native long sendExpose (short window, int x, int y, KEYCODE_VOLUME_MUTE should be forwarded to Emacs. */ public static native boolean shouldForwardMultimediaButtons (); + /* Initialize the current thread, by blocking signals that do not + interest it. */ + public static native void setupSystemThread (); + /* Input connection functions. These mostly correspond to their diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 48e39f8b355..96216e51cf4 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -25,6 +25,7 @@ import java.util.List; +import java.util.concurrent.Semaphore; import java.util.concurrent.atomic.AtomicInteger; import android.graphics.Matrix; @@ -211,6 +212,7 @@ public final class EmacsService extends Service final String filesDir, libDir, cacheDir, classPath; final double pixelDensityX; final double pixelDensityY; + final Semaphore signalSemaphore; SERVICE = this; handler = new Handler (Looper.getMainLooper ()); @@ -220,6 +222,7 @@ public final class EmacsService extends Service pixelDensityX = metrics.xdpi; pixelDensityY = metrics.ydpi; resolver = getContentResolver (); + signalSemaphore = new Semaphore (0); try { @@ -248,11 +251,33 @@ invocation of app_process (through android-emacs) can cacheDir, (float) pixelDensityX, (float) pixelDensityY, classPath, EmacsService.this); + + /* Wait for the signal mask to be set up in the UI + thread. */ + + while (true) + { + try + { + signalSemaphore.acquire (); + break; + } + catch (InterruptedException e) + { + ;; + } + } } }, extraStartupArgument, /* If any file needs to be opened, open it now. */ EmacsOpenActivity.fileToOpen); thread.start (); + + /* Now that the thread has been started, block signals which + don't interest the current thread. */ + + EmacsNative.setupSystemThread (); + signalSemaphore.release (); } catch (IOException exception) { diff --git a/src/android.c b/src/android.c index 92aab548180..681723124ee 100644 --- a/src/android.c +++ b/src/android.c @@ -3077,6 +3077,30 @@ NATIVE_NAME (answerQuerySpin) (JNIEnv *env, jobject object) android_answer_query_spin (); } + + +/* System thread setup. Android doesn't always block signals Emacs is + interested in from being received by the UI or render threads, + which can lead to problems when those signals then interrupt one of + those threads. */ + +JNIEXPORT void JNICALL +NATIVE_NAME (setupSystemThread) (void) +{ + sigset_t sigset; + + /* Block everything except for SIGSEGV and SIGBUS; those two are + used by the runtime. */ + + sigfillset (&sigset); + sigaddset (&sigset, SIGSEGV); + sigaddset (&sigset, SIGBUS); + + if (pthread_sigmask (SIG_BLOCK, &sigset, NULL)) + __android_log_print (ANDROID_LOG_WARN, __func__, + "pthread_sigmask: %s", strerror (errno)); +} + #ifdef __clang__ #pragma clang diagnostic pop #else commit 7f073df53374bc1b96ac07e949af11b8af06b94b Author: Po Lu Date: Fri Jun 9 09:27:04 2023 +0800 Fix crash starting Emacs to open file * java/org/gnu/emacs/EmacsThread.java (run): Correct check against extraStartupArgument when an initial file is specified. diff --git a/java/org/gnu/emacs/EmacsThread.java b/java/org/gnu/emacs/EmacsThread.java index 1343b70bf5a..c003ea95c50 100644 --- a/java/org/gnu/emacs/EmacsThread.java +++ b/java/org/gnu/emacs/EmacsThread.java @@ -65,7 +65,7 @@ public final class EmacsThread extends Thread } else { - if (extraStartupArgument != null) + if (extraStartupArgument == null) args = new String[] { "libandroid-emacs.so", fileToOpen, }; else commit f33f3b973a413e51d3f69e9e1efdd3227a5975f3 Merge: 6d86ded5442 c9c0d1cf7f6 Author: Po Lu Date: Fri Jun 9 08:55:52 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 6d86ded5442d9c2762a9275c5f3155db6d24ea80 Merge: 16617627845 46b6d175054 Author: Po Lu Date: Thu Jun 8 20:50:26 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 1661762784520eb6834aa9831dcb646396efde73 Author: Po Lu Date: Thu Jun 8 20:50:02 2023 +0800 Correctly display popup dialogs from Emacsclient * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): Make subclasses final. * java/org/gnu/emacs/EmacsDialog.java (display1): Check if an instance of EmacsOpenActivity is open; if it is, try using it to display the pop up dialog. * java/org/gnu/emacs/EmacsDialogButtonLayout.java (EmacsDialogButtonLayout): Make final. * java/org/gnu/emacs/EmacsHolder.java (EmacsHolder): Likewise. * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity): New field `currentActivity'. (onCreate, onDestroy, onWindowFocusChanged, onPause): Set that field as appropriate. diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index 60f2db67fb0..d69d0263b93 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -58,7 +58,7 @@ public final class EmacsContextMenu /* The last group ID used for a menu item. */ public int lastGroupId; - private static class Item implements MenuItem.OnMenuItemClickListener + private static final class Item implements MenuItem.OnMenuItemClickListener { public int itemID; public String itemName, tooltip; diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java index afdce3c50ec..3f8fe0109c0 100644 --- a/java/org/gnu/emacs/EmacsDialog.java +++ b/java/org/gnu/emacs/EmacsDialog.java @@ -68,8 +68,8 @@ public final class EmacsDialog implements DialogInterface.OnDismissListener /* The menu serial associated with this dialog box. */ private int menuEventSerial; - private class EmacsButton implements View.OnClickListener, - DialogInterface.OnClickListener + private final class EmacsButton implements View.OnClickListener, + DialogInterface.OnClickListener { /* Name of this button. */ public String name; @@ -244,13 +244,22 @@ private class EmacsButton implements View.OnClickListener, if (EmacsActivity.focusedActivities.isEmpty ()) { /* If focusedActivities is empty then this dialog may have - been displayed immediately after a popup dialog is + been displayed immediately after another popup dialog was dismissed. Or Emacs might legitimately be in the - background. Try the service context first if possible. */ + background, possibly displaying this popup in response to + an Emacsclient request. Try the service context if it will + work, then any focused EmacsOpenActivity, and finally the + last EmacsActivity to be focused. */ + + Log.d (TAG, "display1: no focused activities..."); + Log.d (TAG, ("display1: EmacsOpenActivity.currentActivity: " + + EmacsOpenActivity.currentActivity)); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M || Settings.canDrawOverlays (EmacsService.SERVICE)) context = EmacsService.SERVICE; + else if (EmacsOpenActivity.currentActivity != null) + context = EmacsOpenActivity.currentActivity; else context = EmacsActivity.lastFocusedActivity; diff --git a/java/org/gnu/emacs/EmacsDialogButtonLayout.java b/java/org/gnu/emacs/EmacsDialogButtonLayout.java index 5d97eea32aa..fd8d63d81d3 100644 --- a/java/org/gnu/emacs/EmacsDialogButtonLayout.java +++ b/java/org/gnu/emacs/EmacsDialogButtonLayout.java @@ -37,7 +37,7 @@ -public class EmacsDialogButtonLayout extends ViewGroup +public final class EmacsDialogButtonLayout extends ViewGroup { public EmacsDialogButtonLayout (Context context) diff --git a/java/org/gnu/emacs/EmacsHolder.java b/java/org/gnu/emacs/EmacsHolder.java index 3ca803d1640..6cd48ba57ce 100644 --- a/java/org/gnu/emacs/EmacsHolder.java +++ b/java/org/gnu/emacs/EmacsHolder.java @@ -24,7 +24,7 @@ /* This class serves as a simple reference to an object of type T. Nothing could be found inside the standard library. */ -public class EmacsHolder +public final class EmacsHolder { T thing; }; diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index f402e25c7fb..6af2b2d2e94 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -72,8 +72,17 @@ public final class EmacsOpenActivity extends Activity DialogInterface.OnCancelListener { private static final String TAG = "EmacsOpenActivity"; + + /* The name of any file that should be opened as EmacsThread starts + Emacs. This is never cleared, even if EmacsOpenActivity is + started a second time, as EmacsThread only starts once. */ public static String fileToOpen; + /* Any currently focused EmacsOpenActivity. Used to show pop ups + while the activity is active and Emacs doesn't have permission to + display over other programs. */ + public static EmacsOpenActivity currentActivity; + private class EmacsClientThread extends Thread { private ProcessBuilder builder; @@ -362,6 +371,15 @@ private class EmacsClientThread extends Thread thread.start (); } + /* Run emacsclient to open the file specified in the Intent that + caused this activity to start. + + Determine the name of the file corresponding to the URI specified + in that intent; then, run emacsclient and wait for it to finish. + + Finally, display any error message, transfer the focus to an + Emacs frame, and finish the activity. */ + @Override public void onCreate (Bundle savedInstanceState) @@ -463,10 +481,60 @@ private class EmacsClientThread extends Thread } } - /* And start emacsclient. */ + /* And start emacsclient. Set `currentActivity' to this now. + Presumably, it will shortly become capable of displaying + dialogs. */ + currentActivity = this; startEmacsClient (fileName); } else finish (); } + + + + @Override + public void + onDestroy () + { + Log.d (TAG, "onDestroy: " + this); + + /* Clear `currentActivity' if it refers to the activity being + destroyed. */ + + if (currentActivity == this) + this.currentActivity = null; + + super.onDestroy (); + } + + @Override + public void + onWindowFocusChanged (boolean isFocused) + { + Log.d (TAG, "onWindowFocusChanged: " + this + ", is now focused: " + + isFocused); + + if (isFocused) + currentActivity = this; + else if (currentActivity == this) + currentActivity = null; + + super.onWindowFocusChanged (isFocused); + } + + @Override + public void + onPause () + { + Log.d (TAG, "onPause: " + this); + + /* XXX: clear currentActivity here as well; I don't know whether + or not onWindowFocusChanged is always called prior to this. */ + + if (currentActivity == this) + currentActivity = null; + + super.onPause (); + } } commit b1bd40dce197d2938426d1ec33cebd3d51ccc8cf Author: Po Lu Date: Thu Jun 8 14:04:31 2023 +0800 Update Android port * src/android.c (android_is_special_directory): New function. (android_get_asset_name, android_content_name_p) (android_get_content_name): * src/android.h (android_is_special_directory) (JNI_STACK_ALIGNMENT_PROLOGUE): * src/fileio.c (check_mutable_filename): * src/filelock.c (WTMP_FILE, make_lock_file_name): * src/inotify.c (IN_ONLYDIR, Finotify_add_watch): Factor out checks against asset and content directories to that function. diff --git a/src/android.c b/src/android.c index f59c0d9e5d2..92aab548180 100644 --- a/src/android.c +++ b/src/android.c @@ -1045,19 +1045,72 @@ android_user_full_name (struct passwd *pw) #endif } + + +/* Determine whether or not the specified file NAME describes a file + in the directory DIR, which should be an absolute file name. NAME + must be in canonical form. + + Value is NULL if not. Otherwise, it is a pointer to the first + character in NAME after the part containing DIR and its trailing + directory separator. */ + +const char * +android_is_special_directory (const char *name, const char *dir) +{ + size_t len; + + /* Compare up to strlen (DIR) bytes of NAME with DIR. */ + + len = strlen (dir); + if (strncmp (name, dir, len)) + return NULL; + + /* Now see if the character of NAME after len is either a directory + separator or a terminating NULL. */ + + name += len; + switch (*name) + { + case '\0': + /* Return the empty string if this is the end of the file + name. */ + return name; + + case '/': + /* Return NAME (with the separator removed) if it describes a + file. */ + return name + 1; + + default: + /* The file name doesn't match. */ + return NULL; + } +} + /* Given a real file name, return the part that describes its asset - path, or NULL if it is not an asset. */ + path, or NULL if it is not an asset. + + If FILENAME contains only `/assets', return `/' to indicate the + root of the assets hierarchy. */ static const char * android_get_asset_name (const char *filename) { - if (!strcmp (filename, "/assets") || !strcmp (filename, "/assets/")) - return "/"; + const char *name; - if (!strncmp (filename, "/assets/", sizeof "/assets/" - 1)) - return filename + (sizeof "/assets/" - 1); + name = android_is_special_directory (filename, "/assets"); - return NULL; + if (!name) + return NULL; + + /* If NAME is empty, return /. Otherwise, return the name relative + to /assets/. */ + + if (*name) + return name; + + return "/"; } /* Return whether or not the specified FILENAME actually resolves to a @@ -1072,9 +1125,9 @@ android_content_name_p (const char *filename) if (android_api_level < 19) return false; - return (!strcmp (filename, "/content") - || !strncmp (filename, "/content/", - sizeof "/content/" - 1)); + return (android_is_special_directory (filename, + "/content") + != NULL); } /* Return the content URI corresponding to a `/content' file name, @@ -1091,20 +1144,21 @@ android_get_content_name (const char *filename) n = PATH_MAX; - /* First handle content ``URIs'' without a provider. */ + /* Find the file name described if it starts with `/content'. If + just the directory is described, return content://. */ - if (!strcmp (filename, "/content") - || !strcmp (filename, "/content/")) - return "content://"; + filename = android_is_special_directory (filename, "/content"); - /* Next handle ordinary file names. */ - - if (strncmp (filename, "/content/", sizeof "/content/" - 1)) + if (!filename) return NULL; - /* Forward past the first directory specifying the schema. */ + if (!*filename) + return "content://"; + + /* Now copy FILENAME into a buffer and convert it into a content + URI. */ - copy = xstrdup (filename + sizeof "/content"); + copy = xstrdup (filename); token = saveptr = NULL; head = stpcpy (buffer, "content:/"); diff --git a/src/android.h b/src/android.h index d0440259161..c748d99a09a 100644 --- a/src/android.h +++ b/src/android.h @@ -48,6 +48,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. extern int android_open (const char *, int, mode_t); extern char *android_user_full_name (struct passwd *); +extern const char *android_is_special_directory (const char *, const char *); extern int android_fstat (int, struct stat *); extern int android_fstatat (int, const char *restrict, struct stat *restrict, int); @@ -195,7 +196,13 @@ Copyright (C) 2023 Free Software Foundation, Inc. /* Process related functions. */ extern int android_rewrite_spawn_argv (const char ***); -#endif +#else /* ANDROID_STUBIFY */ + +/* Define a substitute for use during Emacs compilation. */ + +#define android_is_special_directory(name, dir) ((const char *) NULL) + +#endif /* !ANDROID_STUBIFY */ /* JNI functions should not be built when Emacs is stubbed out for the build. These should be documented in EmacsNative.java. */ @@ -227,5 +234,5 @@ #define JNI_STACK_ALIGNMENT_PROLOGUE \ #define JNI_STACK_ALIGNMENT_PROLOGUE ((void) 0) #endif /* __i386__ */ -#endif +#endif /* !ANDROID_STUBIFY */ #endif /* _ANDROID_H_ */ diff --git a/src/fileio.c b/src/fileio.c index f2f440d0a3b..fbccd796751 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -56,9 +56,9 @@ Copyright (C) 1985-1988, 1993-2023 Free Software Foundation, Inc. #include "region-cache.h" #include "frame.h" -#if defined HAVE_ANDROID +#ifdef HAVE_ANDROID #include "android.h" -#endif +#endif /* HAVE_ANDROID */ #ifdef HAVE_LINUX_FS_H # include @@ -193,9 +193,11 @@ #define emacs_fd_to_int(fds) ((fds).asset ? -1 : (fds).fd) check_mutable_filename (Lisp_Object encoded, bool write) { #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY - if (!strcmp (SSDATA (encoded), "/assets") - || !strncmp (SSDATA (encoded), "/assets/", - sizeof "/assets/" - 1)) + const char *name; + + name = SSDATA (encoded); + + if (android_is_special_directory (name, "/assets")) xsignal2 (Qfile_error, build_string ("File lies on read-only directory"), encoded); @@ -203,13 +205,11 @@ check_mutable_filename (Lisp_Object encoded, bool write) if (write) return; - if (!strcmp (SSDATA (encoded), "/content") - || !strncmp (SSDATA (encoded), "/content/", - sizeof "/content/" - 1)) + if (android_is_special_directory (name, "/content")) xsignal2 (Qfile_error, build_string ("File lies on read-only directory"), encoded); -#endif +#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ } diff --git a/src/filelock.c b/src/filelock.c index be551fc876f..cbbcc016b27 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -75,6 +75,10 @@ #define BOOT_TIME_FILE "/var/run/random-seed" #define WTMP_FILE "/var/log/wtmp" #endif +#ifdef HAVE_ANDROID +#include "android.h" /* For `android_is_special_directory'. */ +#endif /* HAVE_ANDROID */ + /* Normally use a symbolic link to represent a lock. The strategy: to lock a file FN, create a symlink .#FN in FN's directory, with link data USER@HOST.PID:BOOT. This avoids a single @@ -673,14 +677,10 @@ make_lock_file_name (Lisp_Object fn) name = SSDATA (fn); - if (strcmp (name, "/assets") - || strcmp (name, "/assets/") - || strcmp (name, "/content") - || strcmp (name, "/content/") - || strncmp (name, "/assets/", sizeof "/assets") - || strncmp (name, "/content/", sizeof "/content")) + if (android_is_special_directory (name, "/assets") + || android_is_special_directory (name, "/content")) return Qnil; -#endif +#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ lock_file_name = call1 (Qmake_lock_file_name, fn); diff --git a/src/inotify.c b/src/inotify.c index 844bf54105c..105ff5a9d8a 100644 --- a/src/inotify.c +++ b/src/inotify.c @@ -40,6 +40,10 @@ Copyright (C) 2012-2023 Free Software Foundation, Inc. # define IN_ONLYDIR 0 #endif +#ifdef HAVE_ANDROID +#include "android.h" /* For `android_is_special_directory'. */ +#endif /* HAVE_ANDROID */ + /* File handle for inotify. */ static int inotifyfd = -1; @@ -440,14 +444,10 @@ renames (moved-from and moved-to). instead of letting inotify fail. These directories cannot receive file notifications as they are read only. */ - if (strcmp (name, "/assets") - || strcmp (name, "/assets/") - || strcmp (name, "/content") - || strcmp (name, "/content/") - || strncmp (name, "/assets/", sizeof "/assets") - || strncmp (name, "/content/", sizeof "/content")) + if (android_is_special_directory (name, "/assets") + || android_is_special_directory (name, "/content")) return Qnil; -#endif +#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ wd = inotify_add_watch (inotifyfd, name, mask); if (wd < 0) commit f2b2863ff7380e7c3c31662ca7615bd8edb83440 Author: Po Lu Date: Thu Jun 8 08:48:37 2023 +0800 ; Update from Gnulib diff --git a/lib/getdelim.c b/lib/getdelim.c index 79ec3dd12a3..e414df648f6 100644 --- a/lib/getdelim.c +++ b/lib/getdelim.c @@ -30,10 +30,6 @@ #define _GL_ARG_NONNULL(params) #include #include -#ifndef SSIZE_MAX -# define SSIZE_MAX ((ssize_t) (SIZE_MAX / 2)) -#endif - #if USE_UNLOCKED_IO # include "unlocked-io.h" # define getc_maybe_unlocked(fp) getc(fp) diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 802790e14e4..63a6cd60f35 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -927,6 +927,14 @@ divide (mpn_t a, mpn_t b, mpn_t *q) return roomptr; } +/* Avoid pointless GCC warning "argument 1 value '18446744073709551615' exceeds + maximum object size 9223372036854775807", triggered by the use of xsum as + argument of malloc. */ +# if __GNUC__ >= 7 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Walloc-size-larger-than=" +# endif + /* Convert a bignum a >= 0, multiplied with 10^extra_zeroes, to decimal representation. Destroys the contents of a. @@ -983,6 +991,10 @@ convert_to_decimal (mpn_t a, size_t extra_zeroes) return c_ptr; } +# if __GNUC__ >= 7 +# pragma GCC diagnostic pop +# endif + # if NEED_PRINTF_LONG_DOUBLE /* Assuming x is finite and >= 0: @@ -1177,8 +1189,6 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) void *z_memory; char *digits; - if (memory == NULL) - return NULL; /* x = 2^e * m, hence y = round (2^e * 10^n * m) = round (2^(e+n) * 5^n * m) = round (2^s * 5^n * m). */ @@ -1386,10 +1396,13 @@ scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n) static char * scale10_round_decimal_long_double (long double x, int n) { - int e IF_LINT(= 0); + int e; mpn_t m; void *memory = decode_long_double (x, &e, &m); - return scale10_round_decimal_decoded (e, m, memory, n); + if (memory != NULL) + return scale10_round_decimal_decoded (e, m, memory, n); + else + return NULL; } # endif @@ -1404,10 +1417,13 @@ scale10_round_decimal_long_double (long double x, int n) static char * scale10_round_decimal_double (double x, int n) { - int e IF_LINT(= 0); + int e; mpn_t m; void *memory = decode_double (x, &e, &m); - return scale10_round_decimal_decoded (e, m, memory, n); + if (memory != NULL) + return scale10_round_decimal_decoded (e, m, memory, n); + else + return NULL; } # endif commit 59fdd16f900ad16e726b46b266e85df0c22748c8 Merge: 49dceb9dd6b a902156068a Author: Po Lu Date: Thu Jun 8 08:46:19 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 49dceb9dd6b3c2567a4a405e4a480aa79ebdc005 Author: Po Lu Date: Wed Jun 7 19:52:38 2023 +0800 ; Update Android port * doc/emacs/android.texi (Android Startup): Fix reference to non existent node. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 0fdc620f3f6..f13b3e640ae 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -120,8 +120,8 @@ Android Startup details. Once Emacs starts up, simply running the command @command{logcat} as -an asynchronous shell command (@pxref{Running Shell Commands From -Emacs}) will display the log buffer. +an asynchronous shell command (@pxref{Shell}) will display the log +buffer. @cindex emacsclient wrapper, android Since there is no other way to start the @command{emacsclient} commit 63339a9577f085074d16fa8c56ddd56864c94dda Author: Po Lu Date: Wed Jun 7 11:03:56 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsInputConnection.java (beginBatchEdit) (endBatchEdit, commitCompletion, commitText, deleteSurroundingText) (finishComposingText, getSelectedText, getTextAfterCursor) (getTextBeforeCursor, setComposingText, setComposingRegion) (performEditorAction, performContextMenuAction, getExtractedText) (setSelection, sendKeyEvent, deleteSurroundingTextInCodePoints) (requestCursorUpdates): Ensure that the input connection is up to date. (getSurroundingText): New function. * java/org/gnu/emacs/EmacsNative.java (getSurroundingText): Export new C function. * java/org/gnu/emacs/EmacsService.java (resetIC): Invalidate previously created input connections. * java/org/gnu/emacs/EmacsView.java (EmacsView) (onCreateInputConnection): Signify that input connections are now up to date. * src/androidterm.c (struct android_get_surrounding_text_context): New structure. (android_get_surrounding_text, NATIVE_NAME): * src/textconv.c (get_surrounding_text): * src/textconv.h: New functions. diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index 9ced7cb7aaf..73c93c67ac7 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -23,7 +23,9 @@ import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.SurroundingText; import android.view.inputmethod.TextSnapshot; + import android.view.KeyEvent; import android.os.Build; @@ -88,6 +90,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean beginBatchEdit () { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, "beginBatchEdit"); @@ -99,6 +105,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean endBatchEdit () { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, "endBatchEdit"); @@ -110,6 +120,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean commitCompletion (CompletionInfo info) { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, "commitCompletion: " + info); @@ -125,6 +139,10 @@ public final class EmacsInputConnection extends BaseInputConnection { int[] selection; + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, "commitText: " + text + " " + newCursorPosition); @@ -156,6 +174,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean deleteSurroundingText (int leftLength, int rightLength) { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, ("deleteSurroundingText: " + leftLength + " " + rightLength)); @@ -169,6 +191,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean finishComposingText () { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, "finishComposingText"); @@ -180,6 +206,10 @@ public final class EmacsInputConnection extends BaseInputConnection public String getSelectedText (int flags) { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return null; + if (EmacsService.DEBUG_IC) Log.d (TAG, "getSelectedText: " + flags); @@ -192,6 +222,10 @@ public final class EmacsInputConnection extends BaseInputConnection { String string; + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return null; + if (EmacsService.DEBUG_IC) Log.d (TAG, "getTextAfterCursor: " + length + " " + flags); @@ -210,6 +244,10 @@ public final class EmacsInputConnection extends BaseInputConnection { String string; + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return null; + if (EmacsService.DEBUG_IC) Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags); @@ -226,6 +264,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean setComposingText (CharSequence text, int newCursorPosition) { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, ("setComposingText: " + text + " ## " + newCursorPosition)); @@ -239,6 +281,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean setComposingRegion (int start, int end) { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, "setComposingRegion: " + start + " " + end); @@ -250,6 +296,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean performEditorAction (int editorAction) { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, "performEditorAction: " + editorAction); @@ -263,6 +313,10 @@ public final class EmacsInputConnection extends BaseInputConnection { int action; + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, "performContextMenuAction: " + contextMenuAction); @@ -310,6 +364,10 @@ public final class EmacsInputConnection extends BaseInputConnection ExtractedText text; int[] selection; + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return null; + if (EmacsService.DEBUG_IC) Log.d (TAG, "getExtractedText: " + request.hintMaxChars + ", " + request.hintMaxLines + " " + flags); @@ -360,6 +418,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean setSelection (int start, int end) { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, "setSelection: " + start + " " + end); @@ -371,6 +433,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean sendKeyEvent (KeyEvent key) { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, "sendKeyEvent: " + key); @@ -381,6 +447,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean deleteSurroundingTextInCodePoints (int beforeLength, int afterLength) { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + /* This can be implemented the same way as deleteSurroundingText. */ return this.deleteSurroundingText (beforeLength, afterLength); @@ -390,6 +460,10 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean requestCursorUpdates (int cursorUpdateMode) { + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return false; + if (EmacsService.DEBUG_IC) Log.d (TAG, "requestCursorUpdates: " + cursorUpdateMode); @@ -397,6 +471,37 @@ public final class EmacsInputConnection extends BaseInputConnection return true; } + @Override + public SurroundingText + getSurroundingText (int beforeLength, int afterLength, + int flags) + { + SurroundingText text; + + /* Return if the input connection is out of date. */ + if (view.icSerial < view.icGeneration) + return null; + + if (EmacsService.DEBUG_IC) + Log.d (TAG, ("getSurroundingText: " + beforeLength + ", " + + afterLength)); + + text = EmacsNative.getSurroundingText (windowHandle, beforeLength, + afterLength, flags); + + if (text != null) + Log.d (TAG, ("getSurroundingText: " + + text.getSelectionStart () + + "," + + text.getSelectionEnd () + + "+" + + text.getOffset () + + ": " + + text.getText ())); + + return text; + } + /* Override functions which are not implemented. */ diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index cb1c6caa79a..cb89cf6808a 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -25,6 +25,7 @@ import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.SurroundingText; public final class EmacsNative { @@ -222,6 +223,9 @@ public static native ExtractedText getExtractedText (short window, public static native void requestSelectionUpdate (short window); public static native void requestCursorUpdates (short window, int mode); public static native void clearInputFlags (short window); + public static native SurroundingText getSurroundingText (short window, + int left, int right, + int flags); /* Return the current value of the selection, or -1 upon diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 2074a5b7c2b..48e39f8b355 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -748,6 +748,7 @@ invocation of app_process (through android-emacs) can window.view.setICMode (icMode); icBeginSynchronous (); + window.view.icGeneration++; window.view.imManager.restartInput (window.view); icEndSynchronous (); } diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index a78dec08839..d432162132d 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -111,6 +111,13 @@ public final class EmacsView extends ViewGroup details. */ private int icMode; + /* The number of calls to `resetIC' to have taken place the last + time an InputConnection was created. */ + public long icSerial; + + /* The number of calls to `recetIC' that have taken place. */ + public volatile long icGeneration; + public EmacsView (EmacsWindow window) { @@ -627,6 +634,12 @@ else if (child.getVisibility () != GONE) return null; } + /* Set icSerial. If icSerial < icGeneration, the input connection + has been reset, and future input should be ignored until a new + connection is created. */ + + icSerial = icGeneration; + /* Reset flags set by the previous input method. */ EmacsNative.clearInputFlags (window.handle); diff --git a/src/androidterm.c b/src/androidterm.c index 2a054715d6a..77f2bd1c7a0 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5636,6 +5636,149 @@ NATIVE_NAME (clearInputFlags) (JNIEnv *env, jobject object, android_write_event (&event); } + + +/* Context for a call to `getSurroundingText'. */ + +struct android_get_surrounding_text_context +{ + /* Number of characters before the region to return. */ + int before_length; + + /* Number of characters after the region to return. */ + int after_length; + + /* The returned text, or NULL. */ + char *text; + + /* The size of that text in characters and bytes. */ + ptrdiff_t length, bytes; + + /* Offsets into that text. */ + ptrdiff_t offset, start, end; + + /* The window. */ + android_window window; +}; + +/* Return the surrounding text in the surrounding text context + specified by DATA. */ + +static void +android_get_surrounding_text (void *data) +{ + struct android_get_surrounding_text_context *request; + struct frame *f; + ptrdiff_t temp; + + request = data; + + /* Find the frame associated with the window. */ + f = android_window_to_frame (NULL, request->window); + + if (!f) + return; + + /* Now get the surrounding text. */ + request->text + = get_surrounding_text (f, request->before_length, + request->after_length, &request->length, + &request->bytes, &request->offset, + &request->start, &request->end); + + /* Sort request->start and request->end for compatibility with some + bad input methods. */ + + if (request->end < request->start) + { + temp = request->start; + request->start = request->end; + request->end = temp; + } +} + +JNIEXPORT jobject JNICALL +NATIVE_NAME (getSurroundingText) (JNIEnv *env, jobject ignored_object, + jshort window, jint before_length, + jint after_length, jint flags) +{ + JNI_STACK_ALIGNMENT_PROLOGUE; + + static jclass class; + static jmethodID constructor; + + struct android_get_surrounding_text_context context; + jstring string; + jobject object; + + /* Initialize CLASS if it has not yet been initialized. */ + + if (!class) + { + class + = (*env)->FindClass (env, ("android/view/inputmethod" + "/SurroundingText")); + +#if __ANDROID_API__ < 31 + /* If CLASS cannot be found, the version of Android currently + running is too old. */ + + if (!class) + { + (*env)->ExceptionClear (env); + return NULL; + } +#else /* __ANDROID_API__ >= 31 */ + assert (class); +#endif /* __ANDROID_API__ < 31 */ + + class = (*env)->NewGlobalRef (env, class); + if (!class) + return NULL; + + /* Now look for its constructor. */ + constructor = (*env)->GetMethodID (env, class, "", + "(Ljava/lang/CharSequence;III)V"); + assert (constructor); + } + + context.before_length = before_length; + context.after_length = after_length; + context.window = window; + context.text = NULL; + + android_sync_edit (); + if (android_run_in_emacs_thread (android_get_surrounding_text, + &context)) + return NULL; + + if (!context.text) + return NULL; + + /* Encode the returned text. */ + string = android_text_to_string (env, context.text, context.length, + context.bytes); + free (context.text); + + if (!string) + return NULL; + + /* Create an SurroundingText object containing this information. */ + object = (*env)->NewObject (env, class, constructor, string, + (jint) min (context.start, + TYPE_MAXIMUM (jint)), + (jint) min (context.end, + TYPE_MAXIMUM (jint)), + /* Adjust point offsets to fit into + Android's 0-based indexing. */ + (jint) min (context.offset - 1, + TYPE_MAXIMUM (jint))); + if (!object) + return NULL; + + return object; +} + #ifdef __clang__ #pragma clang diagnostic pop #else diff --git a/src/textconv.c b/src/textconv.c index 0dcf5bdcea8..1161b781b6a 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -1732,6 +1732,106 @@ get_extracted_text (struct frame *f, ptrdiff_t n, return buffer; } +/* Return the text between the positions PT - LEFT and PT + RIGHT. If + the mark is active, return the range of text relative to the bounds + of the region instead. + + Set *LENGTH to the number of characters returned, *BYTES to the + number of bytes returned, *OFFSET to the character position of the + returned text, and *START_RETURN and *END_RETURN to the mark and + point relative to that position. */ + +char * +get_surrounding_text (struct frame *f, ptrdiff_t left, + ptrdiff_t right, ptrdiff_t *length, + ptrdiff_t *bytes, ptrdiff_t *offset, + ptrdiff_t *start_return, + ptrdiff_t *end_return) +{ + specpdl_ref count; + ptrdiff_t start, end, start_byte, end_byte, mark, temp; + char *buffer; + + if (!WINDOW_LIVE_P (f->old_selected_window)) + return NULL; + + /* Save the excursion, as there will be extensive changes to the + selected window. */ + count = SPECPDL_INDEX (); + record_unwind_protect_excursion (); + + /* Inhibit quitting. */ + specbind (Qinhibit_quit, Qt); + + /* Temporarily switch to F's selected window at the time of the last + redisplay. */ + select_window (f->old_selected_window, Qt); + buffer = NULL; + + /* Figure out the bounds of the text to return. */ + + /* First, obtain start and end. */ + end = get_mark (); + start = PT; + + /* If the mark is not active, make it start and end. */ + + if (end == -1) + end = start; + + /* Now sort start and end. */ + + if (end < start) + { + temp = start; + start = end; + end = temp; + } + + /* And subtract left and right. */ + + if (INT_SUBTRACT_WRAPV (start, left, &start) + || INT_ADD_WRAPV (end, right, &end)) + goto finish; + + start = max (start, BEGV); + end = min (end, ZV); + + /* Detect overflow. */ + + if (!(start <= PT && PT <= end)) + goto finish; + + /* Convert the character positions to byte positions. */ + start_byte = CHAR_TO_BYTE (start); + end_byte = CHAR_TO_BYTE (end); + + /* Extract the text from the buffer. */ + buffer = xmalloc (end_byte - start_byte); + copy_buffer (start, start_byte, end, end_byte, + buffer); + + /* Get the mark. If it's not active, use PT. */ + + mark = get_mark (); + + if (mark == -1) + mark = PT; + + /* Return the offsets. Unlike `get_extracted_text', this need not + sort mark and point. */ + + *offset = start; + *start_return = mark - start; + *end_return = PT - start; + *length = end - start; + *bytes = end_byte - start_byte; + + finish: + unbind_to (count, Qnil); + return buffer; +} + /* Return whether or not text conversion is temporarily disabled. `reset' should always call this to determine whether or not to disable the input method. */ diff --git a/src/textconv.h b/src/textconv.h index 339cefdba92..7550388a723 100644 --- a/src/textconv.h +++ b/src/textconv.h @@ -143,6 +143,10 @@ #define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0) extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, bool *); +extern char *get_surrounding_text (struct frame *, ptrdiff_t, + ptrdiff_t, ptrdiff_t *, + ptrdiff_t *, ptrdiff_t *, + ptrdiff_t *, ptrdiff_t *); extern bool conversion_disabled_p (void); extern void register_textconv_interface (struct textconv_interface *); commit 9a68041f2ccb07c2baf9cf6e1075243706f73b82 Merge: 8b102bf6067 ef8485ad05d Author: Po Lu Date: Wed Jun 7 07:28:00 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 8b102bf6067b59f6ca56d464d866404cde36b8e5 Author: Po Lu Date: Tue Jun 6 21:06:58 2023 +0800 ; * lisp/simple.el (analyze-text-conversion): Remove old workaround. diff --git a/lisp/simple.el b/lisp/simple.el index 62489d8ffcb..3757f91d11a 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -11051,12 +11051,7 @@ analyze-text-conversion ;; Save the current undo list to figure out ;; whether or not auto-fill has actually taken ;; place. - (old-undo-list buffer-undo-list) - ;; FIXME: this leads to an error in - ;; `atomic-change-group', seemingly because - ;; buffer-undo-list is being modified or - ;; prematurely truncated. Turn it off for now. - (electric-pair-preserve-balance nil)) + (old-undo-list buffer-undo-list)) (save-excursion (if (and auto-fill-function newline-p) (progn (goto-char (nth 2 edit)) commit 34391d66757c8a1f197947156d7c3f07fa2ffa96 Merge: 1263531b9a2 229f0b8dd3b Author: Po Lu Date: Tue Jun 6 21:06:16 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 1263531b9a267ee024264b8aee2d616935969030 Merge: 5b4dea0fc78 bf28b019a85 Author: Po Lu Date: Tue Jun 6 21:01:56 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 5b4dea0fc781fe40548e7b58fe5bd201a05f3913 Author: Po Lu Date: Tue Jun 6 14:35:19 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsContextMenu.java (display): Use `EmacsHolder' instead of `Holder'. * java/org/gnu/emacs/EmacsDialog.java (toAlertDialog): Use `EmacsDialogButtonLayout' to ensure that buttons are wrapped properly. (display): Adjust for new holder class. * java/org/gnu/emacs/EmacsDialogButtonLayout.java (EmacsDialogButtonLayout, onMeasure, onLayout): New functions. * java/org/gnu/emacs/EmacsDrawLine.java: * java/org/gnu/emacs/EmacsFillPolygon.java: Remove redundant imports. * java/org/gnu/emacs/EmacsHolder.java (EmacsHolder): * java/org/gnu/emacs/EmacsService.java (class Holder) (getEmacsView, EmacsService): Rename `Holder' to `EmacsHolder' and make it public. diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index 5bae41bd61d..60f2db67fb0 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -331,9 +331,9 @@ private static class Item implements MenuItem.OnMenuItemClickListener final int yPosition, final int serial) { Runnable runnable; - final Holder rc; + final EmacsHolder rc; - rc = new Holder (); + rc = new EmacsHolder (); runnable = new Runnable () { @Override diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java index 3a5f22021fc..afdce3c50ec 100644 --- a/java/org/gnu/emacs/EmacsDialog.java +++ b/java/org/gnu/emacs/EmacsDialog.java @@ -150,7 +150,7 @@ private class EmacsButton implements View.OnClickListener, AlertDialog dialog; int size; EmacsButton button; - LinearLayout layout; + EmacsDialogButtonLayout layout; Button buttonView; ViewGroup.LayoutParams layoutParams; @@ -192,19 +192,16 @@ private class EmacsButton implements View.OnClickListener, } else { - /* There are more than 4 buttons. Add them all to a - LinearLayout. */ - layout = new LinearLayout (context); - layoutParams - = new LinearLayout.LayoutParams (ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT); + /* There are more than 3 buttons. Add them all to a special + container widget that handles wrapping. */ + + layout = new EmacsDialogButtonLayout (context); for (EmacsButton emacsButton : buttons) { buttonView = new Button (context); buttonView.setText (emacsButton.name); buttonView.setOnClickListener (emacsButton); - buttonView.setLayoutParams (layoutParams); buttonView.setEnabled (emacsButton.enabled); layout.addView (buttonView); } @@ -336,9 +333,9 @@ private class EmacsButton implements View.OnClickListener, display () { Runnable runnable; - final Holder rc; + final EmacsHolder rc; - rc = new Holder (); + rc = new EmacsHolder (); runnable = new Runnable () { @Override public void diff --git a/java/org/gnu/emacs/EmacsDialogButtonLayout.java b/java/org/gnu/emacs/EmacsDialogButtonLayout.java new file mode 100644 index 00000000000..5d97eea32aa --- /dev/null +++ b/java/org/gnu/emacs/EmacsDialogButtonLayout.java @@ -0,0 +1,152 @@ +/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + +package org.gnu.emacs; + + + +import android.content.Context; + +import android.view.View; +import android.view.View.MeasureSpec; +import android.view.ViewGroup; + + + +/* This ``view group'' implements a container widget for multiple + buttons of the type found in pop-up dialogs. It is used when + displaying a dialog box that contains more than three buttons, as + the default dialog box widget is not capable of holding more than + that many. */ + + + +public class EmacsDialogButtonLayout extends ViewGroup +{ + public + EmacsDialogButtonLayout (Context context) + { + super (context); + } + + @Override + protected void + onMeasure (int widthMeasureSpec, int heightMeasureSpec) + { + int width, count, i, x, y, height, spec, tempSpec; + View view; + + /* Obtain the width of this widget and create the measure + specification used to measure children. */ + + width = MeasureSpec.getSize (widthMeasureSpec); + spec = MeasureSpec.makeMeasureSpec (0, MeasureSpec.UNSPECIFIED); + tempSpec + = MeasureSpec.makeMeasureSpec (width, MeasureSpec.AT_MOST); + x = y = height = 0; + + /* Run through each widget. */ + + count = getChildCount (); + + for (i = 0; i < count; ++i) + { + view = getChildAt (i); + + /* Measure this view. */ + view.measure (spec, spec); + + if (width - x < view.getMeasuredWidth ()) + { + /* Move onto the next line, unless this line is empty. */ + + if (x != 0) + { + y += height; + height = x = 0; + } + + if (view.getMeasuredWidth () > width) + /* Measure the view again, this time forcing it to be at + most width wide, if it is not already. */ + view.measure (tempSpec, spec); + } + + height = Math.max (height, view.getMeasuredHeight ()); + x += view.getMeasuredWidth (); + } + + /* Now set the measured size of this widget. */ + setMeasuredDimension (width, y + height); + } + + @Override + protected void + onLayout (boolean changed, int left, int top, int right, + int bottom) + { + int width, count, i, x, y, height, spec, tempSpec; + View view; + + /* Obtain the width of this widget and create the measure + specification used to measure children. */ + + width = getMeasuredWidth (); + spec = MeasureSpec.makeMeasureSpec (0, MeasureSpec.UNSPECIFIED); + tempSpec + = MeasureSpec.makeMeasureSpec (width, MeasureSpec.AT_MOST); + x = y = height = 0; + + /* Run through each widget. */ + + count = getChildCount (); + + for (i = 0; i < count; ++i) + { + view = getChildAt (i); + + /* Measure this view. */ + view.measure (spec, spec); + + if (width - x < view.getMeasuredWidth ()) + { + /* Move onto the next line, unless this line is empty. */ + + if (x != 0) + { + y += height; + height = x = 0; + } + + if (view.getMeasuredWidth () > width) + /* Measure the view again, this time forcing it to be at + most width wide, if it is not already. */ + view.measure (tempSpec, spec); + } + + /* Now assign this view its position. */ + view.layout (x, y, x + view.getMeasuredWidth (), + y + view.getMeasuredHeight ()); + + /* And move on to the next widget. */ + height = Math.max (height, view.getMeasuredHeight ()); + x += view.getMeasuredWidth (); + } + } +}; diff --git a/java/org/gnu/emacs/EmacsDrawLine.java b/java/org/gnu/emacs/EmacsDrawLine.java index 92e03c48e26..3f5067c0bdd 100644 --- a/java/org/gnu/emacs/EmacsDrawLine.java +++ b/java/org/gnu/emacs/EmacsDrawLine.java @@ -19,8 +19,6 @@ package org.gnu.emacs; -import java.lang.Math; - import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; diff --git a/java/org/gnu/emacs/EmacsFillPolygon.java b/java/org/gnu/emacs/EmacsFillPolygon.java index ea8324c543c..4ae3882cab4 100644 --- a/java/org/gnu/emacs/EmacsFillPolygon.java +++ b/java/org/gnu/emacs/EmacsFillPolygon.java @@ -19,8 +19,6 @@ package org.gnu.emacs; -import java.lang.Math; - import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; diff --git a/java/org/gnu/emacs/EmacsHolder.java b/java/org/gnu/emacs/EmacsHolder.java new file mode 100644 index 00000000000..3ca803d1640 --- /dev/null +++ b/java/org/gnu/emacs/EmacsHolder.java @@ -0,0 +1,30 @@ +/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + +package org.gnu.emacs; + + + +/* This class serves as a simple reference to an object of type T. + Nothing could be found inside the standard library. */ + +public class EmacsHolder +{ + T thing; +}; diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 6d70536faf0..2074a5b7c2b 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -71,11 +71,6 @@ import android.widget.Toast; -class Holder -{ - T thing; -}; - /* EmacsService is the service that starts the thread running Emacs and handles requests by that Emacs instance. */ @@ -282,9 +277,9 @@ invocation of app_process (through android-emacs) can final boolean isFocusedByDefault) { Runnable runnable; - final Holder view; + final EmacsHolder view; - view = new Holder (); + view = new EmacsHolder (); runnable = new Runnable () { public void @@ -604,10 +599,10 @@ invocation of app_process (through android-emacs) can public ClipboardManager getClipboardManager () { - final Holder manager; + final EmacsHolder manager; Runnable runnable; - manager = new Holder (); + manager = new EmacsHolder (); runnable = new Runnable () { public void commit 5f09df3d6a359fabb2485c83549a0176a0ba2796 Author: Po Lu Date: Tue Jun 6 10:06:10 2023 +0800 Improve undo behavior on Android * lisp/simple.el (undo-auto-amalgamate): Update doc string to describe new amalgamating commands. (analyze-text-conversion): Make this an amalgamating command by default, unless a new line has been inserted. Also, shorten the undo boundary timer. * src/textconv.c (really_commit_text) (really_set_composing_text): Correctly report ephemeral deletions. (syms_of_textconv): Fix doc strings. diff --git a/lisp/simple.el b/lisp/simple.el index 5ae92c6e4a0..62489d8ffcb 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -4092,10 +4092,11 @@ undo-auto-amalgamate "Amalgamate undo if necessary. This function can be called before an amalgamating command. It removes the previous `undo-boundary' if a series of such calls -have been made. By default `self-insert-command' and -`delete-char' are the only amalgamating commands, although this -function could be called by any command wishing to have this -behavior." +have been made. `self-insert-command' and `delete-char' are the +most common amalgamating commands, although this function can be +called by any command which desires this behavior. +`analyze-text-conversion' (which see) is also an amalgamating +command in most circumstances." (let ((last-amalgamating-count (undo-auto--last-boundary-amalgamating-number))) (setq undo-auto--this-command-amalgamating t) @@ -10988,6 +10989,9 @@ text-conversion-edits (defvar electric-pair-preserve-balance) (declare-function electric-pair-analyze-conversion "elec-pair.el") +;; Actually in emacs-lisp/timer.el. +(declare-function timer-set-time "emacs-lisp/timer.el") + (defvar-local post-text-conversion-hook nil "Hook run after text is inserted by an input method. Each function in this list is run until one returns non-nil. @@ -11016,57 +11020,99 @@ analyze-text-conversion can work. - Run `post-text-conversion-hook' with `last-command-event' set - to the last character of any inserted text to finish up." + to the last character of any inserted text to finish up. + +Finally, amalgamate recent changes to the undo list with previous +ones, unless a new line has been inserted or auto-fill has taken +place. If undo information is being recorded, make sure +`undo-auto-current-boundary-timer' will run within the next 5 +seconds." (interactive) - ;; The list must be processed in reverse. - (dolist (edit (reverse text-conversion-edits)) - ;; Filter out ephemeral edits and deletions. - (when (and (stringp (nth 3 edit))) - (with-current-buffer (car edit) - (if (not (eq (nth 1 edit) (nth 2 edit))) - ;; Process this insertion. (nth 3 edit) is the text which - ;; was inserted. - (let* ((inserted (nth 3 edit)) - ;; Get the first and last characters. - (start (aref inserted 0)) - (end (aref inserted (1- (length inserted)))) - ;; Figure out whether or not to auto-fill. - (auto-fill-p (or (aref auto-fill-chars start) - (aref auto-fill-chars end))) - ;; Figure out whether or not a newline was inserted. - (newline-p (string-search "\n" inserted)) - ;; FIXME: this leads to an error in - ;; `atomic-change-group', seemingly because - ;; buffer-undo-list is being modified or - ;; prematurely truncated. Turn it off for now. - (electric-pair-preserve-balance nil)) + (let ((any-nonephemeral nil)) + ;; The list must be processed in reverse. + (dolist (edit (reverse text-conversion-edits)) + ;; Filter out ephemeral edits and deletions after point. Here, we + ;; are only interested in insertions or deletions whose contents + ;; can be identified. + (when (stringp (nth 3 edit)) + (with-current-buffer (car edit) + (if (not (eq (nth 1 edit) (nth 2 edit))) + ;; Process this insertion. (nth 3 edit) is the text which + ;; was inserted. + (let* ((inserted (nth 3 edit)) + ;; Get the first and last characters. + (start (aref inserted 0)) + (end (aref inserted (1- (length inserted)))) + ;; Figure out whether or not to auto-fill. + (auto-fill-p (or (aref auto-fill-chars start) + (aref auto-fill-chars end))) + ;; Figure out whether or not a newline was inserted. + (newline-p (string-search "\n" inserted)) + ;; Save the current undo list to figure out + ;; whether or not auto-fill has actually taken + ;; place. + (old-undo-list buffer-undo-list) + ;; FIXME: this leads to an error in + ;; `atomic-change-group', seemingly because + ;; buffer-undo-list is being modified or + ;; prematurely truncated. Turn it off for now. + (electric-pair-preserve-balance nil)) + (save-excursion + (if (and auto-fill-function newline-p) + (progn (goto-char (nth 2 edit)) + (previous-logical-line) + (funcall auto-fill-function)) + (when (and auto-fill-function auto-fill-p) + (progn (goto-char (nth 2 edit)) + (funcall auto-fill-function)))) + ;; Record whether or not this edit should result in + ;; an undo boundary being added. + (setq any-nonephemeral + (or any-nonephemeral newline-p + ;; See if auto-fill has taken place by + ;; comparing the current undo list with + ;; the saved head. + (not (eq old-undo-list + buffer-undo-list))))) + (goto-char (nth 2 edit)) + (let ((last-command-event end)) + (unless (run-hook-with-args-until-success + 'post-text-conversion-hook) + (run-hooks 'post-self-insert-hook)))) + ;; Process this deletion before point. (nth 2 edit) is the + ;; text which was deleted. Input methods typically prefer + ;; to edit words instead of deleting characters off their + ;; ends, but they seem to always send proper requests for + ;; deletion for punctuation. + (when (and (boundp 'electric-pair-delete-adjacent-pairs) + (symbol-value 'electric-pair-delete-adjacent-pairs) + ;; Make sure elec-pair is loaded. + (fboundp 'electric-pair-analyze-conversion) + ;; Only do this if only a single edit happened. + text-conversion-edits) (save-excursion - (if (and auto-fill-function newline-p) - (progn (goto-char (nth 2 edit)) - (previous-logical-line) - (funcall auto-fill-function)) - (when (and auto-fill-function auto-fill-p) - (progn (goto-char (nth 2 edit)) - (funcall auto-fill-function))))) - (goto-char (nth 2 edit)) - (let ((last-command-event end)) - (unless (run-hook-with-args-until-success - 'post-text-conversion-hook) - (run-hooks 'post-self-insert-hook)))) - ;; Process this deletion before point. (nth 2 edit) is the - ;; text which was deleted. Input methods typically prefer - ;; to edit words instead of deleting characters off their - ;; ends, but they seem to always send proper requests for - ;; deletion for punctuation. - (when (and (boundp 'electric-pair-delete-adjacent-pairs) - (symbol-value 'electric-pair-delete-adjacent-pairs) - ;; Make sure elec-pair is loaded. - (fboundp 'electric-pair-analyze-conversion) - ;; Only do this if only a single edit happened. - text-conversion-edits) - (save-excursion - (goto-char (nth 2 edit)) - (electric-pair-analyze-conversion (nth 3 edit))))))))) + (goto-char (nth 2 edit)) + (electric-pair-analyze-conversion (nth 3 edit)))))))) + ;; If all edits were ephemeral, make this an amalgamating command. + ;; Then, make sure that an undo boundary is placed within the next + ;; five seconds. + (unless any-nonephemeral + (undo-auto-amalgamate) + (let ((timer undo-auto-current-boundary-timer)) + (if timer + ;; The timer is already running. See if it's due to expire + ;; within the next five seconds. + (let ((time (list (aref timer 1) (aref timer 2) + (aref timer 3)))) + (unless (<= (time-convert (time-subtract time nil) + 'integer) + 5) + ;; It's not, so make it run in 5 seconds. + (timer-set-time undo-auto-current-boundary-timer + (time-add nil 5)))) + ;; Otherwise, start it for five seconds from now. + (setq undo-auto-current-boundary-timer + (run-at-time 5 nil #'undo-auto--boundary-timer))))))) diff --git a/src/textconv.c b/src/textconv.c index 0716cf7edbb..0dcf5bdcea8 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -600,7 +600,7 @@ really_commit_text (struct frame *f, EMACS_INT position, /* Now delete whatever needs to go. */ del_range (start, end); - record_buffer_change (start, start, Qnil); + record_buffer_change (start, start, Qt); /* Don't record changes if TEXT is empty. */ @@ -786,7 +786,7 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, { del_range (start, end); set_point (start); - record_buffer_change (start, start, Qnil); + record_buffer_change (start, start, Qt); } /* Now set the markers which denote the composition region. */ @@ -808,14 +808,14 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, set_point (start); if (start != end) - record_buffer_change (start, start, Qnil); + record_buffer_change (start, start, Qt); } /* Insert the new text. */ Finsert (1, &text); if (start != PT) - record_buffer_change (start, PT, Qnil); + record_buffer_change (start, PT, Qt); /* Now move point to an appropriate location. */ if (position <= 0) @@ -1927,27 +1927,24 @@ syms_of_textconv (void) doc: /* List of buffers that were last edited as a result of text conversion. This list can be used while handling a `text-conversion' event to -determine the changes which have taken place. +determine which changes have taken place. Each element of the list describes a single edit in a buffer, of the form: (BUFFER BEG END EPHEMERAL) -If an insertion or a change occured, then BEG and END are markers -which denote the bounds of the text that was changed or inserted. - -If EPHEMERAL is t, then the input method will shortly make more -changes to the text, so any actions that would otherwise be taken -(such as indenting or automatically filling text) should not take -place; otherwise, it is a string describing the text which was -inserted. +If an insertion or an edit to the buffer text is described, then BEG +and END are markers which denote the bounds of the text that was +changed or inserted. If a deletion is described, then BEG and END are +the same object. -If a deletion occured before point, then BEG and END are the same -object, and EPHEMERAL is the text which was deleted. +If EPHEMERAL is t, then the input method is preparing to make further +edits to the text, so any actions that would otherwise be taken, such +as indenting or automatically filling text, should not take place. -If a deletion occured after point, then BEG and END are also the same -object, but EPHEMERAL is nil. +Otherwise, it is either a string containing text that was inserted, +text deleted before point, or nil if text was deleted after point. The list contents are ordered later edits first, so you must iterate through the list in reverse. */); commit 49ae645624740936421ec4bb814c4dc65f6a06e7 Merge: c4a89851ed9 7ca1d782f59 Author: Po Lu Date: Tue Jun 6 07:53:41 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit c4a89851ed949e311d8b84eda413c4ba440485c4 Author: Po Lu Date: Mon Jun 5 11:34:18 2023 +0800 Clear batch edit state once a new input connection is established * src/androidterm.c (android_handle_ime_event): Clear batch edit state, in case the previous input method forgot to do so. diff --git a/src/androidterm.c b/src/androidterm.c index afa10a1b94b..2a054715d6a 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -725,6 +725,16 @@ android_handle_ime_event (union android_event *event, struct frame *f) finish_composing_text (f, event->ime.counter, event->ime.length == 1); + + if (event->ime.length == 2) + { + /* Now cancel outstanding batch edits if a new input method + has connected. */ + + f->conversion.batch_edit_flags = 0; + f->conversion.batch_edit_count = 0; + } + break; case ANDROID_IME_SET_COMPOSING_TEXT: commit 66783af554176c68cb58726aeff4ae6a23224234 Author: Po Lu Date: Mon Jun 5 10:38:25 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New function clearInputFlags. * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): Stop reporting changes after a new input method connection is established. * src/androidterm.c (android_handle_ime_event): Implement that change. (JNICALL): New function. diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index f03736fe614..cb1c6caa79a 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -221,6 +221,7 @@ public static native ExtractedText getExtractedText (short window, int flags); public static native void requestSelectionUpdate (short window); public static native void requestCursorUpdates (short window, int mode); + public static native void clearInputFlags (short window); /* Return the current value of the selection, or -1 upon diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index c223dfa7911..a78dec08839 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -627,6 +627,10 @@ else if (child.getVisibility () != GONE) return null; } + /* Reset flags set by the previous input method. */ + + EmacsNative.clearInputFlags (window.handle); + /* Obtain the current position of point and set it as the selection. Don't do this under one specific situation: if `android_update_ic' is being called in the main thread, trying @@ -663,10 +667,6 @@ else if (child.getVisibility () != GONE) if (inputConnection == null) inputConnection = new EmacsInputConnection (this); - else - /* Reset the composing region, in case there is still composing - text. */ - inputConnection.finishComposingText (); /* Return the input connection. */ return inputConnection; diff --git a/src/androidterm.c b/src/androidterm.c index d8a8b4c2d71..afa10a1b94b 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -675,6 +675,7 @@ android_request_cursor_updates (struct frame *f, int mode) android_handle_ime_event (union android_event *event, struct frame *f) { Lisp_Object text UNINIT; + struct android_output *output; /* First, decode the text if necessary. */ @@ -707,8 +708,23 @@ android_handle_ime_event (union android_event *event, struct frame *f) break; case ANDROID_IME_FINISH_COMPOSING_TEXT: + + if (event->ime.length == 2) + { + output = FRAME_ANDROID_OUTPUT (f); + + /* A new input method has connected to Emacs. Stop + reporting changes that the previous input method has + asked to monitor. */ + + output->extracted_text_flags = 0; + output->extracted_text_token = 0; + output->extracted_text_hint = 0; + output->need_cursor_updates = false; + } + finish_composing_text (f, event->ime.counter, - event->ime.length); + event->ime.length == 1); break; case ANDROID_IME_SET_COMPOSING_TEXT: @@ -5580,6 +5596,36 @@ NATIVE_NAME (requestCursorUpdates) (JNIEnv *env, jobject object, android_write_event (&event); } +/* Notice that a new input method connection has been initialized and + clear cursor update requests, extracted text requests, and the + composing region. */ + +JNIEXPORT void JNICALL +NATIVE_NAME (clearInputFlags) (JNIEnv *env, jobject object, + jshort window) +{ + JNI_STACK_ALIGNMENT_PROLOGUE; + + union android_event event; + + event.ime.type = ANDROID_INPUT_METHOD; + event.ime.serial = ++event_serial; + event.ime.window = window; + event.ime.operation = ANDROID_IME_FINISH_COMPOSING_TEXT; + event.ime.start = 0; + event.ime.end = 0; + + /* This value of `length' means that updates to the cursor position + and extracted text should not be reported anymore. */ + + event.ime.length = 2; + event.ime.position = 0; + event.ime.text = NULL; + event.ime.counter = ++edit_counter; + + android_write_event (&event); +} + #ifdef __clang__ #pragma clang diagnostic pop #else commit 6f0ebe11aaf9b2e54df14147cd2f62b048ffec9a Merge: 835ac18a76a 6058b4559d4 Author: Po Lu Date: Mon Jun 5 08:46:58 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 835ac18a76a30e2a5142e6e643e858bb05e83642 Author: Po Lu Date: Sun Jun 4 20:11:40 2023 +0800 ; * src/keyboard.c: Fix build without window system diff --git a/src/keyboard.c b/src/keyboard.c index ec9a6dc712a..116846611ce 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -5875,6 +5875,8 @@ coords_in_menu_bar_window (struct frame *f, int x, int y) #endif +#ifdef HAVE_WINDOW_SYSTEM + /* Return whether or not the coordinates X and Y are inside the tab-bar window of the given frame F. */ @@ -5894,6 +5896,8 @@ coords_in_tab_bar_window (struct frame *f, int x, int y) && x <= WINDOW_RIGHT_EDGE_X (window)); } +#endif /* HAVE_WINDOW_SYSTEM */ + /* Given a struct input_event, build the lisp event which represents it. If EVENT is 0, build a mouse movement event from the mouse movement buffer, which should have a movement event in it. commit 783a8354e9634673d276c0ca8f773c5f0970447a Author: Po Lu Date: Sun Jun 4 15:11:48 2023 +0800 * configure.ac: Tune pty detection for Android. diff --git a/configure.ac b/configure.ac index aa5fcea732b..7454e201c3f 100644 --- a/configure.ac +++ b/configure.ac @@ -6575,7 +6575,26 @@ AC_DEFUN AC_DEFINE([FIRST_PTY_LETTER], ['p']) ;; - gnu-linux | gnu-kfreebsd | dragonfly | freebsd | openbsd | netbsd | darwin | nacl | android ) + android ) + AC_DEFINE([PTY_ITERATION], [int i; for (i = 0; i < 1; ++i)]) + dnl grantpt may be defined in libc but not declared. The same + dnl goes for posix_openpt. When that is the case, it means that + dnl grantpt or posix_openpt cannot actually be used. + AC_CHECK_DECLS([grantpt, posix_openpt]) + AS_IF([test "x$ac_cv_have_decl_grantpt" = xyes], + [AC_DEFINE([PTY_TTY_NAME_SPRINTF], + [{ char *ptyname = 0; sigset_t blocked; sigemptyset (&blocked); sigaddset (&blocked, SIGCHLD); pthread_sigmask (SIG_BLOCK, &blocked, 0); if (grantpt (fd) != -1 && unlockpt (fd) != -1) ptyname = ptsname(fd); pthread_sigmask (SIG_UNBLOCK, &blocked, 0); if (!ptyname) { emacs_close (fd); return -1; } snprintf (pty_name, PTY_NAME_SIZE, "%s", ptyname); }])], + [AC_DEFINE([PTY_TTY_NAME_SPRINTF], + [{ char *ptyname = 0; sigset_t blocked; sigemptyset (&blocked); sigaddset (&blocked, SIGCHLD); pthread_sigmask (SIG_BLOCK, &blocked, 0); if (unlockpt (fd) != -1) ptyname = ptsname(fd); pthread_sigmask (SIG_UNBLOCK, &blocked, 0); if (!ptyname) { emacs_close (fd); return -1; } snprintf (pty_name, PTY_NAME_SIZE, "%s", ptyname); }])]) + AS_IF([test "x$ac_cv_have_decl_posix_openpt" = xyes], + [AC_DEFINE([PTY_OPEN], + [do { fd = posix_openpt (O_RDWR | O_CLOEXEC | O_NOCTTY); if (fd < 0 && errno == EINVAL) fd = posix_openpt (O_RDWR | O_NOCTTY); } while (false)]) + AC_DEFINE([PTY_NAME_SPRINTF], [])], + [AC_DEFINE([PTY_NAME_SPRINTF], []) + AC_DEFINE([PTY_OPEN], [fd = getpt ()])]) + ;; + + gnu-linux | gnu-kfreebsd | dragonfly | freebsd | openbsd | netbsd | darwin | nacl ) dnl if HAVE_GRANTPT if test "x$ac_cv_func_grantpt" = xyes; then AC_DEFINE([UNIX98_PTYS], [1], [Define if the system has Unix98 PTYs.]) commit 740af4668c8d9bc8e4ee1e60ebeb366690fee93e Author: Po Lu Date: Sun Jun 4 12:04:15 2023 +0800 Fix input method synchronization problems * java/debug.sh (gdbserver_cmd, is_root): Prefer TCP again. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New function `queryAndSpin'. * java/org/gnu/emacs/EmacsService.java (EmacsService) (icBeginSynchronous, icEndSynchronous, viewGetSelection): New synchronization functions. (resetIC, updateCursorAnchorInfo): Call those instead. * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): Call viewGetSelection. * src/android.c (JNICALL, android_answer_query_spin): New functions. diff --git a/java/debug.sh b/java/debug.sh index 0458003fe72..d6e439bec90 100755 --- a/java/debug.sh +++ b/java/debug.sh @@ -19,7 +19,6 @@ ## along with GNU Emacs. If not, see . set -m -set -x oldpwd=`pwd` cd `dirname $0` @@ -273,7 +272,7 @@ num_pids= gdbserver_cmd= is_root= if [ -z "$gdbserver" ]; then - gdbserver_bin=/system/bin/gdbserver + gdbserver_bin=/system/bin/gdbserver64 else gdbserver_bin=/data/local/tmp/gdbserver gdbserver_cat="cat $gdbserver_bin | run-as $package sh -c \ @@ -312,12 +311,12 @@ is_root= if [ -z "$gdbserver" ]; then if [ "$is_root" = "yes" ]; then adb -s $device shell $gdbserver_bin --multi \ - "+/data/local/tmp/debug.$package.socket" --attach $pid >&5 & - gdb_socket="localfilesystem:/data/local/tmp/debug.$package.socket" + "0.0.0.0:7564" --attach $pid >&5 & + gdb_socket="tcp:7564" else - adb -s $device shell run-as $package $gdbserver_bin --multi \ - "+debug.$package.socket" --attach $pid >&5 & - gdb_socket="localfilesystem:$app_data_dir/debug.$package.socket" + adb -s $device shell $gdbserver_bin --multi \ + "0.0.0.0:7564" --attach $pid >&5 & + gdb_socket="tcp:7564" fi else # Normally the program cannot access $gdbserver_bin when it is diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index eb75201088b..f03736fe614 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -177,6 +177,12 @@ public static native long sendExpose (short window, int x, int y, main thread's looper to respond. */ public static native void endSynchronous (); + /* Prevent deadlocks while reliably allowing queries from the Emacs + thread to the main thread to complete by waiting for a query to + start from the main thread, then answer it; assume that a query + is certain to start shortly. */ + public static native void answerQuerySpin (); + /* Return whether or not KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP and KEYCODE_VOLUME_MUTE should be forwarded to Emacs. */ public static native boolean shouldForwardMultimediaButtons (); diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index dde60e1c5af..6d70536faf0 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -25,6 +25,8 @@ import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + import android.graphics.Matrix; import android.graphics.Point; @@ -106,8 +108,17 @@ public final class EmacsService extends Service performing drawing calls. */ private static final boolean DEBUG_THREADS = false; - /* Whether or not onCreateInputMethod is calling getSelection. */ - public static volatile boolean imSyncInProgress; + /* Atomic integer used for synchronization between + icBeginSynchronous/icEndSynchronous and viewGetSelection. + + Value is 0 if no query is in progress, 1 if viewGetSelection is + being called, and 2 if icBeginSynchronous was called. */ + public static final AtomicInteger servicingQuery; + + static + { + servicingQuery = new AtomicInteger (); + }; /* Return the directory leading to the directory in which native library files are stored on behalf of CONTEXT. */ @@ -658,46 +669,79 @@ invocation of app_process (through android-emacs) can EmacsNative.endSynchronous (); } + + + /* IMM functions such as `updateSelection' holds an internal lock + that is also taken before `onCreateInputConnection' (in + EmacsView.java) is called; when that then asks the UI thread for + the current selection, a dead lock results. To remedy this, + reply to any synchronous queries now -- and prohibit more queries + for the duration of `updateSelection' -- if EmacsView may have + been asking for the value of the region. */ + + public static void + icBeginSynchronous () + { + /* Set servicingQuery to 2, so viewGetSelection knows it shouldn't + proceed. */ + + if (servicingQuery.getAndSet (2) == 1) + /* But if viewGetSelection is already in progress, answer it + first. */ + EmacsNative.answerQuerySpin (); + } + + public static void + icEndSynchronous () + { + if (servicingQuery.getAndSet (0) != 2) + throw new RuntimeException ("incorrect value of `servicingQuery': " + + "likely 1"); + } + + public static int[] + viewGetSelection (short window) + { + int[] selection; + + /* See if a query is already in progress from the other + direction. */ + if (!servicingQuery.compareAndSet (0, 1)) + return null; + + /* Now call the regular getSelection. Note that this can't race + with answerQuerySpin, as `android_servicing_query' can never be + 2 when icBeginSynchronous is called, so a query will always be + started. */ + selection = EmacsNative.getSelection (window); + + /* Finally, clear servicingQuery if its value is still 1. If a + query has started from the other side, it ought to be 2. */ + + servicingQuery.compareAndSet (1, 0); + return selection; + } + + + public void updateIC (EmacsWindow window, int newSelectionStart, int newSelectionEnd, int composingRegionStart, int composingRegionEnd) { - boolean wasSynchronous; - if (DEBUG_IC) Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart + " " + newSelectionEnd + " " + composingRegionStart + " " + composingRegionEnd)); - /* `updateSelection' holds an internal lock that is also taken - before `onCreateInputConnection' (in EmacsView.java) is called; - when that then asks the UI thread for the current selection, a - dead lock results. To remedy this, reply to any synchronous - queries now -- and prohibit more queries for the duration of - `updateSelection' -- if EmacsView may have been asking for the - value of the region. */ - - wasSynchronous = false; - if (EmacsService.imSyncInProgress) - { - /* `beginSynchronous' will answer any outstanding queries and - signal that one is now in progress, thereby preventing - `getSelection' from blocking. */ - - EmacsNative.beginSynchronous (); - wasSynchronous = true; - } - + icBeginSynchronous (); window.view.imManager.updateSelection (window.view, newSelectionStart, newSelectionEnd, composingRegionStart, composingRegionEnd); - - if (wasSynchronous) - EmacsNative.endSynchronous (); + icEndSynchronous (); } public void @@ -707,7 +751,10 @@ invocation of app_process (through android-emacs) can Log.d (TAG, "resetIC: " + window); window.view.setICMode (icMode); + + icBeginSynchronous (); window.view.imManager.restartInput (window.view); + icEndSynchronous (); } public void @@ -733,11 +780,15 @@ invocation of app_process (through android-emacs) can 0); info = builder.build (); + + if (DEBUG_IC) Log.d (TAG, ("updateCursorAnchorInfo: " + x + " " + y + " " + yBaseline + "-" + yBottom)); + icBeginSynchronous (); window.view.imManager.updateCursorAnchorInfo (window.view, info); + icEndSynchronous (); } /* Open a content URI described by the bytes BYTES, a non-terminated diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index bb450bb8e6b..c223dfa7911 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -630,12 +630,11 @@ else if (child.getVisibility () != GONE) /* Obtain the current position of point and set it as the selection. Don't do this under one specific situation: if `android_update_ic' is being called in the main thread, trying - to synchronize with it can cause a dead lock in the IM - manager. */ + to synchronize with it can cause a dead lock in the IM manager. + See icBeginSynchronous in EmacsService.java for more + details. */ - EmacsService.imSyncInProgress = true; - selection = EmacsNative.getSelection (window.handle); - EmacsService.imSyncInProgress = false; + selection = EmacsService.viewGetSelection (window.handle); if (selection != null) Log.d (TAG, "onCreateInputConnection: current selection is: " @@ -664,6 +663,10 @@ else if (child.getVisibility () != GONE) if (inputConnection == null) inputConnection = new EmacsInputConnection (this); + else + /* Reset the composing region, in case there is still composing + text. */ + inputConnection.finishComposingText (); /* Return the input connection. */ return inputConnection; diff --git a/src/android.c b/src/android.c index e74d40a0cdb..f59c0d9e5d2 100644 --- a/src/android.c +++ b/src/android.c @@ -2997,6 +2997,7 @@ NATIVE_NAME (blitRect) (JNIEnv *env, jobject object, static void android_begin_query (void); static void android_end_query (void); +static void android_answer_query_spin (void); JNIEXPORT void JNICALL NATIVE_NAME (beginSynchronous) (JNIEnv *env, jobject object) @@ -3014,6 +3015,14 @@ NATIVE_NAME (endSynchronous) (JNIEnv *env, jobject object) android_end_query (); } +JNIEXPORT void JNICALL +NATIVE_NAME (answerQuerySpin) (JNIEnv *env, jobject object) +{ + JNI_STACK_ALIGNMENT_PROLOGUE; + + android_answer_query_spin (); +} + #ifdef __clang__ #pragma clang diagnostic pop #else @@ -7041,6 +7050,23 @@ android_answer_query (void) sem_post (&android_query_sem); } +/* Like `android_answer_query'. However, the query may not have + begun; spin until it has. */ + +static void +android_answer_query_spin (void) +{ + int n; + + while (!(n = __atomic_load_n (&android_servicing_query, + __ATOMIC_SEQ_CST))) + eassert (!n); + + /* Note that this function is supposed to be called before + `android_begin_query' starts, so clear the service flag. */ + android_check_query (); +} + /* Notice that the Emacs thread will start blocking waiting for a response from the UI thread. Process any pending queries from the UI thread. commit c389df992a9f054f9aced4f9a267730b2221e03a Merge: a4bf3ad9586 6847c01568e Author: Po Lu Date: Sun Jun 4 08:33:30 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit a4bf3ad95869771afb4b8c64d14fa145348e4d2b Author: Po Lu Date: Sat Jun 3 15:08:05 2023 +0800 Fix typos in Android port * lisp/bindings.el (global-map): Bind cut, copy and paste. * src/androidterm.c (JNICALL): Use key. diff --git a/lisp/bindings.el b/lisp/bindings.el index eec51a4e413..75d22bf9b25 100644 --- a/lisp/bindings.el +++ b/lisp/bindings.el @@ -1202,6 +1202,10 @@ global-map (define-key global-map [insertchar] 'overwrite-mode) (define-key global-map [C-insertchar] 'kill-ring-save) (define-key global-map [S-insertchar] 'yank) +;; The next three keys are used on MS Windows and Android. +(define-key global-map [copy] 'kill-ring-save) +(define-key global-map [paste] 'yank) +(define-key global-map [cut] 'kill-region) (define-key global-map [undo] 'undo) (define-key global-map [redo] 'repeat-complex-command) (define-key global-map [again] 'repeat-complex-command) ; Sun keyboard diff --git a/src/androidterm.c b/src/androidterm.c index 2894a58e6b9..d8a8b4c2d71 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5217,6 +5217,7 @@ NATIVE_NAME (performContextMenuAction) (JNIEnv *env, jobject object, case 0: /* android.R.id.selectAll */ case 1: /* android.R.id.startSelectingText */ case 2: /* android.R.id.stopSelectingText */ + default: /* These actions are not implemented. */ return; @@ -5231,9 +5232,6 @@ NATIVE_NAME (performContextMenuAction) (JNIEnv *env, jobject object, case 5: /* android.R.id.paste */ key = 279; break; - - default: - emacs_abort (); } event.xkey.type = ANDROID_KEY_PRESS; @@ -5241,7 +5239,7 @@ NATIVE_NAME (performContextMenuAction) (JNIEnv *env, jobject object, event.xkey.window = window; event.xkey.time = 0; event.xkey.state = 0; - event.xkey.keycode = 66; + event.xkey.keycode = key; event.xkey.unicode_char = 0; event.xkey.counter = ++edit_counter; commit c308accc6d0f45cc64bb664fcc43a33a7ab015b3 Author: Po Lu Date: Sat Jun 3 11:25:01 2023 +0800 Behave correctly when IMEs commit or compose text with active mark * src/textconv.c (really_commit_text) (really_set_composing_text): Delete text between mark and point if the mark is active. Don't record changes if the text is empty. diff --git a/src/textconv.c b/src/textconv.c index e082eb6ee22..0716cf7edbb 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -545,7 +545,10 @@ restore_selected_window (Lisp_Object window) /* Commit the given text in the composing region. If there is no composing region, then insert the text after F's selected window's - last point instead. Finally, remove the composing region. + last point instead, unless the mark is active. Finally, remove the + composing region. + + If the mark is active, delete the text between mark and point. Then, move point to POSITION relative to TEXT. If POSITION is greater than zero, it is relative to the character at the end of @@ -556,7 +559,7 @@ really_commit_text (struct frame *f, EMACS_INT position, Lisp_Object text) { specpdl_ref count; - ptrdiff_t wanted, start, end; + ptrdiff_t wanted, start, end, mark; struct window *w; /* If F's old selected window is no longer live, fail. */ @@ -572,28 +575,48 @@ really_commit_text (struct frame *f, EMACS_INT position, redisplay. */ select_window (f->old_selected_window, Qt); - /* Now detect whether or not there is a composing region. + /* Now detect whether or not there is a composing or active region. If there is, then replace it with TEXT. Don't do that otherwise. */ - if (MARKERP (f->conversion.compose_region_start)) + mark = get_mark (); + if (MARKERP (f->conversion.compose_region_start) || mark != -1) { - /* Replace its contents. */ - start = marker_position (f->conversion.compose_region_start); - end = marker_position (f->conversion.compose_region_end); + /* Replace its contents. Set START and END to the start and end + of the composing region if it exists. */ + + if (MARKERP (f->conversion.compose_region_start)) + { + start = marker_position (f->conversion.compose_region_start); + end = marker_position (f->conversion.compose_region_end); + } + else + { + /* Otherwise, set it to the start and end of the region. */ + start = min (mark, PT); + end = max (mark, PT); + } + + /* Now delete whatever needs to go. */ + del_range (start, end); record_buffer_change (start, start, Qnil); - Finsert (1, &text); - record_buffer_change (start, PT, text); - /* Move to a the position specified in POSITION. If POSITION is - less than zero, it is relative to the start of the text that - was inserted. */ + /* Don't record changes if TEXT is empty. */ + + if (SCHARS (text)) + { + Finsert (1, &text); + record_buffer_change (start, PT, text); + } + + /* Move to a the position specified in POSITION. */ if (position <= 0) { - wanted - = marker_position (f->conversion.compose_region_start); + /* If POSITION is less than zero, it is relative to the + start of the text that was inserted. */ + wanted = start; if (INT_ADD_WRAPV (wanted, position, &wanted) || wanted < BEGV) @@ -608,9 +631,7 @@ really_commit_text (struct frame *f, EMACS_INT position, { /* Otherwise, it is relative to the last character in TEXT. */ - - wanted - = marker_position (f->conversion.compose_region_end); + wanted = PT; if (INT_ADD_WRAPV (wanted, position - 1, &wanted) || wanted > ZV) @@ -642,8 +663,14 @@ really_commit_text (struct frame *f, EMACS_INT position, /* Otherwise, move the text and point to an appropriate location. */ wanted = PT; - Finsert (1, &text); - record_buffer_change (wanted, PT, text); + + /* Don't record changes if TEXT is empty. */ + + if (SCHARS (text)) + { + Finsert (1, &text); + record_buffer_change (start, PT, text); + } if (position <= 0) { @@ -737,6 +764,32 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, if (!MARKERP (f->conversion.compose_region_start)) { + /* Set START and END. */ + start = PT; + wanted = end = get_mark (); + + /* If END is -1, set it to start. */ + + if (end == -1) + end = start; + else + { + /* Now sort start and end. */ + start = min (start, end); + end = max (PT, wanted); + } + + /* If END is not the same as start, delete the text in + between. */ + + if (end != start) + { + del_range (start, end); + set_point (start); + record_buffer_change (start, start, Qnil); + } + + /* Now set the markers which denote the composition region. */ f->conversion.compose_region_start = build_marker (current_buffer, PT, PT_BYTE); f->conversion.compose_region_end @@ -744,8 +797,6 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, Fset_marker_insertion_type (f->conversion.compose_region_end, Qt); - - start = PT; } else { @@ -1368,6 +1419,9 @@ end_batch_edit (struct frame *f, unsigned long counter) /* Insert the specified STRING into F's current buffer's composition region, and set point to POSITION relative to STRING. + If there is no composition region, use the active region instead. + If that doesn't exist either, insert STRING after point. + COUNTER means the same as in `start_batch_edit'. */ void @@ -1415,11 +1469,11 @@ finish_composing_text (struct frame *f, unsigned long counter, /* Insert the given STRING and make it the currently active composition. - If there is currently no composing region, then the new value of - point is used as the composing region. + If there is currently no composing or active region, then the new + value of point is used as the composing region. - Then, the composing region is replaced with the text in the - specified string. + Then, the composing or active region is replaced with the text in + the specified string. Finally, move point to new_point, which is relative to either the start or the end of OBJECT depending on whether or not it is less commit a696ed5c30fb9a7ba2d37f0e691de17872fdab16 Author: Po Lu Date: Sat Jun 3 10:16:30 2023 +0800 Update Android port * src/androidterm.c (struct android_get_extracted_text_context): New field `mark_active'. (android_get_extracted_text): Set that field. (struct android_extracted_text_class): New field `flags'. (android_build_extracted_text): New argument `mark_active'. Set flags appropriately. (NATIVE_NAME, android_update_selection): Likewise. * src/textconv.c (get_extracted_text): New argument `mark_active'. Set it if the mark is active. * src/textconv.h: Update prototypes. diff --git a/src/androidterm.c b/src/androidterm.c index 211faabf5c2..2894a58e6b9 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5274,10 +5274,13 @@ NATIVE_NAME (performContextMenuAction) (JNIEnv *env, jobject object, /* The window. */ android_window window; + + /* Whether or not the mark is active. */ + bool mark_active; }; /* Return the extracted text in the extracted text context specified - by DATA. */ + by DATA. Save its flags and token into its frame's state. */ static void android_get_extracted_text (void *data) @@ -5298,7 +5301,7 @@ android_get_extracted_text (void *data) = get_extracted_text (f, min (request->hint_max_chars, 600), &request->start, &request->start_offset, &request->end_offset, &request->length, - &request->bytes); + &request->bytes, &request->mark_active); /* See if request->flags & GET_EXTRACTED_TEXT_MONITOR. If so, then the input method has asked to monitor changes to the extracted @@ -5326,6 +5329,7 @@ android_get_extracted_text (void *data) { jclass class; jmethodID constructor; + jfieldID flags; jfieldID partial_start_offset; jfieldID partial_end_offset; jfieldID selection_start; @@ -5345,7 +5349,8 @@ android_get_extracted_text (void *data) TEXT. START is a character position describing the offset of the first character in TEXT. START_OFFSET is the offset of the lesser of point or mark relative to START, and END_OFFSET is that of the - greater of point or mark relative to START. + greater of point or mark relative to START. MARK_ACTIVE specifies + whether or not the mark is currently active. Assume that request_class and text_class have already been initialized. @@ -5356,7 +5361,7 @@ android_get_extracted_text (void *data) static jobject android_build_extracted_text (jstring text, ptrdiff_t start, ptrdiff_t start_offset, - ptrdiff_t end_offset) + ptrdiff_t end_offset, bool mark_active) { JNIEnv *env; jobject object; @@ -5373,6 +5378,9 @@ android_build_extracted_text (jstring text, ptrdiff_t start, if (!object) return NULL; + (*env)->SetIntField (env, object, text_class.flags, + /* ExtractedText.FLAG_SELECTING */ + mark_active ? 2 : 0); (*env)->SetIntField (env, object, text_class.partial_start_offset, -1); (*env)->SetIntField (env, object, text_class.partial_end_offset, -1); (*env)->SetIntField (env, object, text_class.selection_start, @@ -5432,6 +5440,8 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, = (*env)->NewGlobalRef (env, text_class.class); assert (text_class.class); + text_class.flags + = (*env)->GetFieldID (env, class, "flags", "I"); text_class.partial_start_offset = (*env)->GetFieldID (env, class, "partialStartOffset", "I"); text_class.partial_end_offset @@ -5478,6 +5488,9 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, if (!object) return NULL; + (*env)->SetIntField (env, object, text_class.flags, + /* ExtractedText.FLAG_SELECTING */ + context.mark_active ? 2 : 0); (*env)->SetIntField (env, object, text_class.partial_start_offset, -1); (*env)->SetIntField (env, object, text_class.partial_end_offset, -1); (*env)->SetIntField (env, object, text_class.selection_start, @@ -5591,6 +5604,7 @@ android_update_selection (struct frame *f, struct window *w) char *text; jobject extracted; jstring string; + bool mark_active; if (MARKERP (f->conversion.compose_region_start)) { @@ -5639,7 +5653,7 @@ android_update_selection (struct frame *f, struct window *w) token = FRAME_ANDROID_OUTPUT (f)->extracted_text_token; text = get_extracted_text (f, min (hint, 600), &start, &start_offset, &end_offset, - &length, &bytes); + &length, &bytes, &mark_active); if (text) { @@ -5652,7 +5666,8 @@ android_update_selection (struct frame *f, struct window *w) /* Make extracted text out of that string. */ extracted = android_build_extracted_text (string, start, start_offset, - end_offset); + end_offset, + mark_active); android_exception_check_1 (string); ANDROID_DELETE_LOCAL_REF (string); diff --git a/src/textconv.c b/src/textconv.c index 9003816e191..e082eb6ee22 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -1573,8 +1573,9 @@ textconv_barrier (struct frame *f, unsigned long counter) to the position of the first character returned, *START_OFFSET to the offset of the lesser of mark and point within that text, *END_OFFSET to the greater of mark and point within that text, and - *LENGTH to the actual number of characters returned, and *BYTES to - the actual number of bytes returned. + *LENGTH to the actual number of characters returned, *BYTES to the + actual number of bytes returned, and *MARK_ACTIVE to whether or not + the mark is active. Value is NULL upon failure, and a malloced string upon success. */ @@ -1583,7 +1584,7 @@ get_extracted_text (struct frame *f, ptrdiff_t n, ptrdiff_t *start_return, ptrdiff_t *start_offset, ptrdiff_t *end_offset, ptrdiff_t *length, - ptrdiff_t *bytes) + ptrdiff_t *bytes, bool *mark_active) { specpdl_ref count; ptrdiff_t start, end, start_byte, end_byte, mark; @@ -1657,9 +1658,13 @@ get_extracted_text (struct frame *f, ptrdiff_t n, /* Get the mark. If it's not active, use PT. */ mark = get_mark (); + *mark_active = true; if (mark == -1) - mark = PT; + { + mark = PT; + *mark_active = false; + } /* Return the offsets. */ *start_return = start; diff --git a/src/textconv.h b/src/textconv.h index d4d0e9d7227..339cefdba92 100644 --- a/src/textconv.h +++ b/src/textconv.h @@ -142,7 +142,7 @@ #define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0) extern void textconv_barrier (struct frame *, unsigned long); extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, - ptrdiff_t *); + ptrdiff_t *, bool *); extern bool conversion_disabled_p (void); extern void register_textconv_interface (struct textconv_interface *); commit d84cbcb8ce970d8a54e164a5ef6f0074c0e59f91 Merge: f16a3f24408 f947a0219bb Author: Po Lu Date: Sat Jun 3 08:55:45 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit f16a3f244080081a4e453424ffdc007d125e4f63 Author: Po Lu Date: Sat Jun 3 08:54:42 2023 +0800 * etc/MACHINES: Fix reference to obsolete file. diff --git a/etc/MACHINES b/etc/MACHINES index c9fdc6cea96..751d59932c5 100644 --- a/etc/MACHINES +++ b/etc/MACHINES @@ -141,7 +141,7 @@ the list at the end of this file. all supported versions of Android on all supported machines: arm, armv7, arm64, x86, x86_64, and mips. - See the file INSTALL.android for detailed installation instructions. + See the file java/INSTALL for detailed installation instructions. * Obsolete platforms commit f396763818905e515c177378abc5f3ebcd7ddc98 Author: Po Lu Date: Fri Jun 2 16:24:42 2023 +0800 Improve Eldoc text conversion support * lisp/emacs-lisp/eldoc.el: ("back-to-indentation"): Register touch screen and text conversion commands. diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el index 18d3eb37af3..3a786bb321b 100644 --- a/lisp/emacs-lisp/eldoc.el +++ b/lisp/emacs-lisp/eldoc.el @@ -991,7 +991,8 @@ eldoc-remove-command-completions "mark-paragraph" "mouse-set-point" "move-" "move-beginning-of-" "move-end-of-" "newline" "next-" "other-window" "pop-global-mark" "previous-" "recenter" "right-" "scroll-" "self-insert-command" - "split-window-" "up-list") + "split-window-" "up-list" "touch-screen-handle-touch" + "analyze-text-conversion") (provide 'eldoc) commit 5dbcc8bd6c1c9188195b748911a0b00cca24cfd6 Author: Po Lu Date: Fri Jun 2 15:41:54 2023 +0800 Improve CC Mode support for text conversion * lisp/progmodes/cc-cmds.el (c-post-text-conversion): New function. * lisp/progmodes/cc-mode.el (c-initialize-cc-mode): Add it as the `post-texxt-conversion-hook'. * lisp/simple.el (post-text-conversion-hook): New hook. (analyze-text-conversion): Run it until success before trying post insert functions. diff --git a/lisp/progmodes/cc-cmds.el b/lisp/progmodes/cc-cmds.el index 4c2340bfc2c..15b103a081f 100644 --- a/lisp/progmodes/cc-cmds.el +++ b/lisp/progmodes/cc-cmds.el @@ -5144,6 +5144,41 @@ c-context-open-line (goto-char here) (delete-char 1)))) + + +;; Text conversion support. + +(defun c-post-text-conversion () + "Notice that the character `last-command-event' has been inserted. +If said character is an electric character such as `*' or `{', delete +it, then call the appropriate CC Mode function to electrically insert +it again." + (cond ((eq last-command-event ?#) + (delete-char -1) + (c-electric-pound nil) t) + ((memq last-command-event '(?{ ?})) + (delete-char -1) + (c-electric-brace nil) t) + ((memq last-command-event '(?\( ?\))) + (delete-char -1) + (c-electric-paren nil) t) + ((eq last-command-event ?*) + (delete-char -1) + (c-electric-star nil) t) + ((eq last-command-event ?/) + (delete-char -1) + (c-electric-slash nil) t) + ((memq last-command-event '(?\; ?,)) + (delete-char -1) + (c-electric-semi&comma nil) t) + ((eq last-command-event ?:) + (delete-char -1) + (c-electric-colon nil) t) + ((memq last-command-event '(?> ?<)) + (delete-char -1) + (c-electric-lt-gt nil) t))) + + (cc-provide 'cc-cmds) diff --git a/lisp/progmodes/cc-mode.el b/lisp/progmodes/cc-mode.el index 11a1d3fe6c2..1364117bdc8 100644 --- a/lisp/progmodes/cc-mode.el +++ b/lisp/progmodes/cc-mode.el @@ -251,7 +251,10 @@ c-initialize-cc-mode (when (fboundp 'electric-indent-local-mode) (add-hook 'electric-indent-mode-hook 'c-electric-indent-mode-hook) (add-hook 'electric-indent-local-mode-hook - 'c-electric-indent-local-mode-hook))) + 'c-electric-indent-local-mode-hook)) + ;; Set up text conversion, for Emacs >= 30.0 + (when (boundp 'post-text-conversion-hook) + (add-hook 'post-text-conversion-hook #'c-post-text-conversion))) ;; Will try initialization hooks again if they failed. (put 'c-initialize-cc-mode initprop c-initialization-ok)))) diff --git a/lisp/simple.el b/lisp/simple.el index 698458c4bc7..d23e2e20c62 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -10980,7 +10980,6 @@ lax-plist-put ;; Text conversion support. See textconv.c for more details about ;; what this is. - ;; Actually in textconv.c. (defvar text-conversion-edits) @@ -10988,6 +10987,12 @@ text-conversion-edits (defvar electric-pair-preserve-balance) (declare-function electric-pair-analyze-conversion "elec-pair.el") +(defvar-local post-text-conversion-hook nil + "Hook run after text is inserted by an input method. +Each function in this list is run until one returns non-nil. +When run, `last-command-event' is bound to the last character +that was inserted by the input method.") + (defun analyze-text-conversion () "Analyze the results of the previous text conversion event. @@ -11007,7 +11012,10 @@ analyze-text-conversion - Run `post-self-insert-functions' for the last character of any inserted text so that modes such as `electric-pair-mode' - can work." + can work. + + - Run `post-text-conversion-hook' with `last-command-event' set + to the last character of any inserted text to finish up." (interactive) ;; The list must be processed in reverse. (dolist (edit (reverse text-conversion-edits)) @@ -11041,7 +11049,9 @@ analyze-text-conversion (funcall auto-fill-function))))) (goto-char (nth 2 edit)) (let ((last-command-event end)) - (run-hooks 'post-self-insert-hook))) + (unless (run-hook-with-args-until-success + 'post-text-conversion-hook) + (run-hooks 'post-self-insert-hook)))) ;; Process this deletion before point. (nth 2 edit) is the ;; text which was deleted. Input methods typically prefer ;; to edit words instead of deleting characters off their commit 189a91bfb699babd936dae48b96d71a332cac8d2 Author: Po Lu Date: Fri Jun 2 13:31:40 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsInputConnection.java (EmacsInputConnection): Apply workarounds on Vivo devices as well. * src/android.c (sendKeyPress, sendKeyRelease): Clear counter. * src/androidgui.h (struct android_key_event): New field `counter'. * src/androidterm.c (handle_one_android_event): Generate barriers as appropriate. (JNICALL): Set `counter'. * src/frame.h (enum text_conversion_operation): * src/textconv.c (detect_conversion_events) (really_set_composing_text, handle_pending_conversion_events_1) (handle_pending_conversion_events, textconv_barrier): * src/textconv.h: Implement text conversion barriers and fix various typos. diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index eb6fd5f2763..9ced7cb7aaf 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -66,11 +66,12 @@ public final class EmacsInputConnection extends BaseInputConnection || Build.MANUFACTURER.equalsIgnoreCase ("Honor")) extractAbsoluteOffsets = syncAfterCommit = true; - /* The Samsung keyboard takes `selectionStart' at face value if - some text is returned, and also searches for words solely - within that text. However, when no text is returned, it falls - back to getTextAfterCursor and getTextBeforeCursor. */ - if (Build.MANUFACTURER.equalsIgnoreCase ("Samsung")) + /* The Samsung and Vivo keyboards take `selectionStart' at face + value if some text is returned, and also searches for words + solely within that text. However, when no text is returned, it + falls back to getTextAfterCursor and getTextBeforeCursor. */ + if (Build.MANUFACTURER.equalsIgnoreCase ("Samsung") + || Build.MANUFACTURER.equalsIgnoreCase ("Vivo")) extractAbsoluteOffsets = true; }; diff --git a/src/android.c b/src/android.c index 94587344eb5..e74d40a0cdb 100644 --- a/src/android.c +++ b/src/android.c @@ -2543,6 +2543,7 @@ NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object, event.xkey.state = state; event.xkey.keycode = keycode; event.xkey.unicode_char = unicode_char; + event.xkey.counter = 0; android_write_event (&event); return event_serial; @@ -2565,6 +2566,7 @@ NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object, event.xkey.state = state; event.xkey.keycode = keycode; event.xkey.unicode_char = unicode_char; + event.xkey.counter = 0; android_write_event (&event); return event_serial; diff --git a/src/androidgui.h b/src/androidgui.h index 02cc73809b9..9e604cdcb8c 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -277,6 +277,10 @@ #define PWinGravity (1L << 9) /* program specified window gravity */ /* If this field is -1, then android_lookup_string should be called to retrieve the associated individual characters. */ unsigned int unicode_char; + + /* If this field is non-zero, a text conversion barrier should be + generated with its value as the counter. */ + unsigned long counter; }; typedef struct android_key_event android_key_pressed_event; diff --git a/src/androidterm.c b/src/androidterm.c index c302e3f2877..211faabf5c2 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -885,6 +885,11 @@ handle_one_android_event (struct android_display_info *dpyinfo, if (!f) goto OTHER; + if (event->xkey.counter) + /* This event was generated by `performEditorAction'. Make + sure it is processed before any subsequent edits. */ + textconv_barrier (f, event->xkey.counter); + wchar_t copy_buffer[129]; wchar_t *copy_bufptr = copy_buffer; int copy_bufsiz = 128 * sizeof (wchar_t); @@ -5178,7 +5183,10 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, android_write_event (&event); - /* Finally, send the return key press. */ + /* Finally, send the return key press. `counter' is set; this means + that a text conversion barrier will be generated once the event + is read, which will cause subsequent edits to wait until the + edits associated with this key press complete. */ event.xkey.type = ANDROID_KEY_PRESS; event.xkey.serial = ++event_serial; @@ -5187,6 +5195,7 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, event.xkey.state = 0; event.xkey.keycode = 66; event.xkey.unicode_char = 0; + event.xkey.counter = ++edit_counter; android_write_event (&event); } @@ -5234,6 +5243,7 @@ NATIVE_NAME (performContextMenuAction) (JNIEnv *env, jobject object, event.xkey.state = 0; event.xkey.keycode = 66; event.xkey.unicode_char = 0; + event.xkey.counter = ++edit_counter; android_write_event (&event); } diff --git a/src/frame.h b/src/frame.h index e2900d1c15b..41b4cd444f6 100644 --- a/src/frame.h +++ b/src/frame.h @@ -89,6 +89,7 @@ #define EMACS_FRAME_H TEXTCONV_SET_POINT_AND_MARK, TEXTCONV_DELETE_SURROUNDING_TEXT, TEXTCONV_REQUEST_POINT_UPDATE, + TEXTCONV_BARRIER, }; /* Structure describing a single edit being performed by the input diff --git a/src/textconv.c b/src/textconv.c index d8166bcfd03..9003816e191 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -36,6 +36,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include "buffer.h" #include "syntax.h" #include "blockinput.h" +#include "keyboard.h" @@ -522,7 +523,11 @@ detect_conversion_events (void) FOR_EACH_FRAME (tail, frame) { - if (XFRAME (frame)->conversion.actions) + /* See if there's a pending edit on this frame. */ + if (XFRAME (frame)->conversion.actions + && ((XFRAME (frame)->conversion.actions->operation + != TEXTCONV_BARRIER) + || (kbd_fetch_ptr == kbd_store_ptr))) return true; } @@ -740,7 +745,7 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, Fset_marker_insertion_type (f->conversion.compose_region_end, Qt); - start = position; + start = PT; } else { @@ -762,7 +767,7 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, record_buffer_change (start, PT, Qnil); /* Now move point to an appropriate location. */ - if (position < 0) + if (position <= 0) { wanted = start; @@ -1198,6 +1203,19 @@ handle_pending_conversion_events_1 (struct frame *f, case TEXTCONV_REQUEST_POINT_UPDATE: really_request_point_update (f); break; + + case TEXTCONV_BARRIER: + if (kbd_fetch_ptr != kbd_store_ptr) + emacs_abort (); + + /* Once a barrier is hit, synchronize F's selected window's + `ephemeral_last_point' with its current point. The reason + for this is because otherwise a previous keyboard event may + have taken place without redisplay happening in between. */ + + if (w) + w->ephemeral_last_point = window_point (w); + break; } /* Signal success. */ @@ -1231,7 +1249,7 @@ handle_pending_conversion_events (void) static int inside; specpdl_ref count; ptrdiff_t last_point; - struct window *w; + struct window *w, *w1; handled = false; @@ -1242,8 +1260,6 @@ handle_pending_conversion_events (void) Vtext_conversion_edits = Qnil; inside++; - last_point = -1; - w = NULL; count = SPECPDL_INDEX (); record_unwind_protect_ptr (decrement_inside, &inside); @@ -1251,6 +1267,8 @@ handle_pending_conversion_events (void) FOR_EACH_FRAME (tail, frame) { f = XFRAME (frame); + last_point = -1; + w = NULL; /* Test if F has any outstanding conversion events. Then process them in bottom to up order. */ @@ -1283,6 +1301,13 @@ handle_pending_conversion_events (void) if (!action) break; + /* If action is a barrier event and the keyboard buffer is + not yet empty, break out of the loop. */ + + if (action->operation == TEXTCONV_BARRIER + && kbd_store_ptr != kbd_fetch_ptr) + break; + /* Unlink this action. */ next = action->next; f->conversion.actions = next; @@ -1515,6 +1540,29 @@ request_point_update (struct frame *f, unsigned long counter) input_pending = true; } +/* Request that text conversion on F pause until the keyboard buffer + becomes empty. + + Use this function to ensure that edits associated with a keyboard + event complete before the text conversion edits after the barrier + take place. */ + +void +textconv_barrier (struct frame *f, unsigned long counter) +{ + struct text_conversion_action *action, **last; + + action = xmalloc (sizeof *action); + action->operation = TEXTCONV_BARRIER; + action->data = Qnil; + action->next = NULL; + action->counter = counter; + for (last = &f->conversion.actions; *last; last = &(*last)->next) + ;; + *last = action; + input_pending = true; +} + /* Return N characters of text around point in F's old selected window. diff --git a/src/textconv.h b/src/textconv.h index e632a9dddcf..d4d0e9d7227 100644 --- a/src/textconv.h +++ b/src/textconv.h @@ -139,6 +139,7 @@ #define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0) extern void delete_surrounding_text (struct frame *, ptrdiff_t, ptrdiff_t, unsigned long); extern void request_point_update (struct frame *, unsigned long); +extern void textconv_barrier (struct frame *, unsigned long); extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *); commit c3c2289b29df2b723b9db93d9ea4cd5d04fc89a0 Merge: 0014a10b242 9f5ca95ef77 Author: Po Lu Date: Fri Jun 2 08:07:36 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 0014a10b242bac62b0ba913a5ee4da4cfbe07f41 Author: Po Lu Date: Thu Jun 1 16:31:50 2023 +0800 Correctly export file:// URIs on Android * java/org/gnu/emacs/EmacsService.java (browseUrl): If uri's scheme is `file', rewrite it into a content URI. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 2f35933a7d1..dde60e1c5af 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -62,6 +62,8 @@ import android.os.VibratorManager; import android.os.VibrationEffect; +import android.provider.DocumentsContract; + import android.util.Log; import android.util.DisplayMetrics; @@ -546,11 +548,33 @@ invocation of app_process (through android-emacs) can browseUrl (String url) { Intent intent; + Uri uri; try { - intent = new Intent (Intent.ACTION_VIEW, Uri.parse (url)); - intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK); + /* Parse the URI. */ + uri = Uri.parse (url); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + { + /* On Android 4.4 and later, check if URI is actually a + file name. If so, rewrite it into a content provider + URI, so that it can be accessed by other programs. */ + + if (uri.getScheme ().equals ("file") + && uri.getPath () != null) + uri + = DocumentsContract.buildDocumentUri ("org.gnu.emacs", + uri.getPath ()); + } + + Log.d (TAG, ("browseUri: browsing " + url + + " --> " + uri.getPath () + + " --> " + uri)); + + intent = new Intent (Intent.ACTION_VIEW, uri); + intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivity (intent); } catch (Exception e) commit aed0a11147e29fc73405f1815fef91ecf6cca7fb Author: Po Lu Date: Thu Jun 1 15:16:02 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsInputConnection.java (EmacsInputConnection, performContextMenuAction): New function. * java/org/gnu/emacs/EmacsNative.java (EmacsNative) (performContextMenuAction): New function. * src/android.c (android_get_gc_values): Implement more efficiently. * src/androidterm.c (android_handle_ime_event): Pass through `update' argument to `finish_composing_text'. Fix thinko. * src/textconv.c (really_finish_composing_text) (really_set_composing_text, really_set_composing_region) (handle_pending_conversion_events_1, finish_composing_text): New argument `update'. Notify IME of conversion region changes if set. * src/textconv.h: Update structs and prototypes. diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index 54c98d950aa..eb6fd5f2763 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -256,6 +256,52 @@ public final class EmacsInputConnection extends BaseInputConnection return true; } + @Override + public boolean + performContextMenuAction (int contextMenuAction) + { + int action; + + if (EmacsService.DEBUG_IC) + Log.d (TAG, "performContextMenuAction: " + contextMenuAction); + + /* Translate the action in Java code. That way, a great deal of + JNI boilerplate can be avoided. */ + + switch (contextMenuAction) + { + case android.R.id.selectAll: + action = 0; + break; + + case android.R.id.startSelectingText: + action = 1; + break; + + case android.R.id.stopSelectingText: + action = 2; + break; + + case android.R.id.cut: + action = 3; + break; + + case android.R.id.copy: + action = 4; + break; + + case android.R.id.paste: + action = 5; + break; + + default: + return true; + } + + EmacsNative.performContextMenuAction (windowHandle, action); + return true; + } + @Override public ExtractedText getExtractedText (ExtractedTextRequest request, int flags) diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 56c03ee38dc..eb75201088b 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -208,6 +208,8 @@ public static native void setComposingRegion (short window, int start, public static native void setSelection (short window, int start, int end); public static native void performEditorAction (short window, int editorAction); + public static native void performContextMenuAction (short window, + int contextMenuAction); public static native ExtractedText getExtractedText (short window, ExtractedTextRequest req, int flags); diff --git a/src/android.c b/src/android.c index 67590ae373d..94587344eb5 100644 --- a/src/android.c +++ b/src/android.c @@ -3863,22 +3863,13 @@ android_get_gc_values (struct android_gc *gc, values->clip_y_origin = gc->clip_y_origin; if (mask & ANDROID_GC_FILL_STYLE) - values->fill_style - = (*android_java_env)->GetIntField (android_java_env, - gcontext, - emacs_gc_fill_style); + values->fill_style = gc->fill_style; if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN) - values->ts_x_origin - = (*android_java_env)->GetIntField (android_java_env, - gcontext, - emacs_gc_ts_origin_x); + values->ts_x_origin = gc->ts_x_origin; if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN) - values->ts_y_origin - = (*android_java_env)->GetIntField (android_java_env, - gcontext, - emacs_gc_ts_origin_y); + values->ts_y_origin = gc->ts_y_origin; /* Fields involving handles are not used by Emacs, and thus not implemented */ diff --git a/src/androidterm.c b/src/androidterm.c index a9b5834c08f..c302e3f2877 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -681,7 +681,6 @@ android_handle_ime_event (union android_event *event, struct frame *f) switch (event->ime.operation) { case ANDROID_IME_COMMIT_TEXT: - case ANDROID_IME_FINISH_COMPOSING_TEXT: case ANDROID_IME_SET_COMPOSING_TEXT: text = android_decode_utf16 (event->ime.text, event->ime.length); @@ -708,7 +707,8 @@ android_handle_ime_event (union android_event *event, struct frame *f) break; case ANDROID_IME_FINISH_COMPOSING_TEXT: - finish_composing_text (f, event->ime.counter); + finish_composing_text (f, event->ime.counter, + event->ime.length); break; case ANDROID_IME_SET_COMPOSING_TEXT: @@ -5161,7 +5161,71 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, /* Undocumented behavior: performEditorAction is apparently expected to finish composing any text. */ - NATIVE_NAME (finishComposingText) (env, object, window); + event.ime.type = ANDROID_INPUT_METHOD; + event.ime.serial = ++event_serial; + event.ime.window = window; + event.ime.operation = ANDROID_IME_FINISH_COMPOSING_TEXT; + event.ime.start = 0; + event.ime.end = 0; + + /* This value of `length' means that the input method should receive + an update containing the new conversion region. */ + + event.ime.length = 1; + event.ime.position = 0; + event.ime.text = NULL; + event.ime.counter = ++edit_counter; + + android_write_event (&event); + + /* Finally, send the return key press. */ + + event.xkey.type = ANDROID_KEY_PRESS; + event.xkey.serial = ++event_serial; + event.xkey.window = window; + event.xkey.time = 0; + event.xkey.state = 0; + event.xkey.keycode = 66; + event.xkey.unicode_char = 0; + + android_write_event (&event); +} + +JNIEXPORT void JNICALL +NATIVE_NAME (performContextMenuAction) (JNIEnv *env, jobject object, + jshort window, int action) +{ + JNI_STACK_ALIGNMENT_PROLOGUE; + + union android_event event; + int key; + + /* Note that ACTION is determined in EmacsInputConnection, and as + such they are not actual resource IDs. */ + + switch (action) + { + case 0: /* android.R.id.selectAll */ + case 1: /* android.R.id.startSelectingText */ + case 2: /* android.R.id.stopSelectingText */ + /* These actions are not implemented. */ + return; + + case 3: /* android.R.id.cut */ + key = 277; + break; + + case 4: /* android.R.id.copy */ + key = 278; + break; + + case 5: /* android.R.id.paste */ + key = 279; + break; + + default: + emacs_abort (); + } event.xkey.type = ANDROID_KEY_PRESS; event.xkey.serial = ++event_serial; diff --git a/src/textconv.c b/src/textconv.c index dcf016104fe..d8166bcfd03 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -676,10 +676,11 @@ really_commit_text (struct frame *f, EMACS_INT position, } /* Remove the composition region on the frame F, while leaving its - contents intact. */ + contents intact. If UPDATE, also notify the input method of the + change. */ static void -really_finish_composing_text (struct frame *f) +really_finish_composing_text (struct frame *f, bool update) { if (!NILP (f->conversion.compose_region_start)) { @@ -687,6 +688,10 @@ really_finish_composing_text (struct frame *f) Fset_marker (f->conversion.compose_region_end, Qnil, Qnil); f->conversion.compose_region_start = Qnil; f->conversion.compose_region_end = Qnil; + + if (update && text_interface + && text_interface->compose_region_changed) + (*text_interface->compose_region_changed) (f); } /* Delete the composition region overlay. */ @@ -796,7 +801,7 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, the documentation, but is ultimately what programs expect. */ if (!SCHARS (text)) - really_finish_composing_text (f); + really_finish_composing_text (f, false); /* If PT hasn't changed, the conversion region definitely has. Otherwise, redisplay will update the input method instead. */ @@ -838,7 +843,7 @@ really_set_composing_region (struct frame *f, ptrdiff_t start, if (max (0, start) == max (0, end)) { - really_finish_composing_text (f); + really_finish_composing_text (f, false); return; } @@ -1167,7 +1172,7 @@ handle_pending_conversion_events_1 (struct frame *f, break; case TEXTCONV_FINISH_COMPOSING_TEXT: - really_finish_composing_text (f); + really_finish_composing_text (f, !NILP (data)); break; case TEXTCONV_SET_COMPOSING_TEXT: @@ -1360,16 +1365,20 @@ commit_text (struct frame *f, Lisp_Object string, /* Remove the composition region and its overlay from F's current buffer. Leave the text being composed intact. + If UPDATE, call `compose_region_changed' after the region is + removed. + COUNTER means the same as in `start_batch_edit'. */ void -finish_composing_text (struct frame *f, unsigned long counter) +finish_composing_text (struct frame *f, unsigned long counter, + bool update) { struct text_conversion_action *action, **last; action = xmalloc (sizeof *action); action->operation = TEXTCONV_FINISH_COMPOSING_TEXT; - action->data = Qnil; + action->data = update ? Qt : Qnil; action->next = NULL; action->counter = counter; for (last = &f->conversion.actions; *last; last = &(*last)->next) diff --git a/src/textconv.h b/src/textconv.h index 055bf251651..e632a9dddcf 100644 --- a/src/textconv.h +++ b/src/textconv.h @@ -128,7 +128,8 @@ #define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0) extern void end_batch_edit (struct frame *, unsigned long); extern void commit_text (struct frame *, Lisp_Object, ptrdiff_t, unsigned long); -extern void finish_composing_text (struct frame *, unsigned long); +extern void finish_composing_text (struct frame *, unsigned long, + bool); extern void set_composing_text (struct frame *, Lisp_Object, ptrdiff_t, unsigned long); extern void set_composing_region (struct frame *, ptrdiff_t, ptrdiff_t, commit 9a958c59a2ce546e6ec99c58ca181dafeac8dd6b Author: Po Lu Date: Thu Jun 1 10:05:42 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsInputConnection.java (EmacsInputConnection): Add compatibility adjustments for Samsung devices. diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index 420da58c0f8..54c98d950aa 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -65,6 +65,13 @@ public final class EmacsInputConnection extends BaseInputConnection if (Build.MANUFACTURER.equalsIgnoreCase ("Huawei") || Build.MANUFACTURER.equalsIgnoreCase ("Honor")) extractAbsoluteOffsets = syncAfterCommit = true; + + /* The Samsung keyboard takes `selectionStart' at face value if + some text is returned, and also searches for words solely + within that text. However, when no text is returned, it falls + back to getTextAfterCursor and getTextBeforeCursor. */ + if (Build.MANUFACTURER.equalsIgnoreCase ("Samsung")) + extractAbsoluteOffsets = true; }; public commit ce238de2b1126bb1f356285c9713d8efefae3d59 Author: Po Lu Date: Thu Jun 1 08:29:48 2023 +0800 Correctly report start and end in extracted text * src/androidterm.c (struct android_get_extracted_text_context): New field `start_offset' and `end_offset'. Delete `offset'. (android_get_extracted_text, android_build_extracted_text): Replace `offset' with new args `start_offset' and `end_offset'. (NATIVE_NAME): Set `start_offset' and `end_offset'. (android_update_selection): Likewise. * src/textconv.c (get_extracted_text): Likewise. * src/textconv.h: Update prototypes. diff --git a/src/androidterm.c b/src/androidterm.c index 6f7c06875ca..a9b5834c08f 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5196,7 +5196,7 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, ptrdiff_t length, bytes; /* Offsets into that text. */ - ptrdiff_t start, offset; + ptrdiff_t start, start_offset, end_offset; /* The window. */ android_window window; @@ -5222,8 +5222,9 @@ android_get_extracted_text (void *data) /* Now get the extracted text. */ request->text = get_extracted_text (f, min (request->hint_max_chars, 600), - &request->start, &request->offset, - &request->length, &request->bytes); + &request->start, &request->start_offset, + &request->end_offset, &request->length, + &request->bytes); /* See if request->flags & GET_EXTRACTED_TEXT_MONITOR. If so, then the input method has asked to monitor changes to the extracted @@ -5268,8 +5269,9 @@ android_get_extracted_text (void *data) /* Return an ExtractedText object corresponding to the extracted text TEXT. START is a character position describing the offset of the - first character in TEXT. OFFSET is the offset of point relative to - START. + first character in TEXT. START_OFFSET is the offset of the lesser + of point or mark relative to START, and END_OFFSET is that of the + greater of point or mark relative to START. Assume that request_class and text_class have already been initialized. @@ -5279,7 +5281,8 @@ android_get_extracted_text (void *data) static jobject android_build_extracted_text (jstring text, ptrdiff_t start, - ptrdiff_t offset) + ptrdiff_t start_offset, + ptrdiff_t end_offset) { JNIEnv *env; jobject object; @@ -5299,9 +5302,9 @@ android_build_extracted_text (jstring text, ptrdiff_t start, (*env)->SetIntField (env, object, text_class.partial_start_offset, -1); (*env)->SetIntField (env, object, text_class.partial_end_offset, -1); (*env)->SetIntField (env, object, text_class.selection_start, - min (offset, TYPE_MAXIMUM (jint))); + min (start_offset, TYPE_MAXIMUM (jint))); (*env)->SetIntField (env, object, text_class.selection_end, - min (offset, TYPE_MAXIMUM (jint))); + min (end_offset, TYPE_MAXIMUM (jint))); /* Subtract 1 from start: point indices in Emacs start from 1, but Android expects 0. */ @@ -5404,9 +5407,9 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, (*env)->SetIntField (env, object, text_class.partial_start_offset, -1); (*env)->SetIntField (env, object, text_class.partial_end_offset, -1); (*env)->SetIntField (env, object, text_class.selection_start, - min (context.offset, TYPE_MAXIMUM (jint))); + min (context.start_offset, TYPE_MAXIMUM (jint))); (*env)->SetIntField (env, object, text_class.selection_end, - min (context.offset, TYPE_MAXIMUM (jint))); + min (context.end_offset, TYPE_MAXIMUM (jint))); /* Subtract 1 from start: point indices in Emacs start from 1, but Android expects 0. */ @@ -5507,7 +5510,8 @@ NATIVE_NAME (requestCursorUpdates) (JNIEnv *env, jobject object, static void android_update_selection (struct frame *f, struct window *w) { - ptrdiff_t start, end, point, mark, offset, length, bytes; + ptrdiff_t start, end, point, mark, start_offset, end_offset; + ptrdiff_t length, bytes; struct buffer *b; int hint, token; char *text; @@ -5560,7 +5564,8 @@ android_update_selection (struct frame *f, struct window *w) hint = FRAME_ANDROID_OUTPUT (f)->extracted_text_hint; token = FRAME_ANDROID_OUTPUT (f)->extracted_text_token; text = get_extracted_text (f, min (hint, 600), &start, - &offset, &length, &bytes); + &start_offset, &end_offset, + &length, &bytes); if (text) { @@ -5572,7 +5577,8 @@ android_update_selection (struct frame *f, struct window *w) /* Make extracted text out of that string. */ extracted = android_build_extracted_text (string, start, - offset); + start_offset, + end_offset); android_exception_check_1 (string); ANDROID_DELETE_LOCAL_REF (string); diff --git a/src/textconv.c b/src/textconv.c index a2c790d5374..dcf016104fe 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -1513,21 +1513,23 @@ request_point_update (struct frame *f, unsigned long counter) that the mark is active. Set *N to the actual number of characters returned, *START_RETURN - to the position of the first character returned, *OFFSET to the - offset of point within that text, *LENGTH to the actual number of - characters returned, and *BYTES to the actual number of bytes - returned. + to the position of the first character returned, *START_OFFSET to + the offset of the lesser of mark and point within that text, + *END_OFFSET to the greater of mark and point within that text, and + *LENGTH to the actual number of characters returned, and *BYTES to + the actual number of bytes returned. Value is NULL upon failure, and a malloced string upon success. */ char * get_extracted_text (struct frame *f, ptrdiff_t n, ptrdiff_t *start_return, - ptrdiff_t *offset, ptrdiff_t *length, + ptrdiff_t *start_offset, + ptrdiff_t *end_offset, ptrdiff_t *length, ptrdiff_t *bytes) { specpdl_ref count; - ptrdiff_t start, end, start_byte, end_byte; + ptrdiff_t start, end, start_byte, end_byte, mark; char *buffer; if (!WINDOW_LIVE_P (f->old_selected_window)) @@ -1595,9 +1597,17 @@ get_extracted_text (struct frame *f, ptrdiff_t n, copy_buffer (start, start_byte, end, end_byte, buffer); + /* Get the mark. If it's not active, use PT. */ + + mark = get_mark (); + + if (mark == -1) + mark = PT; + /* Return the offsets. */ *start_return = start; - *offset = PT - start; + *start_offset = min (mark - start, PT - start); + *end_offset = max (mark - start, PT - start); *length = end - start; *bytes = end_byte - start_byte; diff --git a/src/textconv.h b/src/textconv.h index 6abca97bc52..055bf251651 100644 --- a/src/textconv.h +++ b/src/textconv.h @@ -139,7 +139,8 @@ #define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0) ptrdiff_t, unsigned long); extern void request_point_update (struct frame *, unsigned long); extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *, - ptrdiff_t *, ptrdiff_t *, ptrdiff_t *); + ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, + ptrdiff_t *); extern bool conversion_disabled_p (void); extern void register_textconv_interface (struct textconv_interface *); commit a964116008735492a50a309c28fb8768630c14b7 Merge: 53f7cc2078c dc3b3548b7c Author: Po Lu Date: Thu Jun 1 07:40:25 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 53f7cc2078c64fa169b167faa89f552fbafea18e Author: Po Lu Date: Wed May 31 21:08:33 2023 +0800 Fix build with Lisp_Object type checking * configure.ac: Pass through `--enable-check-lisp-object-type' on Android. * src/alloc.c (android_make_lisp_symbol): * src/android.c: * src/androidfns.c (android_set_no_focus_on_map) (android_set_no_accept_focus): * src/androidfont.c (androidfont_match, androidfont_open_font): * src/androidselect.c (Fandroid_get_clipboard) (Fandroid_get_clipboard_targets): * src/keyboard.c (make_lispy_event, syms_of_keyboard): * src/sfntfont.c (sfnt_enum_font_1, sfntfont_list_1): * src/textconv.c (really_set_point_and_mark): Fix Lisp_Object and integer screw-ups. diff --git a/configure.ac b/configure.ac index 529639bfa17..aa5fcea732b 100644 --- a/configure.ac +++ b/configure.ac @@ -1216,6 +1216,10 @@ AC_DEFUN passthrough="$passthrough --with-pop=$with_pop" passthrough="$passthrough --with-harfbuzz=$with_harfbuzz" + # Now pass through some checking options. + emacs_val="--enable-check-lisp-object-type=$enable_check_lisp_object_type" + passthrough="$passthrough $emacs_val" + AS_IF([test "x$with_mailutils" = "xyes"], [emacs_use_mailutils=yes]) AC_SUBST([emacs_use_mailutils]) diff --git a/src/alloc.c b/src/alloc.c index 82b1c6b0355..c77bdc6372d 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -6195,14 +6195,15 @@ #define make_lisp_symbol android_make_lisp_symbol android_make_lisp_symbol (struct Lisp_Symbol *sym) { intptr_t symoffset; - Lisp_Object a; symoffset = (intptr_t) sym; INT_SUBTRACT_WRAPV (symoffset, (intptr_t) &lispsym, &symoffset); - a = TAG_PTR (Lisp_Symbol, symoffset); - return a; + { + Lisp_Object a = TAG_PTR (Lisp_Symbol, symoffset); + return a; + } } #endif diff --git a/src/android.c b/src/android.c index 43122f73be1..67590ae373d 100644 --- a/src/android.c +++ b/src/android.c @@ -6495,8 +6495,8 @@ android_exception_check_1 (jobject object) } } -/* Like android_exception_check_one, except it takes more than one - local reference argument. */ +/* Like android_exception_check_1, except it takes more than one local + reference argument. */ void android_exception_check_2 (jobject object, jobject object1) diff --git a/src/androidfns.c b/src/androidfns.c index 60b0549e7d1..cc48de1a359 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -2885,7 +2885,7 @@ android_set_no_focus_on_map (struct frame *f, Lisp_Object new_value, if (!EQ (new_value, old_value)) { android_set_dont_focus_on_map (FRAME_ANDROID_WINDOW (f), - new_value); + !NILP (new_value)); FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value); } } @@ -2897,7 +2897,7 @@ android_set_no_accept_focus (struct frame *f, Lisp_Object new_value, if (!EQ (new_value, old_value)) { android_set_dont_accept_focus (FRAME_ANDROID_WINDOW (f), - new_value); + !NILP (new_value)); FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value); } } diff --git a/src/androidfont.c b/src/androidfont.c index 1a09027bca7..db2f94008f2 100644 --- a/src/androidfont.c +++ b/src/androidfont.c @@ -636,7 +636,7 @@ androidfont_match (struct frame *f, Lisp_Object font_spec) androidfont_from_java (result, entity); info->object = (*android_java_env)->NewGlobalRef (android_java_env, (jobject) result); - android_exception_check_2 (entity, result); + android_exception_check_1 (result); ANDROID_DELETE_LOCAL_REF (result); return entity; @@ -713,10 +713,6 @@ androidfont_open_font (struct frame *f, Lisp_Object font_entity, pixel_size = 12; } - __android_log_print (ANDROID_LOG_DEBUG, __func__, - "opening font entity %"pI"x:%d", - (EMACS_INT) font_entity, pixel_size); - entity = (struct androidfont_entity *) XFONT_ENTITY (font_entity); block_input (); diff --git a/src/androidselect.c b/src/androidselect.c index 54c712ca93b..d1f2ebb52f9 100644 --- a/src/androidselect.c +++ b/src/androidselect.c @@ -208,7 +208,7 @@ DEFUN ("android-get-clipboard", Fandroid_get_clipboard, ANDROID_DELETE_LOCAL_REF (bytes); /* Now decode the resulting string. */ - return code_convert_string_norecord (string, Qutf_8, Qnil); + return code_convert_string_norecord (string, Qutf_8, false); } DEFUN ("android-clipboard-exists-p", Fandroid_clipboard_exists_p, @@ -304,7 +304,7 @@ DEFUN ("android-get-clipboard-targets", Fandroid_get_clipboard_targets, /* Decode the string. */ tem = make_unibyte_string ((char *) data, length1); - tem = code_convert_string_norecord (tem, Qutf_8, Qnil); + tem = code_convert_string_norecord (tem, Qutf_8, false); targets = Fcons (tem, targets); /* Delete the retrieved data. */ diff --git a/src/keyboard.c b/src/keyboard.c index 364f26f421d..ec9a6dc712a 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -6569,7 +6569,7 @@ make_lispy_event (struct input_event *event) menu bar event. */ menu_bar_touch_id = Qnil; - if (f->menu_bar_window) + if (!NILP (f->menu_bar_window)) { x_y_to_hpos_vpos (XWINDOW (f->menu_bar_window), XFIXNUM (x), XFIXNUM (y), &column, &row, NULL, NULL, @@ -13465,12 +13465,12 @@ syms_of_keyboard (void) is called with one argument, the string that was selected. */); Vpost_select_region_hook = Qnil; - DEFVAR_LISP ("disable-inhibit-text-conversion", + DEFVAR_BOOL ("disable-inhibit-text-conversion", disable_inhibit_text_conversion, doc: /* Don't disable text conversion inside `read-key-sequence'. If non-nil, text conversion will continue to happen after a prefix key has been read inside `read-key-sequence'. */); - disable_inhibit_text_conversion = false; + disable_inhibit_text_conversion = false; pdumper_do_now_and_after_load (syms_of_keyboard_for_pdumper); } diff --git a/src/sfntfont.c b/src/sfntfont.c index 71399b890d2..826c830ece5 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -991,7 +991,7 @@ sfnt_enum_font_1 (int fd, const char *file, style1 = sfnt_decode_instance_name (&fvar->instance[i], name); - if (!style1) + if (NILP (style1)) continue; /* Now parse the style. */ @@ -1520,8 +1520,8 @@ sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec, if (STRINGP (XCAR (XCAR (tail))) && STRINGP (XCDR (XCAR (tail))) - && Fstring_equal (SYMBOL_NAME (tem), - XCAR (XCAR (tail)))) + && !NILP (Fstring_equal (SYMBOL_NAME (tem), + XCAR (XCAR (tail))))) { /* Special family found. */ tem = Fintern (XCDR (XCAR (tail)), Qnil); diff --git a/src/textconv.c b/src/textconv.c index 1530cc0ce32..a2c790d5374 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -1022,7 +1022,8 @@ really_set_point_and_mark (struct frame *f, ptrdiff_t point, /* Set the point. */ Fgoto_char (make_fixnum (point)); - if (mark == point && BVAR (current_buffer, mark_active)) + if (mark == point + && !NILP (BVAR (current_buffer, mark_active))) call0 (Qdeactivate_mark); else call1 (Qpush_mark, make_fixnum (mark)); commit 69c4bbc0d694594f43b02e9da5c236f3b9271b1d Author: Po Lu Date: Wed May 31 13:47:08 2023 +0800 Update Android port * doc/emacs/input.texi (Other Input Devices, Touchscreens) (On-Screen Keyboards): * doc/lispref/commands.texi (Misc Events): * src/android.c (android_faccessat): Improve word choices and commentary. * lisp/touch-screen.el (touch-screen-handle-scroll): Make precision scrolling work better with horizontal movement. diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi index 96743349a1c..0df3162ce97 100644 --- a/doc/emacs/input.texi +++ b/doc/emacs/input.texi @@ -5,14 +5,14 @@ Other Input Devices @appendix Emacs and unconventional input devices @cindex other input devices - Emacs was originally developed with the assumption that users will -be sitting in front of a desktop computer, with a keyboard and perhaps -a suitable pointing device such as a mouse. + Emacs was originally developed with the assumption that its users +have access to a desktop computer or computer terminal, with a +keyboard and perhaps a suitable pointing device such as a mouse. - However, recent developments in the X Window System, and in other -operating systems such as Android, mean that this assumption no longer -holds true. As a result, Emacs now has support for other kinds of -input devices, which is detailed here. + However, recent developments in the X Window System and operating +systems such as Android mean that this assumption no longer holds +true. Emacs supports input from various other kinds of input devices, +which is detailed here. @menu * Touchscreens:: Using Emacs on touchscreens. @@ -23,21 +23,21 @@ Touchscreens @section Using Emacs on touchscreens @cindex touchscreens - Touchscreen input works by having the user press tools onto the -screen, which can be his own fingers, or a pointing device such as a -stylus, in order to manipulate the contents there in. + Touchscreen input works by pressing and moving tools (which include +fingers and some pointing devices--styluses, for example) onto a frame +in order to manipulate its contents. When running under the X Window System or Android, Emacs -automatically detects and maps the following touchscreen gestures to -common actions: +automatically detects and maps the following sequences of movements +(``gestures'') to common actions: @itemize @bullet @item @cindex tapping, touchscreens - ``Tapping'', meaning to briefly place and lift a tool from the -display, will result in Emacs selecting the window that was tapped, -and executing any command bound to @code{mouse-1} at that location in -the window. If the tap happened on top of a link (@pxref{Mouse + ``Tapping'', briefly placing and lifting a tool from the display, +will result in Emacs selecting the window that was tapped, and +executing any command bound to @code{mouse-1} at that location in the +window. If the tap happened on top of a link (@pxref{Mouse References}), then Emacs will follow the link instead. @item @@ -52,12 +52,11 @@ Touchscreens @item @cindex dragging, touchscreens @cindex long-press, touchscreens - ``Dragging'', meaning to perform a @dfn{long-press} (placing a tool -on the display and leaving it there for a while) before moving the -tool around, will make Emacs set the point to where the tool was and -begin selecting text under the tool as it moves around, much like what -would happen if @code{mouse-1} were to be held down. @xref{Mouse -Commands}. + ``Dragging'', which is performing a @dfn{long-press} by placing a +tool on the display and leaving it there for a while prior to moving +the tool around will make Emacs set the point to where the tool was +and begin selecting text under the tool as it moves around, as if +@code{mouse-1} were to be held down. @xref{Mouse Commands}. @end itemize @vindex touch-screen-delay @@ -99,9 +98,9 @@ On-Screen Keyboards @vindex touch-screen-display-keyboard The user option @code{touch-screen-display-keyboard} forces Emacs to -always display the on screen keyboard; it may also be bound buffer -locally, meaning to always display the keyboard when the buffer is -selected. +always display the on screen keyboard; it may also be set buffer +locally, which means that Emacs should always display the keyboard +when the buffer is selected. Emacs also provides a set of functions to show or hide the on-screen keyboard. For more details, @pxref{On-Screen Keyboards,,, elisp, The @@ -109,15 +108,15 @@ On-Screen Keyboards @cindex quitting, without a keyboard Since it may not be possible for Emacs to display the on screen -keyboard when it is executing a command, Emacs implements a feature on -devices with only an on-screen keyboard, by which two rapid clicks of -a hardware button that is always present on the device results in +keyboard while it is executing a command, Emacs implements a feature +on devices with only an on-screen keyboard, by which two rapid clicks +of a hardware button that is always present on the device results in Emacs quitting. @xref{Quitting}. @vindex x-quit-keysym - The exact button is used to do this varies by system: on X, it is -defined in the variable @code{x-quit-keysym}, and on Android, it is -always the volume down button. + Which button is used to do this depends on the window system in use: +on X, it is defined in the variable @code{x-quit-keysym}, and on +Android, it is always the volume down button. @cindex text conversion, keyboards Most input methods designed to work with on-screen keyboards perform diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 783ab583ec4..edeea9ab27c 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2212,13 +2212,11 @@ Misc Events performs an edit to one or more buffers. @vindex text-conversion-edits -Once the event is sent, the input method may already have made -changes to multiple buffers inside many different frames. To -determine which buffers have been changed, and what edits have -been made to them, use the variable -@code{text-conversion-edits}, which is set prior to each -@code{text-conversion} event being sent; it is a list of the -form: +Once the event is sent, the input method may already have made changes +to multiple buffers inside many different frames. To determine which +buffers have been changed, and what edits have been made to them, use +the variable @code{text-conversion-edits}, which is set prior to each +@code{text-conversion} event being sent; it is a list of the form: @example @w{@code{((@var{buffer} @var{beg} @var{end} @var{ephemeral}) ...)}} @@ -2227,10 +2225,9 @@ Misc Events Where @var{ephemeral} is the buffer which was modified, @var{beg} and @var{end} are markers set to the positions of the edit at the time it was completed, and @var{ephemeral} is either a string, containing any -text which was inserted, or any text before point which was deleted, +text which was inserted (or any text before point which was deleted), @code{t}, meaning that the edit is a temporary edit made by the input -method, and @code{nil}, meaning that some text was deleted after -point. +method, or @code{nil}, meaning that some text was deleted after point. @vindex text-conversion-style Whether or not this event is sent depends on the value of the @@ -2247,26 +2244,25 @@ Misc Events @item action This means that the input method will be enabled, but @key{RET} will -be sent wherever the input method wanted to insert a new line. +be sent whenever the input method wants to insert a new line. @item t This, or any other value, means that the input method will be enabled -and make edits terminated by @code{text-conversion} events. +and make edits followed by @code{text-conversion} events. @end table @findex disable-text-conversion -Changes to the value of this variable will only take effect upon -the next redisplay after the buffer becomes the selected buffer -of a frame. If you need to disable text conversion in a way -that takes immediate effect, call the function -@code{set-text-conversion-style} instead. This can potentially -lock up the input method for a significant amount of time, so do -not do this lightly! +Changes to the value of this variable will only take effect upon the +next redisplay after the buffer becomes the selected buffer of a +frame. If you need to disable text conversion in a way that takes +immediate effect, call the function @code{set-text-conversion-style} +instead. This has the potential to lock up the input method for a +significant amount of time, and should be used with care. @vindex disable-inhibit-text-conversion In addition, text conversion is automatically disabled after a prefix -key is read by the command loop, or through @code{read-key-sequence}. -This can be disabled by setting or binding the variable +key is read by the command loop or @code{read-key-sequence}. This can +be disabled by setting or binding the variable @code{disable-inhibit-text-conversion} to a non-@code{nil} value. @cindex @code{delete-frame} event diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 2db8b62f6f9..ba55374d090 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -108,9 +108,17 @@ touch-screen-handle-scroll ;; Show a message instead. (condition-case nil (if touch-screen-precision-scroll - (if (> dy 0) - (pixel-scroll-precision-scroll-down-page dy) - (pixel-scroll-precision-scroll-up-page (- dy))) + (progn + (if (> dy 0) + (pixel-scroll-precision-scroll-down-page dy) + (pixel-scroll-precision-scroll-up-page (- dy))) + ;; Now set `lines-vscrolled' to an value that will result + ;; in hscroll being disabled if dy looks as if a + ;; significant amount of scrolling is about to take + ;; Otherwise, horizontal scrolling may then interfere with + ;; precision scrolling. + (when (> (abs dy) 10) + (setcar (nthcdr 7 touch-screen-current-tool) 10))) ;; Start conventional scrolling. First, determine the ;; direction in which the scrolling is taking place. Load the ;; accumulator value. diff --git a/src/android.c b/src/android.c index 625253bdfe0..43122f73be1 100644 --- a/src/android.c +++ b/src/android.c @@ -5989,20 +5989,6 @@ android_toggle_on_screen_keyboard (android_window window, bool show) -/* When calling the system's faccessat, make sure to clear the flag - AT_EACCESS. - - Android's faccessat simply fails upon using AT_EACCESS, so replace - it with zero here. This isn't caught during configuration as Emacs - is being cross compiled. - - This replacement is only done when building for Android 16 or - later, because earlier versions use the gnulib replacement that - lacks these issues. - - This is unnecessary on earlier API versions, as gnulib's - rpl_faccessat will be used instead, which lacks these problems. */ - /* Like faccessat, except it also understands DIRFD opened using android_dirfd. */ @@ -6046,10 +6032,20 @@ android_faccessat (int dirfd, const char *pathname, int mode, int flags) } #if __ANDROID_API__ >= 16 + /* When calling `faccessat', make sure to clear the flag AT_EACCESS. + + Android's faccessat simply fails if FLAGS contains AT_EACCESS, so + replace it with zero here. This isn't caught at configuration-time + as Emacs is being cross compiled. + + This takes place only when building for Android 16 and later, + because earlier versions use a Gnulib replacement that lacks these + issues. */ + return faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS); -#else +#else /* __ANDROID_API__ < 16 */ return faccessat (dirfd, pathname, mode, flags); -#endif +#endif /* __ANDROID_API__ >= 16 */ } commit b1f606f86375084d6e56b830953005caf1027eeb Author: Po Lu Date: Wed May 31 11:50:21 2023 +0800 ; * src/android.c (android_copy_area): Pacify compiler warning. diff --git a/src/android.c b/src/android.c index 9d1399f3fc2..625253bdfe0 100644 --- a/src/android.c +++ b/src/android.c @@ -4799,6 +4799,9 @@ android_copy_area (android_drawable src, android_drawable dest, case ANDROID_GC_XOR: do_blit = android_blit_xor; break; + + default: + emacs_abort (); } /* Load the bounds of the destination rectangle. */ commit 456095ed3129f7ce2fe1ff019ea5d912a69ed2a1 Author: Po Lu Date: Wed May 31 11:27:19 2023 +0800 Update Android port * exec/exec.c (insert_args): New argument `arg3'. Replace argv[1] with that argument. (exec_0): Pass file name of script to `insert_args'. diff --git a/exec/exec.c b/exec/exec.c index 0e077284860..0d9187cabfa 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -740,16 +740,18 @@ process_program_header (const char *name, int fd, } /* Prepend one or two extra arguments ARG1 and ARG2 to a pending - execve system call. TRACEE is the tracee performing the system - call, and REGS are its current user registers. Value is 1 upon - failure, else 0. */ + execve system call. Replace the argument immediately after + with ARG3. + + TRACEE is the tracee performing the system call, and REGS are its + current user registers. Value is 1 upon failure, else 0. */ static int insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, - const char *arg1, const char *arg2) + const char *arg1, const char *arg2, const char *arg3) { USER_WORD argv, argc, word, new; - USER_WORD new1, new2; + USER_WORD new1, new2, new3, i; size_t text_size, effective_size; USER_REGS_STRUCT original; @@ -783,15 +785,17 @@ insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, text. */ text_size = (strlen (arg1) + 1 - + (arg2 ? strlen (arg2) + 1 : 0)); + + (arg2 ? strlen (arg2) + 1 : 0) + + strlen (arg3) + 1); /* Round it up to the user word size. */ text_size += sizeof (USER_WORD) - 1; text_size &= ~(sizeof (USER_WORD) - 1); - /* Now allocate the new argv. */ + /* Now allocate the new argv. Make sure argc is at least 1; it + needs to hold ARG3. */ - effective_size = sizeof word * (argc + 2) + text_size; + effective_size = sizeof word * (MAX (1, argc) + 2) + text_size; if (arg2) effective_size += sizeof word; @@ -808,11 +812,13 @@ insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, /* Figure out where argv starts. */ - new2 = new + text_size; + new3 = new + text_size; - /* Now write the two strings. */ + /* Now write the first two strings. */ new1 = new + strlen (arg1) + 1; + new2 = new1 + (arg2 ? strlen (arg2) + 1 : 0); + if (user_copy (tracee, (const unsigned char *) arg1, new, new1 - new)) goto fail; @@ -821,50 +827,77 @@ insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, new1, new2 - new1)) goto fail; + /* Write the replacement arg3, the file name of the executable. */ + + if (user_copy (tracee, (const unsigned char *) arg3, + new2, new3 - new2)) + goto fail; + /* Start copying argv back to new2. First, write the one or two new arguments. */ if (ptrace (PTRACE_POKETEXT, tracee->pid, - (void *) new2, (void *) new)) + (void *) new3, (void *) new)) goto fail; - new2 += sizeof new2; + new3 += sizeof new3; if (arg2 && ptrace (PTRACE_POKETEXT, tracee->pid, - (void *) new2, (void *) new1)) + (void *) new3, (void *) new1)) goto fail; else if (arg2) - new2 += sizeof new2; + new3 += sizeof new3; + + /* Next, write the third argument. */ + + if (ptrace (PTRACE_POKETEXT, tracee->pid, (void *) new3, + (void *) new2)) + goto fail; + + new3 += sizeof new3; /* Copy the remaining arguments back. */ argv = regs->SYSCALL_ARG1_REG; - /* Make sure the trailing NULL is included. */ - argc += 1; - - while (argc) + if (argc) { - /* Read one argument. */ - word = ptrace (PTRACE_PEEKDATA, tracee->pid, - (void *) argv, NULL); - argv += sizeof argv; - argc--; + /* Make sure the trailing NULL is included. */ + argc += 1; + + /* Now copy each argument in argv, starting from argv[1]. */ + + for (i = 1; i < argc; ++i) + { + /* Read one argument. */ + word = ptrace (PTRACE_PEEKDATA, tracee->pid, + (void *) (argv + i * sizeof argv), NULL); + + /* Write one argument, then increment new3. */ - /* Write one argument, then increment new2. */ + if (ptrace (PTRACE_POKETEXT, tracee->pid, + (void *) new3, (void *) word)) + goto fail; + + new3 += sizeof new3; + } + } + else + { + /* Just write the trailing NULL. */ if (ptrace (PTRACE_POKETEXT, tracee->pid, - (void *) new2, (void *) word)) + (void *) new3, (void *) 0)) goto fail; - new2 += sizeof new2; + new3 += sizeof new3; } - /* Assert that new2 is not out of bounds. */ - assert (new2 == new + effective_size); + /* Assert that new3 is not out of bounds. */ + assert (new3 == new + effective_size); /* And that it is properly aligned. */ - assert (!(new2 & (sizeof new2 - 2))); + assert (!(new3 & (sizeof new3 - 2))); /* Now modify the system call argument to point to new + text_size. */ @@ -1046,7 +1079,7 @@ exec_0 (char *name, struct exec_tracee *tracee, and perhaps `extra'. */ if (insert_args (tracee, regs, interpreter_name, - extra)) + extra, name)) goto fail1; } commit 8db957366448b6cc09462e295bed9a079426519f Author: Po Lu Date: Wed May 31 10:48:52 2023 +0800 Update android.texi * doc/emacs/android.texi (What is Android?): (Android Startup): (Android File System): (Android Environment): (Android Windowing): (Android Troubleshooting): Improve wording and various other issues. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index d94b91c7ab7..0fdc620f3f6 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -59,21 +59,20 @@ What is Android? @node Android Startup @section Starting up Emacs on Android - Emacs is not installed on Android devices from source code, or by a -package manager. Instead, Emacs is compiled for Android on a -different operating system, with the resulting binaries packaged into -an archive, that is then transferred to the device and unpacked. + Emacs is not installed on Android devices from source code or +through a package manager. Instead, Emacs is compiled for Android on +a different operating system, with the resulting binaries packaged +into an archive, that is then transferred to the system and installed. - After being unpacked, Emacs instructs the system to display an -application icon on the desktop. Emacs then starts up once the + After being installed, the system places an application icon on the +desktop (a.k.a@: ``home screen''.) Emacs then starts up once the application icon is clicked. @cindex ``adb logcat'' During startup, Emacs will display messages in the system log -buffer; reading that buffer requires the Android Debug Bridge -(@command{adb}) utility to be installed on another computer; it cannot -be read on the computer running Android itself. +buffer; reading that buffer during start-up requires the Android Debug +Bridge (@command{adb}) utility to be installed on another computer. After enabling the ``USB Debugging'' feature on the Android system, and connecting it via USB to another system with the @command{adb} @@ -120,6 +119,10 @@ Android Startup @url{https://developer.android.com/studio/command-line/adb} for more details. + Once Emacs starts up, simply running the command @command{logcat} as +an asynchronous shell command (@pxref{Running Shell Commands From +Emacs}) will display the log buffer. + @cindex emacsclient wrapper, android Since there is no other way to start the @command{emacsclient} program (@pxref{Emacs Server}) from another Android program, Emacs @@ -139,14 +142,15 @@ Android Startup attempts to open the file with the wrapper will fail. @cindex /content directory, android - Some files are given to Emacs as ``content identifiers'', which the + Some files are given to Emacs as ``content identifiers'' that the system provides access to outside the normal filesystem APIs. Emacs -internally supports a temporary @file{/content} directory which is -used to access those files. Do not make any assumptions about the -contents of this directory, or try to open files in it yourself. +uses a pseudo-directory named @file{/content} to access those files. +Do not make any assumptions about the contents of this directory, or +try to open files in it yourself. This feature is not provided on Android 4.3 and earlier, in which -case the file is copied to a temporary directory instead. +case such files are copied to a temporary directory before being +opened. @node Android File System @section What files Emacs can access under Android @@ -160,8 +164,8 @@ Android File System Lisp}) is also enabled by default, as the @command{ls} binary which comes with the system varies by manufacturer and usually does not support all of the features required by Emacs. One copy of -@command{ls} shipped with some Android devices is even known to lack -support for the @code{-l} flag. +@command{ls} distributed with some Android systems is even known to +lack support for the @code{-l} flag. @cindex limitations of the /assets directory @@ -183,8 +187,8 @@ Android File System @file{/assets} directory. @item -Files in the @file{/assets} directory are always read only, and have -to be completely read in to memory each time they are opened. +Files in the @file{/assets} directory are always read only, and may be +read in to memory more than once each time they are opened. @end itemize Aside from the @file{/assets} directory, Android programs normally @@ -197,7 +201,8 @@ Android File System @item The @dfn{app library} directory. This is automatically appended to -@code{exec-path} upon startup. +@code{exec-path} and made @code{exec-directory} upon startup, and +contains utility executables alongside Emacs itself. @item The @dfn{external storage} directory. This is accessible to Emacs @@ -248,11 +253,11 @@ Android Environment operating system; however, from the perspective of applications and Emacs, the system has an overwhelming number of users. - Each application runs in its own user, with his own home directory, -which is the app data directory (@pxref{Android File System}.) + Each application runs in its own user, with its home directory set +to its app data directory (@pxref{Android File System}.) - Each application is also prohibited from accessing system -directories, and the app data directories of other applications. + Each application is also prohibited from accessing many system +directories and the app data directories of other applications. Emacs comes with several binaries. While being executable files, they are packaged as libraries in the library directory, because @@ -260,7 +265,12 @@ Android Environment installed. This means, instead of specifying @code{ctags} or @code{emacsclient} in a subprocess, Lisp code must specify @code{libctags.so} or @code{libemacsclient.so} on the command line -instead when starting either of those programs in a subprocess. +instead when starting either of those programs in a subprocess; to +determine which names to use, consult the values of the variables +@code{ctags-program-name}, @code{etags-program-name}, +@code{hexl-program-name}, @code{emacsclient-program-name}, +@code{movemail-program-name}, and @code{ebrowse-program-name}. +@xref{Subprocess Creation,,, elisp, the Emacs Lisp Reference Manual}. The @file{/assets} directory containing Emacs start-up files is supposed to be inaccessible to processes not directly created by @@ -269,7 +279,7 @@ Android Environment directory, it would thus follow that it is not possible for Emacs to start itself as a subprocess. A special binary named @command{libandroid-emacs.so} is provided with Emacs, and does its -best to start Emacs, for the purpose of running Lisp in batch mode. +best to start Emacs for the purpose of running Lisp in batch mode. However, the approach it takes was devised by reading Android source code, and is not sanctioned by the Android compatibility definition documents, so your mileage may vary. @@ -317,13 +327,13 @@ Android Environment @cindex emacs in the background, android Application processes are treated as disposable entities by the -system. When all Emacs frames move to the background, Emacs is liable -to be killed by the system at any time, for the purpose of saving -system resources. +system. When all Emacs frames move to the background, Emacs might be +terminated by the system at any time, for the purpose of saving system +resources. On Android 7.1 and earlier, Emacs tells the system to treat it as a ``background service''. The system will try to avoid killing Emacs -unless the device is under memory stress. +unless the system is stressed for memory. Android 8.0 removed the ability for background services to receive such special treatment. However, Emacs applies a workaround: the @@ -334,12 +344,12 @@ Android Environment safely hidden through the system settings without resulting in Emacs being killed. - However, it is not guaranteed that the system will not kill Emacs, -even if the notification is being displayed. While the Open Handset + However, it is not guaranteed that the system will not kill Emacs +even if a notification is being displayed. While the Open Handset Alliance's sample implementation of Android behaves correctly, many manufacturers place additional restrictions on program execution in the background in their proprietary versions of Android. There is a -list of such troublesome manufacturers and sometimes workarounds, at +list of such troublesome manufacturers and sometimes workarounds at @url{https://dontkillmyapp.com/}. @section Android permissions @@ -445,12 +455,12 @@ Android Windowing tiled on the screen at any time. Windows on Android do not continue to exist indefinitely after they -are created. Instead, the system may choose to terminate windows that -are not on screen in order to save memory, with the assumption that -the program will save its contents to disk and restore them later, -when the user asks for it to be opened again. As this is obviously -not possible with Emacs, Emacs separates the resources associated with -a frame from its system window. +are created. Instead, the system may choose to close windows that are +not on screen in order to save memory, with the assumption that the +program will save its contents to disk and restore them later, when +the user asks for it to be opened again. As this is obviously not +possible with Emacs, Emacs separates the resources associated with a +frame from its system window. Each system window created (including the initial window created during Emacs startup) is appended to a list of windows that do not @@ -617,7 +627,7 @@ Android Troubleshooting @xref{Initial Options}. However, Emacs can be started with the equivalent of either the -option @code{--quick}, or @code{--debug-init}, through a special +option @code{--quick}, or @code{--debug-init} through a special preferences screen. Under Android 7.0 and later, this can be accessed through the Emacs ``app info'' page in the system settings program; on older systems, this is displayed as a separate icon on the desktop @@ -630,15 +640,16 @@ Android Troubleshooting The first time any given copy of Emacs starts on a device, it spends a while loading the preloaded Lisp files which normally come with Emacs. This produces a ``dump file'' (@pxref{Initial Options}) in the -files directory, containing an identifier unique to this copy of +files directory, containing an identifier unique to that copy of Emacs. The next time that same copy of Emacs starts up, it simply loads the -data contained in that dump file, greatly improving start up time. +data contained in that dump file, greatly reducing start up time. If by some unforeseen circumstance the dump file is corrupted, Emacs can crash. If that happens, the dump file stored in the Emacs files -directory can be erased through the same preferences screen. +directory can be erased through the preferences screen described +above. @cindex accessing Emacs directories, Android Emacs supports an alternative method of rescuing broken Emacs commit 822463ea5adae0f17bde4246e320800ee02f592d Merge: 57903519eb6 ecc1d990d9e Author: Po Lu Date: Wed May 31 10:16:06 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 57903519eb61632c4a85fbaf420109892955079a Author: Po Lu Date: Wed May 31 10:13:04 2023 +0800 Update Android port * java/debug.sh (is_root): Go back to using unix sockets; allow adb to forward them correctly. * java/org/gnu/emacs/EmacsInputConnection.java (getExtractedText): Don't print text if NULL. * java/org/gnu/emacs/EmacsService.java (EmacsService): New field `imSyncInProgress'. (updateIC): If an IM sync might be in progress, avoid deadlocks. * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): Set `imSyncInProgress' across synchronization point. * src/android.c (android_check_query): Use __atomic_store_n. (android_answer_query): New function. (android_begin_query): Set `android_servicing_query' to 2. Check once, and don't spin waiting for query to complete. (android_end_query): Use __atomic_store_n. (android_run_in_emacs_thread): Compare-and-exchange flag. If originally 1, fail. * src/textconv.c (really_set_composing_text): Clear conversion region if text is empty. diff --git a/java/debug.sh b/java/debug.sh index 339b3604810..0458003fe72 100755 --- a/java/debug.sh +++ b/java/debug.sh @@ -19,6 +19,7 @@ ## along with GNU Emacs. If not, see . set -m +set -x oldpwd=`pwd` cd `dirname $0` @@ -310,22 +311,26 @@ is_root= if [ -z "$gdbserver" ]; then if [ "$is_root" = "yes" ]; then - adb -s $device shell $gdbserver_bin --once \ + adb -s $device shell $gdbserver_bin --multi \ "+/data/local/tmp/debug.$package.socket" --attach $pid >&5 & gdb_socket="localfilesystem:/data/local/tmp/debug.$package.socket" - else - adb -s $device shell run-as $package $gdbserver_bin --once \ + else + adb -s $device shell run-as $package $gdbserver_bin --multi \ "+debug.$package.socket" --attach $pid >&5 & gdb_socket="localfilesystem:$app_data_dir/debug.$package.socket" fi else # Normally the program cannot access $gdbserver_bin when it is # placed in /data/local/tmp. - adb -s $device shell run-as $package $gdbserver_cmd --once \ - "0.0.0.0:7654" --attach $pid >&5 & - gdb_socket="tcp:7654" + adb -s $device shell run-as $package $gdbserver_cmd --multi \ + "+debug.$package.socket" --attach $pid >&5 & + gdb_socket="localfilesystem:$app_data_dir/debug.$package.socket" fi +# In order to allow adb to forward to the gdbserver socket, make the +# app data directory a+x. +adb -s $device shell run-as $package chmod a+x $app_data_dir + # Wait until gdbserver successfully runs. line= while read -u 5 line; do diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index 21bbaca5d07..420da58c0f8 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -286,6 +286,14 @@ public final class EmacsInputConnection extends BaseInputConnection text = EmacsNative.getExtractedText (windowHandle, request, flags); + if (text == null) + { + if (EmacsService.DEBUG_IC) + Log.d (TAG, "getExtractedText: text is NULL"); + + return null; + } + if (EmacsService.DEBUG_IC) Log.d (TAG, "getExtractedText: " + text.text + " @" + text.startOffset + ":" + text.selectionStart diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 546d22627c5..2f35933a7d1 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -104,6 +104,9 @@ public final class EmacsService extends Service performing drawing calls. */ private static final boolean DEBUG_THREADS = false; + /* Whether or not onCreateInputMethod is calling getSelection. */ + public static volatile boolean imSyncInProgress; + /* Return the directory leading to the directory in which native library files are stored on behalf of CONTEXT. */ @@ -636,16 +639,41 @@ invocation of app_process (through android-emacs) can int newSelectionEnd, int composingRegionStart, int composingRegionEnd) { + boolean wasSynchronous; + if (DEBUG_IC) Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart + " " + newSelectionEnd + " " + composingRegionStart + " " + composingRegionEnd)); + + /* `updateSelection' holds an internal lock that is also taken + before `onCreateInputConnection' (in EmacsView.java) is called; + when that then asks the UI thread for the current selection, a + dead lock results. To remedy this, reply to any synchronous + queries now -- and prohibit more queries for the duration of + `updateSelection' -- if EmacsView may have been asking for the + value of the region. */ + + wasSynchronous = false; + if (EmacsService.imSyncInProgress) + { + /* `beginSynchronous' will answer any outstanding queries and + signal that one is now in progress, thereby preventing + `getSelection' from blocking. */ + + EmacsNative.beginSynchronous (); + wasSynchronous = true; + } + window.view.imManager.updateSelection (window.view, newSelectionStart, newSelectionEnd, composingRegionStart, composingRegionEnd); + + if (wasSynchronous) + EmacsNative.endSynchronous (); } public void diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 09bc9d719d3..bb450bb8e6b 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -628,8 +628,14 @@ else if (child.getVisibility () != GONE) } /* Obtain the current position of point and set it as the - selection. */ + selection. Don't do this under one specific situation: if + `android_update_ic' is being called in the main thread, trying + to synchronize with it can cause a dead lock in the IM + manager. */ + + EmacsService.imSyncInProgress = true; selection = EmacsNative.getSelection (window.handle); + EmacsService.imSyncInProgress = false; if (selection != null) Log.d (TAG, "onCreateInputConnection: current selection is: " diff --git a/src/android.c b/src/android.c index 8cc18787358..9d1399f3fc2 100644 --- a/src/android.c +++ b/src/android.c @@ -6959,8 +6959,11 @@ android_display_toast (const char *text) -/* Whether or not a query is currently being made. */ -static bool android_servicing_query; +/* The thread from which a query against a thread is currently being + made, if any. Value is 0 if no query is in progress, 1 if a query + is being made from the UI thread to the main thread, and 2 if a + query is being made the other way around. */ +static char android_servicing_query; /* Function that is waiting to be run in the Emacs thread. */ static void (*android_query_function) (void *); @@ -7010,7 +7013,37 @@ android_check_query (void) /* Finish the query. */ __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST); __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST); - __atomic_clear (&android_servicing_query, __ATOMIC_SEQ_CST); + __atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST); + + /* Signal completion. */ + sem_post (&android_query_sem); +} + +/* Run the function that the UI thread has asked to run, and then + signal its completion. Do not change `android_servicing_query' + after it completes. */ + +static void +android_answer_query (void) +{ + void (*proc) (void *); + void *closure; + + eassert (__atomic_load_n (&android_servicing_query, __ATOMIC_SEQ_CST) + == 1); + + /* First, load the procedure and closure. */ + __atomic_load (&android_query_context, &closure, __ATOMIC_SEQ_CST); + __atomic_load (&android_query_function, &proc, __ATOMIC_SEQ_CST); + + if (!proc) + return; + + proc (closure); + + /* Finish the query. */ + __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST); + __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST); /* Signal completion. */ sem_post (&android_query_sem); @@ -7025,18 +7058,23 @@ android_check_query (void) static void android_begin_query (void) { - if (__atomic_test_and_set (&android_servicing_query, - __ATOMIC_SEQ_CST)) + char old; + + /* Load the previous value of `android_servicing_query' and upgrade + it to 2. */ + + old = __atomic_exchange_n (&android_servicing_query, + 2, __ATOMIC_SEQ_CST); + + /* See if a query was previously in progress. */ + if (old == 1) { /* Answer the query that is currently being made. */ assert (android_query_function != NULL); - android_check_query (); - - /* Wait for that query to complete. */ - while (__atomic_load_n (&android_servicing_query, - __ATOMIC_SEQ_CST)) - ;; + android_answer_query (); } + + /* `android_servicing_query' is now 2. */ } /* Notice that a query has stopped. This function may be called from @@ -7045,7 +7083,7 @@ android_begin_query (void) static void android_end_query (void) { - __atomic_clear (&android_servicing_query, __ATOMIC_SEQ_CST); + __atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST); } /* Synchronously ask the Emacs thread to run the specified PROC with @@ -7063,6 +7101,7 @@ android_end_query (void) android_run_in_emacs_thread (void (*proc) (void *), void *closure) { union android_event event; + char old; event.xaction.type = ANDROID_WINDOW_ACTION; event.xaction.serial = ++event_serial; @@ -7074,10 +7113,13 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) __atomic_store_n (&android_query_function, proc, __ATOMIC_SEQ_CST); /* Don't allow deadlocks to happen; make sure the Emacs thread is - not waiting for something to be done. */ + not waiting for something to be done (in that case, + `android_query_context' is 2.) */ - if (__atomic_test_and_set (&android_servicing_query, - __ATOMIC_SEQ_CST)) + old = 0; + if (!__atomic_compare_exchange_n (&android_servicing_query, &old, + 1, false, __ATOMIC_SEQ_CST, + __ATOMIC_SEQ_CST)) { __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST); @@ -7098,6 +7140,15 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure) while (sem_wait (&android_query_sem) < 0) ;; + /* At this point, `android_servicing_query' should either be zero if + the query was answered or two if the main thread has started a + query. */ + + eassert (!__atomic_load_n (&android_servicing_query, + __ATOMIC_SEQ_CST) + || (__atomic_load_n (&android_servicing_query, + __ATOMIC_SEQ_CST) == 2)); + return 0; } diff --git a/src/textconv.c b/src/textconv.c index 26f351dc729..1530cc0ce32 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -792,6 +792,12 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, /* Move the composition overlay. */ sync_overlay (f); + /* If TEXT is empty, remove the composing region. This goes against + the documentation, but is ultimately what programs expect. */ + + if (!SCHARS (text)) + really_finish_composing_text (f); + /* If PT hasn't changed, the conversion region definitely has. Otherwise, redisplay will update the input method instead. */ commit 733a6776f9d665e5fd99bcc2a65c84c4360e277c Merge: 9e52188628e f8a79c0055f Author: Po Lu Date: Tue May 30 19:16:32 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 9e52188628e3cece8402a40c7f0fea8731f07959 Author: Po Lu Date: Mon May 29 18:19:52 2023 +0800 ; * src/android.c: Fix typos. diff --git a/src/android.c b/src/android.c index e0d0da6704a..8cc18787358 100644 --- a/src/android.c +++ b/src/android.c @@ -2895,6 +2895,102 @@ NATIVE_NAME (shouldForwardMultimediaButtons) (JNIEnv *env, return !android_pass_multimedia_buttons_to_system; } +JNIEXPORT void JNICALL +NATIVE_NAME (blitRect) (JNIEnv *env, jobject object, + jobject src, jobject dest, + jint x1, jint y1, jint x2, jint y2) +{ + AndroidBitmapInfo src_info, dest_info; + unsigned char *src_data_1, *dest_data_1; + void *src_data, *dest_data; + + /* N.B. that X2 and Y2 represent the pixel past the edge of the + rectangle; thus, the width is x2 - x1 and the height is y2 - + y1. */ + + memset (&src_info, 0, sizeof src_info); + memset (&dest_info, 0, sizeof dest_info); + AndroidBitmap_getInfo (env, src, &src_info); + AndroidBitmap_getInfo (env, dest, &dest_info); + + /* If the stride is 0 after a call to `getInfo', assume it + failed. */ + + if (!src_info.stride || !dest_info.stride) + return; + + /* If formats differ, abort. */ + eassert (src_info.format == dest_info.format + && src_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888); + + /* Lock the image data. */ + src_data = NULL; + AndroidBitmap_lockPixels (env, src, &src_data); + + if (!src_data) + return; + + dest_data = NULL; + AndroidBitmap_lockPixels (env, dest, &dest_data); + + if (!dest_data) + goto fail1; + + /* Now clip the rectangle to the bounds of the source and + destination bitmap. */ + + x1 = MAX (x1, 0); + y1 = MAX (y1, 0); + x2 = MAX (x2, 0); + y2 = MAX (y2, 0); + + + if (x1 >= src_info.width + || x1 >= dest_info.width) + x1 = MIN (dest_info.width - 1, src_info.width - 1); + + if (x2 > src_info.width + || x2 > dest_info.width) + x2 = MIN (src_info.width, dest_info.width); + + if (y1 >= src_info.height + || y1 >= dest_info.height) + y1 = MIN (dest_info.height - 1, src_info.height - 1); + + if (y2 > src_info.height + || y2 > dest_info.height) + y2 = MIN (src_info.height, dest_info.height); + + if (x1 >= x2 || y1 >= y2) + goto fail2; + + /* Determine the address of the first line to copy. */ + + src_data_1 = src_data; + dest_data_1 = dest_data; + src_data_1 += x1 * 4; + src_data_1 += y1 * src_info.stride; + dest_data_1 += x1 * 4; + dest_data_1 += y1 * dest_info.stride; + + /* Start copying each line. */ + + while (y1 != y2) + { + memcpy (dest_data_1, src_data_1, (x2 - x1) * 4); + src_data_1 += src_info.stride; + dest_data_1 += dest_info.stride; + y1++; + } + + /* Complete the copy and unlock the bitmap. */ + + fail2: + AndroidBitmap_unlockPixels (env, dest); + fail1: + AndroidBitmap_unlockPixels (env, src); +} + /* Forward declarations of deadlock prevention functions. */ static void android_begin_query (void); @@ -4032,7 +4128,7 @@ android_neon_mask_line (unsigned int *src, unsigned int *dst, N.B. that currently only copies between bitmaps of depth 24 are implemented. */ -void +static void android_blit_copy (int src_x, int src_y, int width, int height, int dst_x, int dst_y, struct android_gc *gc, unsigned char *src, AndroidBitmapInfo *src_info, @@ -4044,7 +4140,10 @@ android_blit_copy (int src_x, int src_y, int width, int height, size_t pixel, offset, offset1; unsigned char *src_current, *dst_current; unsigned char *mask_current; - int overflow, temp, i, j; + int overflow, temp, i; +#ifndef __aarch64__ + int j; +#endif /* __aarch64__ */ bool backwards; unsigned int *long_src, *long_dst; @@ -4328,7 +4427,7 @@ android_blit_copy (int src_x, int src_y, int width, int height, N.B. that currently only copies between bitmaps of depth 24 are implemented. */ -void +static void android_blit_xor (int src_x, int src_y, int width, int height, int dst_x, int dst_y, struct android_gc *gc, unsigned char *src, AndroidBitmapInfo *src_info, @@ -4760,104 +4859,6 @@ android_copy_area (android_drawable src, android_drawable dest, -JNIEXPORT void JNICALL -NATIVE_NAME (blitRect) (JNIEnv *env, jobject object, - jobject src, jobject dest, - jint x1, jint y1, jint x2, jint y2) -{ - AndroidBitmapInfo src_info, dest_info; - unsigned char *src_data_1, *dest_data_1; - void *src_data, *dest_data; - - /* N.B. that X2 and Y2 represent the pixel past the edge of the - rectangle; thus, the width is x2 - x1 and the height is y2 - - y1. */ - - memset (&src_info, 0, sizeof src_info); - memset (&dest_info, 0, sizeof dest_info); - AndroidBitmap_getInfo (env, src, &src_info); - AndroidBitmap_getInfo (env, dest, &dest_info); - - /* If the stride is 0 after a call to `getInfo', assume it - failed. */ - - if (!src_info.stride || !dest_info.stride) - return; - - /* If formats differ, abort. */ - eassert (src_info.format == dest_info.format - && src_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888); - - /* Lock the image data. */ - src_data = NULL; - AndroidBitmap_lockPixels (env, src, &src_data); - - if (!src_data) - return; - - dest_data = NULL; - AndroidBitmap_lockPixels (env, dest, &dest_data); - - if (!dest_data) - goto fail1; - - /* Now clip the rectangle to the bounds of the source and - destination bitmap. */ - - x1 = MAX (x1, 0); - y1 = MAX (y1, 0); - x2 = MAX (x2, 0); - y2 = MAX (y2, 0); - - - if (x1 >= src_info.width - || x1 >= dest_info.width) - x1 = MIN (dest_info.width - 1, src_info.width - 1); - - if (x2 > src_info.width - || x2 > dest_info.width) - x2 = MIN (src_info.width, dest_info.width); - - if (y1 >= src_info.height - || y1 >= dest_info.height) - y1 = MIN (dest_info.height - 1, src_info.height - 1); - - if (y2 > src_info.height - || y2 > dest_info.height) - y2 = MIN (src_info.height, dest_info.height); - - if (x1 >= x2 || y1 >= y2) - goto fail2; - - /* Determine the address of the first line to copy. */ - - src_data_1 = src_data; - dest_data_1 = dest_data; - src_data_1 += x1 * 4; - src_data_1 += y1 * src_info.stride; - dest_data_1 += x1 * 4; - dest_data_1 += y1 * dest_info.stride; - - /* Start copying each line. */ - - while (y1 != y2) - { - memcpy (dest_data_1, src_data_1, (x2 - x1) * 4); - src_data_1 += src_info.stride; - dest_data_1 += dest_info.stride; - y1++; - } - - /* Complete the copy and unlock the bitmap. */ - - fail2: - AndroidBitmap_unlockPixels (env, dest); - fail1: - AndroidBitmap_unlockPixels (env, src); -} - - - void android_free_pixmap (android_pixmap pixmap) { commit 1a1cf6b86fc3f07cf9ef988cf04967bfd5730de3 Merge: 1088a8e8dab 77087eb170f Author: Po Lu Date: Mon May 29 18:06:32 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 1088a8e8dab13085951254ee191a267145a9d825 Author: Po Lu Date: Mon May 29 17:59:48 2023 +0800 Update Android port * src/android.c (android_blit_copy): (android_blit_xor): Fix typos. diff --git a/src/android.c b/src/android.c index 9674fda36d8..e0d0da6704a 100644 --- a/src/android.c +++ b/src/android.c @@ -4044,7 +4044,7 @@ android_blit_copy (int src_x, int src_y, int width, int height, size_t pixel, offset, offset1; unsigned char *src_current, *dst_current; unsigned char *mask_current; - int overflow, temp, i, xdir; + int overflow, temp, i, j; bool backwards; unsigned int *long_src, *long_dst; @@ -4292,7 +4292,7 @@ android_blit_copy (int src_x, int src_y, int width, int height, mask_current = mask; #ifndef __aarch64__ - while (temp--) + for (j = 0; j < temp; ++j) { /* Sign extend the mask. */ i = *(signed char *) mask_current++; @@ -4335,14 +4335,16 @@ android_blit_xor (int src_x, int src_y, int width, int height, unsigned char *dst, AndroidBitmapInfo *dst_info, unsigned char *mask, AndroidBitmapInfo *mask_info) { +#if 0 uintptr_t start, end; int mask_offset; size_t pixel, offset, offset1; unsigned char *src_current, *dst_current; unsigned char *mask_current; - int overflow, temp, i, xdir; + int overflow, temp, i; bool backwards; unsigned int *long_src, *long_dst; +#endif /* 0 */ /* Note that this alu hasn't been tested -- it probably does not work! */ commit 787c947028c0ffd0ebe31cd675c976049cc9777c Author: Po Lu Date: Mon May 29 17:48:25 2023 +0800 ; * src/android.c (android_blit_copy): Fix typos. diff --git a/src/android.c b/src/android.c index 4184be3086b..9674fda36d8 100644 --- a/src/android.c +++ b/src/android.c @@ -4206,9 +4206,12 @@ android_blit_copy (int src_x, int src_y, int width, int height, || INT_ADD_WRAPV ((uintptr_t) mask, offset, &start)) return; + if (height <= 0) + return; + mask = mask_current = (unsigned char *) start; - while (--height) + while (height--) { /* Skip backwards past the end of the mask. */ @@ -4277,12 +4280,12 @@ android_blit_copy (int src_x, int src_y, int width, int height, else temp = MIN (mask_info->width, width); - if (temp <= 0) + if (temp <= 0 || height <= 0) return; /* Copy bytes according to the mask. */ - while (--height) + while (height--) { long_src = (unsigned int *) src_current; long_dst = (unsigned int *) dst_current; @@ -4292,11 +4295,12 @@ android_blit_copy (int src_x, int src_y, int width, int height, while (temp--) { /* Sign extend the mask. */ - height = *(signed char *) mask_current++; + i = *(signed char *) mask_current++; /* Apply the mask. */ - *long_dst = ((*long_src & height) - | (*long_dst & ~height)); + *long_dst = ((*long_src & i) | (*long_dst & ~i)); + long_dst++; + long_src++; } #else /* __aarch64__ */ android_neon_mask_line (long_src, long_dst, mask, temp); commit 7fdde02f3216536aa8977fa4b396bec8fbaf994b Author: Po Lu Date: Mon May 29 17:46:19 2023 +0800 Work around more problems with Bitmaps * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New function `blitRect'. * java/org/gnu/emacs/EmacsSurfaceView.java (EmacsSurfaceView): Use it on Android 8.x. * src/android.c (blitRect): Implement new function. diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index e699dda9ad4..56c03ee38dc 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -20,6 +20,9 @@ package org.gnu.emacs; import android.content.res.AssetManager; + +import android.graphics.Bitmap; + import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -216,6 +219,13 @@ public static native ExtractedText getExtractedText (short window, failure. */ public static native int[] getSelection (short window); + + /* Graphics functions used as a replacement for potentially buggy + Android APIs. */ + + public static native void blitRect (Bitmap src, Bitmap dest, int x1, + int y1, int x2, int y2); + static { /* Older versions of Android cannot link correctly with shared diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java index e0411f7f8b3..0deb930c2c2 100644 --- a/java/org/gnu/emacs/EmacsSurfaceView.java +++ b/java/org/gnu/emacs/EmacsSurfaceView.java @@ -57,11 +57,36 @@ public final class EmacsSurfaceView extends View private void copyToFrontBuffer (Bitmap bitmap, Rect damageRect) { - if (damageRect != null) - bitmapCanvas.drawBitmap (bitmap, damageRect, damageRect, - bitmapPaint); + EmacsService.checkEmacsThread (); + + if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O + && Build.VERSION.SDK_INT != Build.VERSION_CODES.O_MR1) + { + /* If `drawBitmap' can safely be used while a bitmap is locked + by another thread, continue here... */ + + if (damageRect != null) + bitmapCanvas.drawBitmap (bitmap, damageRect, damageRect, + bitmapPaint); + else + bitmapCanvas.drawBitmap (bitmap, 0f, 0f, bitmapPaint); + } else - bitmapCanvas.drawBitmap (bitmap, 0f, 0f, bitmapPaint); + { + /* But if it can not, as on Android 8.0 and 8.1, then use a + replacement function. */ + + if (damageRect != null) + EmacsNative.blitRect (bitmap, frontBuffer, + damageRect.left, + damageRect.top, + damageRect.right, + damageRect.bottom); + else + EmacsNative.blitRect (bitmap, frontBuffer, 0, 0, + bitmap.getWidth (), + bitmap.getHeight ()); + } } private void diff --git a/src/android.c b/src/android.c index 7b9c478f212..4184be3086b 100644 --- a/src/android.c +++ b/src/android.c @@ -4754,6 +4754,104 @@ android_copy_area (android_drawable src, android_drawable dest, +JNIEXPORT void JNICALL +NATIVE_NAME (blitRect) (JNIEnv *env, jobject object, + jobject src, jobject dest, + jint x1, jint y1, jint x2, jint y2) +{ + AndroidBitmapInfo src_info, dest_info; + unsigned char *src_data_1, *dest_data_1; + void *src_data, *dest_data; + + /* N.B. that X2 and Y2 represent the pixel past the edge of the + rectangle; thus, the width is x2 - x1 and the height is y2 - + y1. */ + + memset (&src_info, 0, sizeof src_info); + memset (&dest_info, 0, sizeof dest_info); + AndroidBitmap_getInfo (env, src, &src_info); + AndroidBitmap_getInfo (env, dest, &dest_info); + + /* If the stride is 0 after a call to `getInfo', assume it + failed. */ + + if (!src_info.stride || !dest_info.stride) + return; + + /* If formats differ, abort. */ + eassert (src_info.format == dest_info.format + && src_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888); + + /* Lock the image data. */ + src_data = NULL; + AndroidBitmap_lockPixels (env, src, &src_data); + + if (!src_data) + return; + + dest_data = NULL; + AndroidBitmap_lockPixels (env, dest, &dest_data); + + if (!dest_data) + goto fail1; + + /* Now clip the rectangle to the bounds of the source and + destination bitmap. */ + + x1 = MAX (x1, 0); + y1 = MAX (y1, 0); + x2 = MAX (x2, 0); + y2 = MAX (y2, 0); + + + if (x1 >= src_info.width + || x1 >= dest_info.width) + x1 = MIN (dest_info.width - 1, src_info.width - 1); + + if (x2 > src_info.width + || x2 > dest_info.width) + x2 = MIN (src_info.width, dest_info.width); + + if (y1 >= src_info.height + || y1 >= dest_info.height) + y1 = MIN (dest_info.height - 1, src_info.height - 1); + + if (y2 > src_info.height + || y2 > dest_info.height) + y2 = MIN (src_info.height, dest_info.height); + + if (x1 >= x2 || y1 >= y2) + goto fail2; + + /* Determine the address of the first line to copy. */ + + src_data_1 = src_data; + dest_data_1 = dest_data; + src_data_1 += x1 * 4; + src_data_1 += y1 * src_info.stride; + dest_data_1 += x1 * 4; + dest_data_1 += y1 * dest_info.stride; + + /* Start copying each line. */ + + while (y1 != y2) + { + memcpy (dest_data_1, src_data_1, (x2 - x1) * 4); + src_data_1 += src_info.stride; + dest_data_1 += dest_info.stride; + y1++; + } + + /* Complete the copy and unlock the bitmap. */ + + fail2: + AndroidBitmap_unlockPixels (env, dest); + fail1: + AndroidBitmap_unlockPixels (env, src); +} + + + void android_free_pixmap (android_pixmap pixmap) { commit 9a3535459333b5a1a32ed5b5ef4c593e585bfe9a Author: Po Lu Date: Mon May 29 16:33:20 2023 +0800 Update Android port * src/android.c (android_neon_mask_line): Fix iteration over remainder. (android_blit_copy): Be more paranoid. diff --git a/src/android.c b/src/android.c index 33d766a90d9..7b9c478f212 100644 --- a/src/android.c +++ b/src/android.c @@ -3958,14 +3958,14 @@ android_neon_mask_line (unsigned int *src, unsigned int *dst, uint32x4_t src_low, src_high, dst_low, dst_high; int16x8_t vmask; int32x4_t ext_mask_low, ext_mask_high, low, high; - int rem; + int rem, i; /* Calculate the remainder. */ - rem = n & 7; + rem = n & 7, n &= ~7; /* Process eight pixels at a time. */ - if (n -= rem) + if (n) { again: /* Load the low and high four pixels from the source. */ @@ -4008,14 +4008,13 @@ android_neon_mask_line (unsigned int *src, unsigned int *dst, /* Process the remaining pixels. */ - while (--rem) + for (i = 0; i < rem; ++i) { /* Sign extend the mask. */ - n = *(signed char *) mask++; + n = ((signed char *) mask)[i]; /* Combine src and dst. */ - *dst = ((*src & n) | (*dst & ~n)); - src++, dst++; + dst[i] = ((src[i] & n) | (dst[i] & ~n)); } } @@ -4262,7 +4261,8 @@ android_blit_copy (int src_x, int src_y, int width, int height, /* Make sure it's not out of bounds. */ eassert (dst_y - gc->clip_y_origin >= 0); - if ((dst_y - gc->clip_y_origin) + height > mask_info->height) + if ((dst_y - gc->clip_y_origin) + height > mask_info->height + || width <= 0) return; /* Now move mask to the position of the first row. */ @@ -4277,6 +4277,9 @@ android_blit_copy (int src_x, int src_y, int width, int height, else temp = MIN (mask_info->width, width); + if (temp <= 0) + return; + /* Copy bytes according to the mask. */ while (--height) commit 00671b18438fec8f2e7f774cb3fd4cd6f694fd18 Author: Po Lu Date: Mon May 29 15:44:14 2023 +0800 Implement android_copy_area in C * java/org/gnu/emacs/EmacsCopyArea.java: Remove file. * java/org/gnu/emacs/EmacsService.java (EmacsService, copyArea): Delete function. * src/android.c (struct android_emacs_service) (android_init_emacs_service): Remove `copy_area'. (android_create_gc, android_change_gc, android_get_gc_values): Record new GC values. (android_neon_mask_line): New function. (android_blit_copy, android_blit_xor): New functions. (android_copy_area): Implement in C. (android_lock_bitmap): Accept drawables instead of windows. * src/android.h: Adjust prototype for `android_lock_bitmap'. * src/androidgui.h (struct android_gc): Record last known GC values. diff --git a/java/org/gnu/emacs/EmacsCopyArea.java b/java/org/gnu/emacs/EmacsCopyArea.java deleted file mode 100644 index f69b0cde866..00000000000 --- a/java/org/gnu/emacs/EmacsCopyArea.java +++ /dev/null @@ -1,206 +0,0 @@ -/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- - -Copyright (C) 2023 Free Software Foundation, Inc. - -This file is part of GNU Emacs. - -GNU Emacs 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 3 of the License, or (at -your option) any later version. - -GNU Emacs 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 GNU Emacs. If not, see . */ - -package org.gnu.emacs; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.PorterDuff.Mode; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.Xfermode; - -public final class EmacsCopyArea -{ - private static Xfermode overAlu; - - static - { - overAlu = new PorterDuffXfermode (Mode.SRC_OVER); - }; - - private static void - insetRectBy (Rect rect, int left, int top, int right, - int bottom) - { - rect.left += left; - rect.top += top; - rect.right -= right; - rect.bottom -= bottom; - } - - public static void - perform (EmacsDrawable source, EmacsGC gc, - EmacsDrawable destination, - int src_x, int src_y, int width, int height, - int dest_x, int dest_y) - { - int i; - Bitmap bitmap; - Paint maskPaint, paint; - Canvas maskCanvas, canvas; - Bitmap srcBitmap, maskBitmap, clipBitmap; - Rect rect, maskRect, srcRect, dstRect, maskDestRect; - boolean needFill; - - /* TODO implement stippling. */ - if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) - return; - - paint = gc.gcPaint; - - canvas = destination.lockCanvas (gc); - - if (canvas == null) - return; - - /* A copy must be created or drawBitmap could end up overwriting - itself. */ - srcBitmap = source.getBitmap (); - - /* If srcBitmap is out of bounds, then adjust the source rectangle - to be within bounds. Note that tiling on windows with - backgrounds is unimplemented. */ - - if (src_x < 0) - { - width += src_x; - dest_x -= src_x; - src_x = 0; - } - - if (src_y < 0) - { - height += src_y; - dest_y -= src_y; - src_y = 0; - } - - if (src_x + width > srcBitmap.getWidth ()) - width = srcBitmap.getWidth () - src_x; - - if (src_y + height > srcBitmap.getHeight ()) - height = srcBitmap.getHeight () - src_y; - - /* If width and height are empty or negative, then skip the entire - CopyArea operation lest createBitmap throw an exception. */ - - if (width <= 0 || height <= 0) - return; - - rect = new Rect (dest_x, dest_y, dest_x + width, - dest_y + height); - - if (gc.clip_mask == null) - { - if (source == destination) - { - /* Create a copy of the bitmap, since Android can't handle - overlapping copies. */ - bitmap = Bitmap.createBitmap (srcBitmap, - src_x, src_y, width, - height); - canvas.drawBitmap (bitmap, null, rect, paint); - bitmap.recycle (); - } - else - { - /* But here the bitmaps are known to not overlap, so avoid - that extra consing overhead. */ - - srcRect = new Rect (src_x, src_y, src_x + width, - src_y + height); - canvas.drawBitmap (srcBitmap, srcRect, rect, paint); - } - } - else - { - /* Drawing with a clip mask involves calculating the - intersection of the clip mask with the dst rect, and - extrapolating the corresponding part of the src rect. */ - clipBitmap = gc.clip_mask.bitmap; - dstRect = new Rect (dest_x, dest_y, - dest_x + width, - dest_y + height); - maskRect = new Rect (gc.clip_x_origin, - gc.clip_y_origin, - (gc.clip_x_origin - + clipBitmap.getWidth ()), - (gc.clip_y_origin - + clipBitmap.getHeight ())); - clipBitmap = gc.clip_mask.bitmap; - - if (!maskRect.setIntersect (dstRect, maskRect)) - /* There is no intersection between the clip mask and the - dest rect. */ - return; - - /* Now figure out which part of the source corresponds to - maskRect and return it relative to srcBitmap. */ - srcRect = new Rect (src_x, src_y, src_x + width, - src_y + height); - insetRectBy (srcRect, maskRect.left - dstRect.left, - maskRect.top - dstRect.top, - maskRect.right - dstRect.right, - maskRect.bottom - dstRect.bottom); - - /* Finally, create a temporary bitmap that is the size of - maskRect. */ - - maskBitmap - = Bitmap.createBitmap (maskRect.width (), maskRect.height (), - Bitmap.Config.ARGB_8888); - - /* Draw the mask onto the maskBitmap. */ - maskCanvas = new Canvas (maskBitmap); - maskPaint = new Paint (); - maskRect.offset (-gc.clip_x_origin, - -gc.clip_y_origin); - maskCanvas.drawBitmap (gc.clip_mask.bitmap, - maskRect, - new Rect (0, 0, - maskRect.width (), - maskRect.height ()), - maskPaint); - maskRect.offset (gc.clip_x_origin, - gc.clip_y_origin); - - /* Set the transfer mode to SRC_IN to preserve only the parts - of the source that overlap with the mask. */ - maskPaint.setXfermode (EmacsGC.srcInAlu); - - /* Draw the source. */ - maskDestRect = new Rect (0, 0, srcRect.width (), - srcRect.height ()); - maskCanvas.drawBitmap (srcBitmap, srcRect, maskDestRect, - maskPaint); - - /* Finally, draw the mask bitmap to the destination. */ - paint.setXfermode (overAlu); - canvas.drawBitmap (maskBitmap, null, maskRect, paint); - gc.resetXfermode (); - - /* Recycle this unused bitmap. */ - maskBitmap.recycle (); - } - - destination.damageRect (rect); - } -} diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index d2e52ed5e62..546d22627c5 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -374,18 +374,6 @@ invocation of app_process (through android-emacs) can EmacsDrawPoint.perform (drawable, gc, x, y); } - public void - copyArea (EmacsDrawable srcDrawable, EmacsDrawable dstDrawable, - EmacsGC gc, - int srcX, int srcY, int width, int height, int destX, - int destY) - { - checkEmacsThread (); - EmacsCopyArea.perform (srcDrawable, gc, dstDrawable, - srcX, srcY, width, height, destX, - destY); - } - public void clearWindow (EmacsWindow window) { diff --git a/src/android.c b/src/android.c index 8a41a7cdec5..33d766a90d9 100644 --- a/src/android.c +++ b/src/android.c @@ -28,6 +28,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include #include +#include #include #include @@ -68,6 +69,10 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include +#ifdef __aarch64__ +#include +#endif /* __aarch64__ */ + #define ANDROID_THROW(env, class, msg) \ ((*(env))->ThrowNew ((env), (*(env))->FindClass ((env), class), msg)) @@ -95,7 +100,6 @@ #define ANDROID_MAX_ASSET_FD 65535 jmethodID draw_rectangle; jmethodID draw_line; jmethodID draw_point; - jmethodID copy_area; jmethodID clear_window; jmethodID clear_area; jmethodID ring_bell; @@ -2178,10 +2182,6 @@ #define FIND_METHOD(c_name, name, signature) \ FIND_METHOD (draw_point, "drawPoint", "(Lorg/gnu/emacs/EmacsDrawable;" "Lorg/gnu/emacs/EmacsGC;II)V"); - FIND_METHOD (copy_area, "copyArea", - "(Lorg/gnu/emacs/EmacsDrawable;" - "Lorg/gnu/emacs/EmacsDrawable;" - "Lorg/gnu/emacs/EmacsGC;IIIIII)V"); FIND_METHOD (clear_window, "clearWindow", "(Lorg/gnu/emacs/EmacsWindow;)V"); FIND_METHOD (clear_area, "clearArea", @@ -3356,6 +3356,16 @@ android_create_gc (enum android_gc_value_mask mask, /* This means to not apply any clipping. */ gc->num_clip_rects = -1; + /* Apply the other default values. */ + gc->function = ANDROID_GC_COPY; + gc->fill_style = ANDROID_FILL_SOLID; + gc->clip_x_origin = 0; + gc->clip_y_origin = 0; + gc->clip_mask = ANDROID_NONE; + gc->stipple = ANDROID_NONE; + gc->ts_x_origin = 0; + gc->ts_y_origin = 0; + if (!gc->gcontext) { xfree (gc); @@ -3430,10 +3440,13 @@ android_change_gc (struct android_gc *gc, } if (mask & ANDROID_GC_FUNCTION) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_function, - values->function); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_function, + values->function); + gc->function = values->function; + } if (mask & ANDROID_GC_CLIP_X_ORIGIN) { @@ -3441,6 +3454,7 @@ android_change_gc (struct android_gc *gc, gcontext, emacs_gc_clip_x_origin, values->clip_x_origin); + gc->clip_x_origin = values->clip_x_origin; clip_changed = true; } @@ -3450,6 +3464,7 @@ android_change_gc (struct android_gc *gc, gcontext, emacs_gc_clip_y_origin, values->clip_y_origin); + gc->clip_y_origin = values->clip_y_origin; clip_changed = true; } @@ -3461,6 +3476,7 @@ android_change_gc (struct android_gc *gc, gcontext, emacs_gc_clip_mask, what); + gc->clip_mask = values->clip_mask; /* Changing GCClipMask also clears the clip rectangles. */ (*android_java_env)->SetObjectField (android_java_env, @@ -3482,25 +3498,35 @@ android_change_gc (struct android_gc *gc, gcontext, emacs_gc_stipple, what); + gc->stipple = values->stipple; } if (mask & ANDROID_GC_FILL_STYLE) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_fill_style, - values->fill_style); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_fill_style, + values->fill_style); + gc->fill_style = values->fill_style; + } if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_ts_origin_x, - values->ts_x_origin); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_ts_origin_x, + values->ts_x_origin); + gc->ts_x_origin = values->ts_x_origin; + } if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN) - (*android_java_env)->SetIntField (android_java_env, - gcontext, - emacs_gc_ts_origin_y, - values->ts_y_origin); + { + (*android_java_env)->SetIntField (android_java_env, + gcontext, + emacs_gc_ts_origin_y, + values->ts_y_origin); + gc->ts_y_origin = values->ts_y_origin; + } if (mask) { @@ -3732,22 +3758,13 @@ android_get_gc_values (struct android_gc *gc, values->background = gc->background; if (mask & ANDROID_GC_FUNCTION) - values->function - = (*android_java_env)->GetIntField (android_java_env, - gcontext, - emacs_gc_function); + values->function = gc->function; if (mask & ANDROID_GC_CLIP_X_ORIGIN) - values->clip_x_origin - = (*android_java_env)->GetIntField (android_java_env, - gcontext, - emacs_gc_clip_x_origin); + values->clip_x_origin = gc->clip_x_origin; if (mask & ANDROID_GC_CLIP_Y_ORIGIN) - values->clip_y_origin - = (*android_java_env)->GetIntField (android_java_env, - gcontext, - emacs_gc_clip_y_origin); + values->clip_y_origin = gc->clip_y_origin; if (mask & ANDROID_GC_FILL_STYLE) values->fill_style @@ -3913,34 +3930,827 @@ android_set_fill_style (struct android_gc *gc, android_change_gc (gc, ANDROID_GC_FILL_STYLE, &gcv); } + + +/* Pixmap bit blit implementation. This exists as `Canvas.drawBitmap' + seems to have trouble with copying bitmap data from one bitmap back + to itself on Android 8.0. */ + +/* Function called to actually perform the copy. */ + +typedef void (*android_blit_func) (int, int, int, int, int, int, + struct android_gc *, + unsigned char *, AndroidBitmapInfo *, + unsigned char *, AndroidBitmapInfo *, + unsigned char *, AndroidBitmapInfo *); + + + +#ifdef __aarch64__ + +/* Copy N pixels from SRC to DST, using MASK as a depth 1 clip + mask. */ + +static void +android_neon_mask_line (unsigned int *src, unsigned int *dst, + unsigned char *mask, int n) +{ + uint32x4_t src_low, src_high, dst_low, dst_high; + int16x8_t vmask; + int32x4_t ext_mask_low, ext_mask_high, low, high; + int rem; + + /* Calculate the remainder. */ + rem = n & 7; + + /* Process eight pixels at a time. */ + + if (n -= rem) + { + again: + /* Load the low and high four pixels from the source. */ + src_low = vld1q_u32 (src); + src_high = vld1q_u32 (src + 4); + + /* Do the same with the destination. */ + dst_low = vld1q_u32 (dst); + dst_high = vld1q_u32 (dst + 4); + + /* Load and sign extend the mask. */ + vmask = vmovl_s8 (vld1_u8 (mask)); + ext_mask_low = vmovl_s16 (vget_low_s16 (vmask)); + ext_mask_high = vmovl_s16 (vget_high_s16 (vmask)); + + /* Reinterpret the mask. */ + low = vreinterpretq_u32_s32 (ext_mask_low); + high = vreinterpretq_u32_s32 (ext_mask_high); + + /* Apply the mask. */ + dst_low = vbicq_u32 (dst_low, low); + src_low = vandq_u32 (src_low, low); + dst_high = vbicq_u32 (dst_high, high); + src_high = vandq_u32 (src_high, high); + + /* Write the result after combining both masked vectors. */ + vst1q_u32 (dst, vorrq_u32 (dst_low, src_low)); + vst1q_u32 (dst + 4, vorrq_u32 (dst_high, src_high)); + + /* Adjust src, dst and mask. */ + dst += 8; + src += 8; + mask += 8; + + /* See if this loop should continue. */ + n -= 8; + if (n > 0) + goto again; + } + + /* Process the remaining pixels. */ + + while (--rem) + { + /* Sign extend the mask. */ + n = *(signed char *) mask++; + + /* Combine src and dst. */ + *dst = ((*src & n) | (*dst & ~n)); + src++, dst++; + } +} + +#endif /* __aarch64__ */ + + + +/* Copy a rectangle SRC_X, SRC_Y, WIDTH and HEIGHT from SRC, described + by SRC_INFO, to DST_X and DST_Y in DST, as described by DST_INFO. + + If MASK is set, mask the source data using MASK_INFO, translating + it by GC->clip_x_origin and GC->clip_y_origin. MASK must be a + pixmap of depth 1. + + N.B. that currently only copies between bitmaps of depth 24 are + implemented. */ + +void +android_blit_copy (int src_x, int src_y, int width, int height, + int dst_x, int dst_y, struct android_gc *gc, + unsigned char *src, AndroidBitmapInfo *src_info, + unsigned char *dst, AndroidBitmapInfo *dst_info, + unsigned char *mask, AndroidBitmapInfo *mask_info) +{ + uintptr_t start, end; + int mask_offset; + size_t pixel, offset, offset1; + unsigned char *src_current, *dst_current; + unsigned char *mask_current; + int overflow, temp, i, xdir; + bool backwards; + unsigned int *long_src, *long_dst; + + /* Assert that the specified coordinates are within bounds. */ + eassert (src_x >= 0 && src_y >= 0 + && dst_x >= 0 && dst_y >= 0); + eassert (src_x + width <= src_info->width); + eassert (src_y + height <= src_info->height); + eassert (dst_x + width <= dst_info->width); + eassert (dst_y + height <= dst_info->height); + + /* Now check that each bitmap has the correct format. */ + eassert (src_info->format == dst_info->format + && src_info->format == ANDROID_BITMAP_FORMAT_RGBA_8888); + pixel = sizeof (unsigned int); + + /* Android doesn't have A1 bitmaps, so A8 is used to represent + packed bitmaps of depth 1. */ + eassert (!mask || mask_info->format == ANDROID_BITMAP_FORMAT_A_8); + + /* Calculate the address of the first pixel of the first row to be + copied in both src and dst. Compare them to determine the + direction in which the copy is to take place. */ + + overflow = ckd_mul (&start, src_y, src_info->stride); + overflow |= ckd_mul (&end, src_x, pixel); + overflow |= ckd_add (&start, end, start); + overflow |= ckd_add (&start, (uintptr_t) src, start); + + if (overflow) + return; + + src_current = (unsigned char *) start; + + overflow = ckd_mul (&start, dst_y, dst_info->stride); + overflow |= ckd_mul (&end, dst_x, pixel); + overflow |= ckd_add (&start, end, start); + overflow |= ckd_add (&start, (uintptr_t) dst, start); + + if (overflow) + return; + + dst_current = (unsigned char *) start; + backwards = false; + + /* Now see if copying should proceed from the bottom up. */ + + if (src == dst && dst_current >= src_current) + { + backwards = true; + + /* Walk src and dst from bottom to top, in order to avoid + overlap. Calculate the coordinate of the last pixel of the + last row in both src and dst. */ + + overflow = ckd_mul (&start, src_y + height - 1, + src_info->stride); + if (mask) /* If a mask is set, put the pointers before the end + of the row. */ + overflow |= ckd_mul (&end, src_x + width - 1, pixel); + else + overflow |= ckd_mul (&end, src_x, pixel); + overflow |= ckd_add (&start, start, end); + overflow |= ckd_add (&start, (uintptr_t) src, start); + + if (overflow) + return; + + src_current = (unsigned char *) start; + + overflow = ckd_mul (&start, dst_y + height - 1, + dst_info->stride); + if (mask) /* If a mask is set, put the pointers before the end + of the row. */ + overflow |= ckd_mul (&end, dst_x + width - 1, pixel); + else + overflow |= ckd_mul (&end, dst_x, pixel); + overflow |= ckd_add (&start, start, end); + overflow |= ckd_add (&start, (uintptr_t) dst, start); + + if (overflow) + return; + + dst_current = (unsigned char *) start; + } + + if (!mask) + { + /* Change the direction of the copy depending on how SRC and DST + overlap. */ + + for (i = 0; i < height; ++i) + { + memmove (dst_current, src_current, + width * pixel); + + if (backwards) + { + /* Proceed to the last row. */ + src_current -= src_info->stride; + dst_current -= dst_info->stride; + } + else + { + /* Proceed to the next row. */ + src_current += src_info->stride; + dst_current += dst_info->stride; + } + } + } + else + { + /* Adjust the source and destination Y. The start is MAX + (dst_y, gc->clip_y_origin); the difference between that value + and dst_y is the offset to apply to src_y. */ + + temp = dst_y; + dst_y = MAX (dst_y, gc->clip_y_origin); + src_y += dst_y - temp; + height -= dst_y - temp; + + /* Verify that the bounds are correct. */ + eassert (dst_y + height + <= gc->clip_y_origin + mask_info->height); + eassert (dst_y >= gc->clip_y_origin); + + /* There is a mask. For each scan line... */ + + if (backwards) + { + /* Calculate the number of pixels at the end of the + mask. */ + + mask_offset = dst_x + width; + mask_offset -= mask_info->width + gc->clip_x_origin; + + if (mask_offset < 0) + mask_offset = 0; + + /* Calculate the last column of the mask that will be + consulted. */ + + temp = dst_x - gc->clip_x_origin; + temp += MIN (mask_info->width - temp, + width - mask_offset); + + if (temp < 0) + return; + + /* Now calculate the last row of the mask that will be + consulted. */ + i = dst_y - gc->clip_y_origin + height; + + /* Turn both into offsets. */ + + if (INT_MULTIPLY_WRAPV (temp, pixel, &offset) + || INT_MULTIPLY_WRAPV (i, mask_info->stride, &offset1) + || INT_ADD_WRAPV (offset, offset1, &offset) + || INT_ADD_WRAPV ((uintptr_t) mask, offset, &start)) + return; + + mask = mask_current = (unsigned char *) start; + + while (--height) + { + /* Skip backwards past the end of the mask. */ + + long_src = (unsigned int *) (src_current - mask_offset * pixel); + long_dst = (unsigned int *) (dst_current - mask_offset * pixel); + mask = mask_current; + + /* For each pixel covered by the mask... */ + temp = MIN (mask_info->width - temp, width - mask_offset); + while (temp--) + { + /* Copy the destination it to the source, masked by + the mask. */ + + /* Sign extend the mask. */ + i = *(signed char *) mask--; + + /* Apply the mask. */ + *long_dst = ((*long_src & i) | (*long_dst & ~i)); + + long_dst--; + long_src--; + } + + /* Return to the last row. */ + src_current -= src_info->stride; + dst_current -= dst_info->stride; + mask_current -= mask_info->stride; + } + } + else + { + /* Calculate the first column of the mask that will be + consulted. */ + + mask_offset = dst_x - gc->clip_x_origin; + + /* Adjust the mask by that much. */ + + if (mask_offset > 0) + mask += mask_offset; + else + { + /* Offset src and dst by the mask offset. */ + src_current += -mask_offset * pixel; + dst_current += -mask_offset * pixel; + width += mask_offset; + } + + /* Make sure it's not out of bounds. */ + + eassert (dst_y - gc->clip_y_origin >= 0); + if ((dst_y - gc->clip_y_origin) + height > mask_info->height) + return; + + /* Now move mask to the position of the first row. */ + + mask += ((dst_y - gc->clip_y_origin) + * mask_info->stride); + + /* Determine how many bytes need to be copied. */ + + if (mask_offset > 0) + temp = MIN (mask_info->width - mask_offset, width); + else + temp = MIN (mask_info->width, width); + + /* Copy bytes according to the mask. */ + + while (--height) + { + long_src = (unsigned int *) src_current; + long_dst = (unsigned int *) dst_current; + mask_current = mask; + +#ifndef __aarch64__ + while (temp--) + { + /* Sign extend the mask. */ + height = *(signed char *) mask_current++; + + /* Apply the mask. */ + *long_dst = ((*long_src & height) + | (*long_dst & ~height)); + } +#else /* __aarch64__ */ + android_neon_mask_line (long_src, long_dst, mask, temp); +#endif /* __aarch64__ */ + + src_current += src_info->stride; + dst_current += dst_info->stride; + mask += mask_info->stride; + } + } + } +} + + +/* Xor a rectangle SRC_X, SRC_Y, WIDTH and HEIGHT from SRC, described + by SRC_INFO, to DST_X and DST_Y in DST, as described by DST_INFO. + + Ignore the alpha channel when computing the exclusive-or of the + destination pixel. + + If MASK is set, mask the source data using MASK_INFO, translating + it by GC->clip_x_origin and GC->clip_y_origin. MASK must be a + pixmap of depth 1. + + N.B. that currently only copies between bitmaps of depth 24 are + implemented. */ + +void +android_blit_xor (int src_x, int src_y, int width, int height, + int dst_x, int dst_y, struct android_gc *gc, + unsigned char *src, AndroidBitmapInfo *src_info, + unsigned char *dst, AndroidBitmapInfo *dst_info, + unsigned char *mask, AndroidBitmapInfo *mask_info) +{ + uintptr_t start, end; + int mask_offset; + size_t pixel, offset, offset1; + unsigned char *src_current, *dst_current; + unsigned char *mask_current; + int overflow, temp, i, xdir; + bool backwards; + unsigned int *long_src, *long_dst; + + /* Note that this alu hasn't been tested -- it probably does not + work! */ + emacs_abort (); + +#if 0 + /* Assert that the specified coordinates are within bounds. */ + eassert (src_x >= 0 && src_y >= 0 + && dst_x >= 0 && dst_y >= 0); + eassert (src_x + width <= src_info->width); + eassert (src_y + height <= src_info->height); + eassert (dst_x + width <= dst_info->width); + eassert (dst_y + height <= dst_info->height); + + /* Now check that each bitmap has the correct format. */ + eassert (src_info->format == dst_info->format + && src_info->format == ANDROID_BITMAP_FORMAT_RGBA_8888); + pixel = sizeof (unsigned int); + + /* Android doesn't have A1 bitmaps, so A8 is used to represent + packed bitmaps of depth 1. */ + eassert (!mask || mask_info->format == ANDROID_BITMAP_FORMAT_A_8); + + /* Calculate the address of the first pixel of the first row to be + copied in both src and dst. Compare them to determine the + direction in which the copy is to take place. */ + + overflow = ckd_mul (&start, src_y, src_info->stride); + overflow |= ckd_mul (&end, src_x, pixel); + overflow |= ckd_add (&start, (uintptr_t) src, start); + + if (overflow) + return; + + src_current = (unsigned char *) start; + + overflow = ckd_mul (&start, dst_y, src_info->stride); + overflow |= ckd_mul (&end, dst_x, pixel); + overflow |= ckd_add (&start, (uintptr_t) dst, start); + + if (overflow) + return; + + dst_current = (unsigned char *) start; + backwards = false; + + /* Now see if copying should proceed from the bottom up. */ + + if (src == dst && dst_current >= src_current) + { + backwards = true; + + /* Walk src and dst from bottom to top, in order to avoid + overlap. Calculate the coordinate of the last pixel of the + last row in both src and dst. */ + + overflow = ckd_mul (&start, src_y + height - 1, + src_info->stride); + if (mask) /* If a mask is set, put the pointers before the end + of the row. */ + overflow |= ckd_mul (&end, src_x + width - 1, pixel); + else + overflow |= ckd_mul (&end, src_x, pixel); + overflow |= ckd_add (&start, start, end); + overflow |= ckd_add (&start, (uintptr_t) src, start); + + if (overflow) + return; + + src_current = (unsigned char *) start; + + overflow = ckd_mul (&start, dst_y + height - 1, + dst_info->stride); + if (mask) /* If a mask is set, put the pointers before the end + of the row. */ + overflow |= ckd_mul (&end, dst_x + width - 1, pixel); + else + overflow |= ckd_mul (&end, dst_x, pixel); + overflow |= ckd_add (&start, start, end); + overflow |= ckd_add (&start, (uintptr_t) dst, start); + + if (overflow) + return; + + dst_current = (unsigned char *) start; + } + + if (!mask) + { + /* Change the direction of the copy depending on how SRC and DST + overlap. */ + + for (i = 0; i < height; ++i) + { + if (backwards) + { + for (i = width - 1; i <= 0; --i) + (((unsigned int *) dst_current)[i]) + /* Keep the alpha channel intact. */ + ^= (((unsigned int *) src_current)[i]) & 0xffffff; + + /* Proceed to the last row. */ + src_current -= src_info->stride; + dst_current -= dst_info->stride; + } + else + { + for (i = 0; i < width; ++i) + (((unsigned int *) dst_current)[i]) + /* Keep the alpha channel intact. */ + ^= (((unsigned int *) src_current)[i]) & 0xffffff; + + /* Proceed to the next row. */ + src_current += src_info->stride; + dst_current += dst_info->stride; + } + } + } + else + { + /* Adjust the source and destination Y. The start is MAX + (dst_y, gc->clip_y_origin); the difference between that value + and dst_y is the offset to apply to src_y. */ + + temp = dst_y; + dst_y = MAX (dst_y, gc->clip_y_origin); + src_y += dst_y - temp; + height -= dst_y - temp; + + /* Verify that the bounds are correct. */ + eassert (dst_y + height + <= gc->clip_y_origin + mask_info->height); + eassert (dst_y >= gc->clip_y_origin); + + /* There is a mask. For each scan line... */ + + if (backwards) + { + /* Calculate the number of pixels at the end of the + mask. */ + + mask_offset = dst_x + width; + mask_offset -= mask_info->width + gc->clip_x_origin; + + if (mask_info < 0) + mask_info = 0; + + /* Calculate the last column of the mask that will be + consulted. */ + + temp = dst_x - gc->clip_x_origin; + temp += MIN (mask_info->width - temp, + width - mask_offset); + + if (temp < 0) + return; + + /* Now calculate the last row of the mask that will be + consulted. */ + i = dst_y - gc->clip_y_origin + height; + + /* Turn both into offsets. */ + + if (INT_MULTIPLY_WRAPV (temp, pixel, &offset) + || INT_MULTIPLY_WRAPV (i, mask_info->stride, &offset1) + || INT_ADD_WRAPV (offset, offset1, &offset) + || INT_ADD_WRAPV ((uintptr_t) mask, offset, &start)) + return; + + mask = mask_current = (unsigned char *) start; + + for (i = 0; i < height; ++i) + { + /* Skip backwards past the end of the mask. */ + + long_src = (unsigned int *) (src_current - mask_offset * pixel); + long_dst = (unsigned int *) (dst_current - mask_offset * pixel); + mask = mask_current; + + /* For each pixel covered by the mask... */ + temp = MIN (mask_info->width - temp, width - mask_offset); + while (temp--) + /* XOR the source to the destination, masked by the + mask. */ + *long_dst-- ^= ((*(long_src--) & (0u - (*(mask--) & 1))) + & 0xffffff); + + /* Return to the last row. */ + src_current -= src_info->stride; + dst_current -= dst_info->stride; + mask_current -= mask_info->stride; + } + } + else + { + /* Calculate the first column of the mask that will be + consulted. */ + + mask_offset = dst_x - gc->clip_x_origin; + + /* Adjust the mask by that much. */ + + if (mask_offset > 0) + mask += mask_offset; + else + { + /* Offset src and dst by the mask offset. */ + src_current += -mask_offset * pixel; + dst_current += -mask_offset * pixel; + width -= mask_offset; + } + + /* Now move mask to the position of the first row. */ + + mask += gc->clip_y_origin * mask_info->stride; + + for (i = 0; i < height; ++i) + { + long_src = (unsigned int *) src_current; + long_dst = (unsigned int *) dst_current; + mask_current = mask; + + if (mask_offset > 0) + { + /* Copy bytes according to the mask. */ + temp = MIN (mask_info->width - mask_offset, width); + while (temp--) + *long_dst++ ^= ((*(long_src++) + & (0u - (*(mask_current++) & 1))) + & 0xffffff); + } + else + { + /* Copy bytes according to the mask. */ + temp = MIN (mask_info->width, width); + while (temp--) + *long_dst++ = ((*(long_src++) + & (0u - (*(mask_current++) & 1))) + & 0xffffff); + } + + src_current += src_info->stride; + dst_current += dst_info->stride; + mask += mask_info->stride; + } + } + } +#endif /* 0 */ +} + void android_copy_area (android_drawable src, android_drawable dest, struct android_gc *gc, int src_x, int src_y, unsigned int width, unsigned int height, int dest_x, int dest_y) { - jobject src_object, dest_object, gcontext; + jobject src_object, dest_object, mask; + android_blit_func do_blit; + AndroidBitmapInfo src_info, dest_info, mask_info; + void *src_data, *dest_data, *mask_data; + int n_clip_rects, i; + bool flag; + struct android_rectangle bounds, rect, temp, *clip_rectangles; - src_object = android_resolve_handle2 (src, ANDROID_HANDLE_WINDOW, - ANDROID_HANDLE_PIXMAP); - dest_object = android_resolve_handle2 (dest, ANDROID_HANDLE_WINDOW, - ANDROID_HANDLE_PIXMAP); - gcontext = android_resolve_handle (gc->gcontext, - ANDROID_HANDLE_GCONTEXT); + /* Perform the copy. Loop over each clip rectangle, unless none are + set. Also, obtain bitmaps for src and dst, and possibly the mask + as well if it is present. */ - (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, - emacs_service, - service_class.class, - service_class.copy_area, - src_object, - dest_object, - gcontext, - (jint) src_x, (jint) src_y, - (jint) width, (jint) height, - (jint) dest_x, (jint) dest_y); - android_exception_check (); + src_data = android_lock_bitmap (src, &src_info, &src_object); + if (!src_data) + return; + + mask_data = mask = NULL; + + if (src != dest) + { + dest_data = android_lock_bitmap (dest, &dest_info, &dest_object); + if (!dest_data) + goto fail; + } + else + { + dest_data = src_data; + dest_info = src_info; + } + + /* Obtain the bitmap for the mask if necessary. */ + + if (gc->clip_mask) + { + mask_data = android_lock_bitmap (gc->clip_mask, + &mask_info, &mask); + if (!mask_data) + goto fail1; + } + + /* Calculate the number of clip rectangles. */ + n_clip_rects = gc->num_clip_rects; + + /* If n_clip_rects is -1, then no clipping is in effect. Set rect + to the bounds of the destination. */ + + flag = n_clip_rects == -1; + if (flag) + { + n_clip_rects = 1; + clip_rectangles = ▭ + } + else if (!n_clip_rects) + goto fail2; + else + clip_rectangles = gc->clip_rects; + + /* Set rect to the bounds of the destination. */ + + rect.x = 0; + rect.y = 0; + rect.width = dest_info.width; + rect.height = dest_info.height; + + if (mask_data) + { + /* Clip width and height to that of the mask. */ + + if (src_x + width > mask_info.width) + width = mask_info.width - src_x; + + if (src_y + height > mask_info.height) + height = mask_info.height - src_y; + } + + /* Clip width and height to that of the source. */ + + if (src_x + width > src_info.width) + width = src_info.width - src_x; + + if (src_y + height > src_info.height) + height = src_info.height - src_y; + + /* Return if the copy is outside the source. */ + + if (width <= 0 || height <= 0) + goto fail2; + + /* Look up the right function for the alu. */ + + switch (gc->function) + { + case ANDROID_GC_COPY: + do_blit = android_blit_copy; + break; + + case ANDROID_GC_XOR: + do_blit = android_blit_xor; + break; + } + + /* Load the bounds of the destination rectangle. */ + bounds.x = dest_x; + bounds.y = dest_y; + bounds.width = width; + bounds.height = height; + + /* For each clip rectangle... */ + for (i = 0; i < n_clip_rects; ++i) + { + /* Calculate its intersection with the destination + rectangle. */ + + if (!gui_intersect_rectangles (&clip_rectangles[i], &bounds, + &temp)) + continue; + + /* And that of the destination itself. */ + + if (!flag && !gui_intersect_rectangles (&temp, &rect, &temp)) + continue; + + /* Now perform the copy. */ + (*do_blit) (src_x + temp.x - dest_x, /* temp.x relative to src_x */ + src_y + temp.y - dest_y, /* temp.y relative to src_y */ + temp.width, /* Width of area to copy. */ + temp.height, /* Height of area to copy. */ + temp.x, temp.y, /* Coordinates to copy to. */ + gc, /* GC. */ + src_data, &src_info, /* Source drawable. */ + dest_data, &dest_info, /* Destination drawable. */ + mask_data, &mask_info); /* Mask drawable. */ + } + + /* Now damage the destination drawable accordingly, should it be a + window. */ + + if (android_handles[dest].type == ANDROID_HANDLE_WINDOW) + android_damage_window (dest, &bounds); + + fail2: + if (mask) + { + AndroidBitmap_unlockPixels (android_java_env, mask); + ANDROID_DELETE_LOCAL_REF (mask); + } + fail1: + if (src != dest) + { + AndroidBitmap_unlockPixels (android_java_env, dest_object); + ANDROID_DELETE_LOCAL_REF (dest_object); + } + fail: + AndroidBitmap_unlockPixels (android_java_env, src_object); + ANDROID_DELETE_LOCAL_REF (src_object); } + + void android_free_pixmap (android_pixmap pixmap) { @@ -4815,26 +5625,27 @@ android_wc_lookup_string (android_key_pressed_event *event, /* Low level drawing primitives. */ -/* Lock the bitmap corresponding to the window WINDOW. Return the +/* Lock the bitmap corresponding to the drawable DRAWABLE. Return the bitmap data upon success, and store the bitmap object in BITMAP_RETURN. Value is NULL upon failure. The caller must take care to unlock the bitmap data afterwards. */ unsigned char * -android_lock_bitmap (android_window window, +android_lock_bitmap (android_window drawable, AndroidBitmapInfo *bitmap_info, jobject *bitmap_return) { - jobject drawable, bitmap; + jobject object, bitmap; void *data; - drawable = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); + object = android_resolve_handle2 (drawable, ANDROID_HANDLE_WINDOW, + ANDROID_HANDLE_PIXMAP); /* Look up the drawable and get the bitmap corresponding to it. Then, lock the bitmap's bits. */ bitmap = (*android_java_env)->CallObjectMethod (android_java_env, - drawable, + object, drawable_class.get_bitmap); if (!bitmap) /* NULL is returned when the bitmap does not currently exist due diff --git a/src/android.h b/src/android.h index 62d420d4cce..d0440259161 100644 --- a/src/android.h +++ b/src/android.h @@ -68,7 +68,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. extern jobject android_resolve_handle (android_handle, enum android_handle_type); -extern unsigned char *android_lock_bitmap (android_window, +extern unsigned char *android_lock_bitmap (android_drawable, AndroidBitmapInfo *, jobject *); extern void android_damage_window (android_window, diff --git a/src/androidgui.h b/src/androidgui.h index 6db25098398..02cc73809b9 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -137,6 +137,21 @@ #define _ANDROID_GUI_H_ /* Current background color. */ unsigned long background; + + /* The function. */ + enum android_gc_function function; + + /* The fill style. */ + enum android_fill_style fill_style; + + /* The clip X and Y origin. */ + int clip_x_origin, clip_y_origin; + + /* The clip mask image and stipple. */ + android_pixmap clip_mask, stipple; + + /* The tile-stipple X and Y origins. */ + int ts_x_origin, ts_y_origin; }; enum android_swap_action commit 327d2d013130ec745880b44573dcda3a6620faba Author: Po Lu Date: Sat May 27 16:30:12 2023 +0800 Add extra thread-related checking * java/org/gnu/emacs/EmacsService.java (EmacsService) (checkEmacsThread): New function. (fillPolygon, drawRectangle, drawLine, drawPoint, copyArea) (clearArea): * java/org/gnu/emacs/EmacsThread.java (EmacsThread): * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers): Call where appropriate. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index bb17d27bcf8..d2e52ed5e62 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -100,6 +100,10 @@ public final class EmacsService extends Service information. */ public static final boolean DEBUG_IC = false; + /* Flag that says whether or not to perform extra checks on threads + performing drawing calls. */ + private static final boolean DEBUG_THREADS = false; + /* Return the directory leading to the directory in which native library files are stored on behalf of CONTEXT. */ @@ -309,10 +313,29 @@ invocation of app_process (through android-emacs) can syncRunnable (runnable); } + + + public static void + checkEmacsThread () + { + if (DEBUG_THREADS) + { + if (Thread.currentThread () instanceof EmacsThread) + return; + + throw new RuntimeException ("Emacs thread function" + + " called from other thread!"); + } + } + + /* These drawing functions must only be called from the Emacs + thread. */ + public void fillRectangle (EmacsDrawable drawable, EmacsGC gc, int x, int y, int width, int height) { + checkEmacsThread (); EmacsFillRectangle.perform (drawable, gc, x, y, width, height); } @@ -321,6 +344,7 @@ invocation of app_process (through android-emacs) can fillPolygon (EmacsDrawable drawable, EmacsGC gc, Point points[]) { + checkEmacsThread (); EmacsFillPolygon.perform (drawable, gc, points); } @@ -328,6 +352,7 @@ invocation of app_process (through android-emacs) can drawRectangle (EmacsDrawable drawable, EmacsGC gc, int x, int y, int width, int height) { + checkEmacsThread (); EmacsDrawRectangle.perform (drawable, gc, x, y, width, height); } @@ -336,6 +361,7 @@ invocation of app_process (through android-emacs) can drawLine (EmacsDrawable drawable, EmacsGC gc, int x, int y, int x2, int y2) { + checkEmacsThread (); EmacsDrawLine.perform (drawable, gc, x, y, x2, y2); } @@ -344,6 +370,7 @@ invocation of app_process (through android-emacs) can drawPoint (EmacsDrawable drawable, EmacsGC gc, int x, int y) { + checkEmacsThread (); EmacsDrawPoint.perform (drawable, gc, x, y); } @@ -353,6 +380,7 @@ invocation of app_process (through android-emacs) can int srcX, int srcY, int width, int height, int destX, int destY) { + checkEmacsThread (); EmacsCopyArea.perform (srcDrawable, gc, dstDrawable, srcX, srcY, width, height, destX, destY); @@ -361,6 +389,7 @@ invocation of app_process (through android-emacs) can public void clearWindow (EmacsWindow window) { + checkEmacsThread (); window.clearWindow (); } @@ -368,6 +397,7 @@ invocation of app_process (through android-emacs) can clearArea (EmacsWindow window, int x, int y, int width, int height) { + checkEmacsThread (); window.clearArea (x, y, width, height); } diff --git a/java/org/gnu/emacs/EmacsThread.java b/java/org/gnu/emacs/EmacsThread.java index 468c6530af0..1343b70bf5a 100644 --- a/java/org/gnu/emacs/EmacsThread.java +++ b/java/org/gnu/emacs/EmacsThread.java @@ -25,7 +25,7 @@ import android.os.Build; import android.util.Log; -public class EmacsThread extends Thread +public final class EmacsThread extends Thread { private static final String TAG = "EmacsThread"; diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index eb1d88ae242..09bc9d719d3 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -338,6 +338,7 @@ else if (child.getVisibility () != GONE) public void damageRect (Rect damageRect) { + EmacsService.checkEmacsThread (); damageRegion.union (damageRect); } @@ -351,6 +352,10 @@ else if (child.getVisibility () != GONE) Rect damageRect; Bitmap bitmap; + /* Make sure this function is called only from the Emacs + thread. */ + EmacsService.checkEmacsThread (); + damageRect = null; /* Now see if there is a damage region. */ commit d33bf0a0afddb76598aa020f68402d0e19cfb65c Author: Po Lu Date: Sat May 27 15:19:02 2023 +0800 Remove synchronization around `damageRegion' * java/org/gnu/emacs/EmacsView.java (EmacsView, swapBuffers): Remove unnecessary documentation. `damageRegion' is only changed from the Emacs thread. diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 124ea5301bb..eb1d88ae242 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -338,10 +338,7 @@ else if (child.getVisibility () != GONE) public void damageRect (Rect damageRect) { - synchronized (damageRegion) - { - damageRegion.union (damageRect); - } + damageRegion.union (damageRect); } /* This method is called from both the UI thread and the Emacs @@ -358,22 +355,19 @@ else if (child.getVisibility () != GONE) /* Now see if there is a damage region. */ - synchronized (damageRegion) - { - if (damageRegion.isEmpty ()) - return; + if (damageRegion.isEmpty ()) + return; - /* And extract and clear the damage region. */ + /* And extract and clear the damage region. */ - damageRect = damageRegion.getBounds (); - damageRegion.setEmpty (); + damageRect = damageRegion.getBounds (); + damageRegion.setEmpty (); - bitmap = getBitmap (); + bitmap = getBitmap (); - /* Transfer the bitmap to the surface view, then invalidate - it. */ - surfaceView.setBitmap (bitmap, damageRect); - } + /* Transfer the bitmap to the surface view, then invalidate + it. */ + surfaceView.setBitmap (bitmap, damageRect); } @Override commit cdca0fddcc3352bcd01bec147c264be1b2a04e12 Merge: 0eb1f4e5712 e77e986a9b7 Author: Po Lu Date: Sat May 27 09:49:49 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 0eb1f4e57125117006f109a5549082008fc9fbb1 Author: Po Lu Date: Fri May 26 15:20:39 2023 +0800 Allow starting Emacs --debug-init on Android * doc/emacs/android.texi (Android Troubleshooting): Document `debug-init' option. * java/AndroidManifest.xml.in (EmacsLauncherPreferencesActivity): New activity. Export on systems older than Android 7.0. * java/org/gnu/emacs/EmacsActivity.java (onCreate): Adjust for string startup argument. * java/org/gnu/emacs/EmacsLauncherPreferencesActivity.java: New file. * java/org/gnu/emacs/EmacsPreferencesActivity.java (EmacsPreferencesActivity): Don't make final. (startEmacsQ): Give start-up argument as an argument, not as a boolean. (startEmacsDebugInit): New function. (onCreate): Register new listener; make final. * java/org/gnu/emacs/EmacsService.java (onCreate): Pass extraStartupArgument. * java/org/gnu/emacs/EmacsThread.java (EmacsThread): Rename startDashQ to extraStartupArgument. (run): Adjust accordingly. * java/res/values-v24/bool.xml: * java/res/values/bool.xml: * java/res/values/strings.xml: New files. * java/res/xml/preferences.xml: Add new option. Move string resources around. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index d7fadd69e4b..d94b91c7ab7 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -608,16 +608,20 @@ Android Troubleshooting @cindex troubleshooting, android @cindex emacs -Q, android +@cindex emacs --debug-init, android Since Android has no command line, there is normally no way to specify command-line arguments when starting Emacs. This is very nasty when you make a mistake in your Emacs initialization files that prevents Emacs from starting up at all, as the system normally prevents other programs from accessing Emacs's home directory. - - However, Emacs can be started with the equivalent of the -@code{--quick} option (@pxref{Initial Options}) through a special -preferences screen, which can be accessed through the Emacs ``app -info'' page in the system settings application. +@xref{Initial Options}. + + However, Emacs can be started with the equivalent of either the +option @code{--quick}, or @code{--debug-init}, through a special +preferences screen. Under Android 7.0 and later, this can be accessed +through the Emacs ``app info'' page in the system settings program; on +older systems, this is displayed as a separate icon on the desktop +labeled ``Emacs options''. Consult the manufacturer of your device for more details, as how to do this varies by device. diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in index f7f834e7582..082c4c9373e 100644 --- a/java/AndroidManifest.xml.in +++ b/java/AndroidManifest.xml.in @@ -180,6 +180,23 @@ along with GNU Emacs. If not, see . --> + + + + + + + + + + . */ + +package org.gnu.emacs; + +/* This class only exists because EmacsPreferencesActivity is already + defined as an activity, the system wants a new class in order to + define a new activity, and only activities can be enabled or + disabled per the API level of the host. */ + +public final class EmacsLauncherPreferencesActivity + extends EmacsPreferencesActivity +{ + +} diff --git a/java/org/gnu/emacs/EmacsPreferencesActivity.java b/java/org/gnu/emacs/EmacsPreferencesActivity.java index 70934fa4bd4..7e67cc3679b 100644 --- a/java/org/gnu/emacs/EmacsPreferencesActivity.java +++ b/java/org/gnu/emacs/EmacsPreferencesActivity.java @@ -42,10 +42,11 @@ Unfortunately, there is no alternative that looks the same way. */ @SuppressWarnings ("deprecation") -public final class EmacsPreferencesActivity extends PreferenceActivity +public class EmacsPreferencesActivity extends PreferenceActivity { - /* Restart Emacs with -Q. Call EmacsThread.exit to kill Emacs now, and - tell the system to EmacsActivity with some parameters later. */ + /* Restart Emacs with -Q. Call EmacsThread.exit to kill Emacs now, + and tell the system to start EmacsActivity with some parameters + later. */ private void startEmacsQ () @@ -55,7 +56,24 @@ public final class EmacsPreferencesActivity extends PreferenceActivity intent = new Intent (this, EmacsActivity.class); intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - intent.putExtra ("org.gnu.emacs.START_DASH_Q", true); + intent.putExtra ("org.gnu.emacs.STARTUP_ARGUMENT", "--quick"); + startActivity (intent); + System.exit (0); + } + + /* Restart Emacs with `--debug-init'. Call EmacsThread.exit to kill + Emacs now, and tell the system to EmacsActivity with some + parameters later. */ + + private void + startEmacsDebugInit () + { + Intent intent; + + intent = new Intent (this, EmacsActivity.class); + intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.putExtra ("org.gnu.emacs.STARTUP_ARGUMENT", "--debug-init"); startActivity (intent); System.exit (0); } @@ -89,7 +107,7 @@ public final class EmacsPreferencesActivity extends PreferenceActivity } @Override - public void + public final void onCreate (Bundle savedInstanceState) { Preference tem; @@ -111,7 +129,6 @@ else if (Build.VERSION.SDK_INT items. */ tem = findPreference ("start_quick"); - listener = new Preference.OnPreferenceClickListener () { @Override public boolean @@ -123,9 +140,19 @@ else if (Build.VERSION.SDK_INT }; tem.setOnPreferenceClickListener (listener); + tem = findPreference ("start_debug_init"); + listener = new Preference.OnPreferenceClickListener () { + @Override + public boolean + onPreferenceClick (Preference preference) + { + startEmacsDebugInit (); + return true; + } + }; + tem.setOnPreferenceClickListener (listener); tem = findPreference ("erase_dump"); - listener = new Preference.OnPreferenceClickListener () { @Override public boolean diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 30ef71540a9..bb17d27bcf8 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -79,7 +79,10 @@ public final class EmacsService extends Service { public static final String TAG = "EmacsService"; public static volatile EmacsService SERVICE; - public static boolean needDashQ; + + /* If non-NULL, an extra argument to pass to + `android_emacs_init'. */ + public static String extraStartupArgument; private EmacsThread thread; private Handler handler; @@ -231,7 +234,7 @@ invocation of app_process (through android-emacs) can (float) pixelDensityY, classPath, EmacsService.this); } - }, needDashQ, + }, extraStartupArgument, /* If any file needs to be opened, open it now. */ EmacsOpenActivity.fileToOpen); thread.start (); diff --git a/java/org/gnu/emacs/EmacsThread.java b/java/org/gnu/emacs/EmacsThread.java index d175fe332b5..468c6530af0 100644 --- a/java/org/gnu/emacs/EmacsThread.java +++ b/java/org/gnu/emacs/EmacsThread.java @@ -29,8 +29,9 @@ public class EmacsThread extends Thread { private static final String TAG = "EmacsThread"; - /* Whether or not Emacs should be started -Q. */ - private boolean startDashQ; + /* Whether or not Emacs should be started with an additional + argument, and that additional argument if non-NULL. */ + private String extraStartupArgument; /* Runnable run to initialize Emacs. */ private Runnable paramsClosure; @@ -40,10 +41,10 @@ public class EmacsThread extends Thread public EmacsThread (EmacsService service, Runnable paramsClosure, - boolean startDashQ, String fileToOpen) + String extraStartupArgument, String fileToOpen) { super ("Emacs main thread"); - this.startDashQ = startDashQ; + this.extraStartupArgument = extraStartupArgument; this.paramsClosure = paramsClosure; this.fileToOpen = fileToOpen; } @@ -56,18 +57,20 @@ public class EmacsThread extends Thread if (fileToOpen == null) { - if (!startDashQ) + if (extraStartupArgument == null) args = new String[] { "libandroid-emacs.so", }; else - args = new String[] { "libandroid-emacs.so", "-Q", }; + args = new String[] { "libandroid-emacs.so", + extraStartupArgument, }; } else { - if (!startDashQ) + if (extraStartupArgument != null) args = new String[] { "libandroid-emacs.so", fileToOpen, }; else - args = new String[] { "libandroid-emacs.so", "-Q", + args = new String[] { "libandroid-emacs.so", + extraStartupArgument, fileToOpen, }; } diff --git a/java/res/drawable/emacs_wrench.png b/java/res/drawable/emacs_wrench.png new file mode 100644 index 00000000000..50572d3bed1 Binary files /dev/null and b/java/res/drawable/emacs_wrench.png differ diff --git a/java/res/values-v24/bool.xml b/java/res/values-v24/bool.xml new file mode 100644 index 00000000000..37f07992995 --- /dev/null +++ b/java/res/values-v24/bool.xml @@ -0,0 +1,22 @@ + + + + false + diff --git a/java/res/values/bool.xml b/java/res/values/bool.xml index d37eab745c0..2b253824e29 100644 --- a/java/res/values/bool.xml +++ b/java/res/values/bool.xml @@ -19,4 +19,5 @@ false + true diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml new file mode 100644 index 00000000000..36a47be6c84 --- /dev/null +++ b/java/res/values/strings.xml @@ -0,0 +1,39 @@ + + + + + Restart Emacs with -Q + + + Restart Emacs, but do not load site lisp or init files. + + + Restart Emacs with --debug-init + + + Restart Emacs, and display the debugger should an error occur while loading initialization files. + + + Delete dump file + + + Remove the dumped state created when Emacs was installed. + + diff --git a/java/res/xml/preferences.xml b/java/res/xml/preferences.xml index f0c3abb52e7..d52d28816e5 100644 --- a/java/res/xml/preferences.xml +++ b/java/res/xml/preferences.xml @@ -19,10 +19,12 @@ - + android:title="@string/start_quick_title" + android:summary="@string/start_quick_caption"/> + + android:title="@string/erase_dump_title" + android:summary="@string/erase_dump_caption"/> commit ddaca337e3a81e811de6e0f61ed78c414a1986be Merge: fbc8c2f660f 4b3de748b0b Author: Po Lu Date: Fri May 26 08:44:06 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit fbc8c2f660f38a91c1894d4ca608d5df7e84d1d2 Merge: 78606a83f7a af4791b5706 Author: Po Lu Date: Thu May 25 08:20:56 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 78606a83f7a50ab5aff58d804c88caba1a436142 Author: Po Lu Date: Wed May 24 10:53:41 2023 +0800 Update Android port * src/sfnt.c (sfnt_decompose_compound_glyph): Allow decomposing up to 16 nested components. (CALL, LOOPCALL): Correctly error if no fdef storage exists. (sfnt_interpret_run): New label `next_instruction', for CALL. (sfnt_interpret_compound_glyph_1): Allow decomposing up to 16 nested components. Prevent crash if there are no end points or points. (sfnt_read_cvar_table): Prevent assigning uninitialized values. (sfnt_vary_simple_glyph): Update commentary. diff --git a/src/sfnt.c b/src/sfnt.c index 800adc19214..7ef56a47469 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -2633,7 +2633,9 @@ sfnt_round_fixed (int32_t number) CONTEXT should be zeroed and put on the stack. OFF_X and OFF_Y should be zero, as should RECURSION_COUNT. GET_GLYPH and FREE_GLYPH, along with DCONTEXT, mean the same as in - sfnt_decompose_glyph. */ + sfnt_decompose_glyph. + + Value is 1 upon failure, else 0. */ static int sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph, @@ -2664,8 +2666,10 @@ sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph, point 1 will be 1 + base_index, and so on. */ base_index = context->num_points; - /* Prevent infinite loops. */ - if (recursion_count > 12) + /* Prevent infinite loops. Simply limit the level of nesting to the + maximum valid value of `max_component_depth', which is 16. */ + + if (recursion_count > 16) return 1; /* Don't defer offsets. */ @@ -2679,7 +2683,7 @@ sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph, dcontext, &need_free); if (!subglyph) - return -1; + return 1; /* Record the size of the point array before expansion. This will be the base to apply to all points coming from this @@ -6005,26 +6009,26 @@ #define CALL() \ TRAP ("invalid function"); \ \ if (def->opcode == id) \ - sfnt_interpret_call (def, \ - interpreter, \ - why); \ - if (def->opcode == id) \ - break; \ + { \ + sfnt_interpret_call (def, \ + interpreter, \ + why); \ + goto next_instruction; \ + } \ } \ \ - if (i == interpreter->function_defs_size) \ - TRAP ("invalid function"); \ + TRAP ("invalid function"); \ } #define LOOPCALL() \ { \ - uint32_t id, i; \ + uint32_t id; \ int32_t n; \ + int i; \ struct sfnt_interpreter_definition *def; \ \ id = POP (); \ n = POP (); \ - def = NULL; /* Pacify -fanalyzer. */ \ \ if (n > 65535) \ TRAP ("invalid LOOPCALL count"); \ @@ -6039,11 +6043,10 @@ #define LOOPCALL() \ TRAP ("invalid function"); \ \ if (def->opcode == id) \ - break; \ + goto loopcall_begin; \ } \ \ - if (i == interpreter->function_defs_size) \ - TRAP ("invalid function"); \ + TRAP ("invalid function"); \ \ loopcall_begin: \ if (n-- <= 0) \ @@ -10828,6 +10831,7 @@ sfnt_interpret_run (struct sfnt_interpreter *interpreter, NOT_IMPLEMENTED (); } + next_instruction: /* In the case of an NPUSHB or NPUSHW instruction, interpreter->IP has only been increased to skip over the extra bytes, and not the byte containing the instruction @@ -11568,7 +11572,8 @@ sfnt_interpret_compound_glyph_2 (struct sfnt_glyph *glyph, METRICS are the unscaled metrics of this compound glyph. Other arguments mean the same as they do in - `sfnt_interpret_compound_glyph'. */ + `sfnt_interpret_compound_glyph'. Value is NULL upon success, or a + string describing the reason for failure. */ static const char * sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph, @@ -11613,8 +11618,10 @@ sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph, /* And this is the index of the first contour in this glyph. */ base_contour = context->num_end_points; - /* Prevent infinite loops. */ - if (recursion_count > 12) + /* Prevent infinite loops. Simply limit the level of nesting to the + maximum valid value of `max_component_depth', which is 16. */ + + if (recursion_count > 16) return "Overly deep recursion in compound glyph data"; /* Don't defer offsets. */ @@ -12014,14 +12021,22 @@ sfnt_interpret_compound_glyph (struct sfnt_glyph *glyph, outline->flags = (unsigned char *) (outline->y_points + outline->num_points); - /* Copy over the contour endpoints, points, and flags. */ - memcpy (outline->contour_end_points, context.contour_end_points, - outline->num_contours * sizeof *outline->contour_end_points); - memcpy (outline->x_points, context.x_coordinates, - outline->num_points * sizeof *outline->x_points); - memcpy (outline->y_points, context.y_coordinates, - outline->num_points * sizeof *outline->y_points); - memcpy (outline->flags, context.flags, context.num_points); + /* Copy over the contour endpoints, points, and flags. Note that + all arrays in `context' are NULL unless num_contours is + non-zero. */ + + if (context.num_end_points) + memcpy (outline->contour_end_points, context.contour_end_points, + outline->num_contours * sizeof *outline->contour_end_points); + + if (context.num_points) + { + memcpy (outline->x_points, context.x_coordinates, + outline->num_points * sizeof *outline->x_points); + memcpy (outline->y_points, context.y_coordinates, + outline->num_points * sizeof *outline->y_points); + memcpy (outline->flags, context.flags, context.num_points); + } /* Free the context data. */ xfree (context.x_coordinates); @@ -13360,6 +13375,16 @@ sfnt_read_cvar_table (int fd, struct sfnt_offset_subtable *subtable, tuple = buffer; points = NULL; + /* Initialize `npoints' to zero. The specification doesn't say what + should happen with tuples using shared point numbers if it is not + set later on; simply assume no points at all apply to such a + tuple. */ + + npoints = 0; + + /* Initialize `size' to 0. */ + size = 0; + if (cvar->tuple_count & 0x8000) { points = sfnt_read_packed_points (data, &npoints, end, @@ -13367,11 +13392,12 @@ sfnt_read_cvar_table (int fd, struct sfnt_offset_subtable *subtable, if (!points) goto bail1; - /* Add npoints words to the size. */ - size = npoints * sizeof *points; + /* Add npoints words to the size. If npoints is UINT16_MAX, no + coordinates will actually be allocated. */ + + if (npoints != UINT16_MAX) + size = npoints * sizeof *points; } - else - size = 0; while (ntuples--) { @@ -13394,8 +13420,8 @@ sfnt_read_cvar_table (int fd, struct sfnt_offset_subtable *subtable, if (index & 0x8000) { - /* Embedded coordinates are present. Read each - coordinate and add it to the size. */ + /* Embedded coordinates are present. Read each coordinate + and add it to the size. */ if (tuple + fvar->axis_count * sizeof *coords - 1 >= end) goto bail2; @@ -14289,7 +14315,10 @@ sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id, data_offset = header.data_offset; /* If gvar->flags & tuples_share_point_numbers, read the shared - point numbers. */ + point numbers. Initialize `npoints' to zero. The specification + doesn't say what should happen with tuples using shared point + numbers if it is not set later on; simply assume no points at all + apply to such a tuple. */ npoints = 0; commit 769d3e17c2bb76a8bb6c604ac1a88e373a1a1c8c Merge: 1145572af27 9ad997cd689 Author: Po Lu Date: Wed May 24 08:25:20 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 1145572af2780df2e88b54372d43977db5305ded Author: Po Lu Date: Tue May 23 09:22:19 2023 +0800 ; * exec/exec.c (exec_0): Use strcpy. diff --git a/exec/exec.c b/exec/exec.c index a15386b51bb..0e077284860 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -983,7 +983,7 @@ exec_0 (char *name, struct exec_tracee *tracee, /* Copy over /proc, the PID, and /cwd/. */ rewrite = stpcpy (buffer, "/proc/"); rewrite = format_pid (rewrite, tracee->pid); - stpcpy (rewrite, "/cwd"); + strcpy (rewrite, "/cwd"); /* Resolve this symbolic link. */ commit fb8d87e18d5540705761df9f3f18a5a103bca073 Merge: 6dc9a3eeb75 d4ff1d74209 Author: Po Lu Date: Tue May 23 08:46:40 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 6dc9a3eeb755b5b0047b39f2bd7ebdefb10a1dc4 Merge: d341adadc8a 438b1205c54 Author: Po Lu Date: Mon May 22 21:00:20 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit d341adadc8a1688df888c55410366e42748a3e2d Merge: 8687f9309f8 71622d70e8b Author: Po Lu Date: Mon May 22 09:13:23 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 8687f9309f833074fcb8da14d0522a74a4e23a2e Merge: d86643a7863 88d1e9b436c Author: Po Lu Date: Sun May 21 08:54:39 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit d86643a7863cef268b69d7925e0982bdb70ba672 Author: Po Lu Date: Sat May 20 16:54:13 2023 +0800 Remove arbitrary process count limit * exec/trace.c (handle_clone_prepare): (handle_clone): When !REENTRANT, use malloc to allocate tracees after running out of static ones. diff --git a/exec/trace.c b/exec/trace.c index 974df1dd5e1..23db8ebcbc7 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -456,6 +456,12 @@ handle_clone_prepare (struct exec_tracee *parent) tracee = &static_tracees[tracees]; tracees++; } +#ifndef REENTRANT + /* Try to allocate a tracee using `malloc' if this library is + not being built to run inside a signal handler. */ + else if ((tracee = malloc (sizeof *tracee))) + ; +#endif /* REENTRANT */ else return; @@ -506,6 +512,12 @@ handle_clone (struct exec_tracee *tracee, pid_t pid) tracee = &static_tracees[tracees]; tracees++; } +#ifndef REENTRANT + /* Try to allocate a tracee using `malloc' if this library is + not being built to run inside a signal handler. */ + else if ((tracee = malloc (sizeof *tracee))) + ; +#endif /* REENTRANT */ else return 1; commit 181453285c67783ebf8eb269dc19fdb0e563af62 Author: Po Lu Date: Sat May 20 10:26:28 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsView.java (swapBuffers): Restore missing damage rect code. (onDetachedFromWindow): Remove redundant synchronization. diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 10c1af9e19a..124ea5301bb 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -356,16 +356,23 @@ else if (child.getVisibility () != GONE) damageRect = null; + /* Now see if there is a damage region. */ + synchronized (damageRegion) { if (damageRegion.isEmpty ()) return; + /* And extract and clear the damage region. */ + + damageRect = damageRegion.getBounds (); + damageRegion.setEmpty (); + bitmap = getBitmap (); /* Transfer the bitmap to the surface view, then invalidate it. */ - surfaceView.setBitmap (bitmap, damageRect); + surfaceView.setBitmap (bitmap, damageRect); } } @@ -545,20 +552,17 @@ else if (child.getVisibility () != GONE) { isAttachedToWindow = false; - synchronized (this) - { - /* Recycle the bitmap and call GC. */ + /* Recycle the bitmap and call GC. */ - if (bitmap != null) - bitmap.recycle (); + if (bitmap != null) + bitmap.recycle (); - bitmap = null; - canvas = null; - surfaceView.setBitmap (null, null); + bitmap = null; + canvas = null; + surfaceView.setBitmap (null, null); - /* Collect the bitmap storage; it could be large. */ - Runtime.getRuntime ().gc (); - } + /* Collect the bitmap storage; it could be large. */ + Runtime.getRuntime ().gc (); super.onDetachedFromWindow (); } commit b434d2e1e4c658a8210a421089c30d904dacf6eb Merge: 6d3cc725cd8 156973639cc Author: Po Lu Date: Sat May 20 09:43:43 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 6d3cc725cd869a46678e5509d11cfa61bbcd8f48 Author: Po Lu Date: Fri May 19 14:50:10 2023 +0800 Make tapping on header lines behave reasonably * lisp/touch-screen.el (touch-screen-tap-header-line): New function. ([header-line touchscreen-begin]): Define to `touch-screen-tap-header-line'. diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index a7fa5b4829c..2db8b62f6f9 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -662,6 +662,58 @@ touch-screen-drag-mode-line (global-set-key [bottom-divider touchscreen-begin] #'touch-screen-drag-mode-line) + + +;; Header line tapping. + +(defun touch-screen-tap-header-line (event) + "Handle a `touchscreen-begin' EVENT on the header line. +Wait for the tap to complete, then run any command bound to +`mouse-1' at the position of EVENT. + +If another keymap is bound to `down-mouse-1', then display a menu +with its contents instead, and run the selected command." + (interactive "e") + (let* ((posn (cdadr event)) + (object (posn-object posn)) + ;; Look for the keymap defined by the object itself. + (object-keymap (and (consp object) + (stringp (car object)) + (or (get-text-property (cdr object) + 'keymap + (car object)) + (get-text-property (cdr object) + 'local-map + (car object))))) + command keymap) + ;; Now look for either a command bound to `mouse-1' or a keymap + ;; bound to `down-mouse-1'. + (with-selected-window (posn-window posn) + (setq command (lookup-key object-keymap + [header-line mouse-1] t) + keymap (lookup-key object-keymap + [header-line down-mouse-1] t)) + (unless (keymapp keymap) + (setq keymap nil))) + ;; Wait for the tap to complete. + (when (touch-screen-track-tap event) + ;; Select the window whose header line was clicked. + (with-selected-window (posn-window posn) + (if keymap + (when-let* ((command (x-popup-menu event keymap)) + (tem (lookup-key keymap + (if (consp command) + (apply #'vector command) + (vector command)) + t))) + (call-interactively tem)) + (when (commandp command) + (call-interactively command nil + (vector (list 'mouse-1 (cdadr event)))))))))) + +(global-set-key [header-line touchscreen-begin] + #'touch-screen-tap-header-line) + (provide 'touch-screen) ;;; touch-screen ends here commit 2e644fc13cc46edb99424223bd9dd6da57d710ce Merge: f59fd5fb27f 8c9377b6c4e Author: Po Lu Date: Fri May 19 08:55:42 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit f59fd5fb27f8bd30f0fb81c47f941b3b0749216b Author: Po Lu Date: Thu May 18 19:44:16 2023 +0800 * make-dist (possibly_non_vc_files): Add Android-specific files. diff --git a/make-dist b/make-dist index 80c672dbf3a..a3b7219af3a 100755 --- a/make-dist +++ b/make-dist @@ -356,7 +356,8 @@ possibly_non_vc_files= $top_level_ChangeLog MANIFEST aclocal.m4 configure admin/charsets/jisx2131-filter - src/config.in + src/config.in exec/configure + exec/config.h.in "$( find admin doc etc lisp \ \( -name '*.el' -o -name '*.elc' -o -name '*.map' -o -name '*.stamp' \ commit fdc0bf25b2a608625c6292c723ef40ad8e6ed76a Author: Po Lu Date: Thu May 18 16:09:00 2023 +0800 Allow interacting with the tab line from a touch screen * doc/emacs/frames.texi (Tab Bars): Explain how to interact with the tab bar from a touch screen. * doc/emacs/input.texi (Touchscreens): Document exactly what a ``long press'' is. * doc/emacs/windows.texi (Tab Line): Likewise. * lisp/tab-line.el (tab-line-tab-map, tab-line-add-map) (tab-line-tab-close-map, tab-line-left-map, tab-line-right-map): Bind `touchscreen-begin' to each command. (tab-line-track-tap, tab-line-event-start): New functions. (tab-line-hscroll-right, tab-line-hscroll-left, tab-line-new-tab) (tab-line-select-tab, tab-line-close-tab): Use them. diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi index ce631561be7..4e09c1c3f67 100644 --- a/doc/emacs/frames.texi +++ b/doc/emacs/frames.texi @@ -1571,6 +1571,15 @@ Tab Bars wheel scrolling switches to the next or previous tab. Holding down the @key{SHIFT} key during scrolling moves the tab to the left or right. + Touch screen input (@pxref{Other Input Devices}) can also be used to +operate on tabs. Long-pressing (@pxref{Touchscreens}) a tab will +display a context menu with items that operate on the tab that was +pressed, and long-pressing the tab bar itself will display a context +menu which lets you create and remove tabs; tapping a tab itself will +result in that tab's window configuration being selected, and tapping +a button on the tab bar will behave as if it was clicked with +@kbd{mouse-1}. + @findex tab-bar-history-mode You can enable @code{tab-bar-history-mode} to remember window configurations used in every tab, and later restore them. diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi index b4b37501a14..96743349a1c 100644 --- a/doc/emacs/input.texi +++ b/doc/emacs/input.texi @@ -51,17 +51,20 @@ Touchscreens @item @cindex dragging, touchscreens - ``Dragging'', meaning to place a tool on the display and leave it -there for a while before moving the tool around, will make Emacs set -the point to where the tool was and begin selecting text under the -tool as it moves around, much like what would happen if @code{mouse-1} -were to be held down. @xref{Mouse Commands}. +@cindex long-press, touchscreens + ``Dragging'', meaning to perform a @dfn{long-press} (placing a tool +on the display and leaving it there for a while) before moving the +tool around, will make Emacs set the point to where the tool was and +begin selecting text under the tool as it moves around, much like what +would happen if @code{mouse-1} were to be held down. @xref{Mouse +Commands}. @end itemize @vindex touch-screen-delay By default, Emacs considers a tool as having been left on the -display for a while after 0.7 seconds, but this can be changed by -customizing the variable @code{touch-screen-delay}. +display long enough to trigger a ``long-press'' after 0.7 seconds, but +this can be changed by customizing the variable +@code{touch-screen-delay}. @node On-Screen Keyboards @section Using Emacs with virtual keyboards diff --git a/doc/emacs/windows.texi b/doc/emacs/windows.texi index e4abdef76be..665fd80e53b 100644 --- a/doc/emacs/windows.texi +++ b/doc/emacs/windows.texi @@ -642,6 +642,13 @@ Tab Line icon of a tab deletes it. The mouse wheel on the tab line scrolls the tabs horizontally. + Touch screen input (@pxref{Other Input Devices}) can also be used to +interact with the ``tab line''. Long-pressing (@pxref{Touchscreens}) +a tab will display a context menu with items that operate on the tab +that was pressed; tapping a tab itself will result in switching to +that tab's buffer, and tapping a button on the tab line will behave as +if it was clicked with @kbd{mouse-1}. + Selecting the previous window-local tab is the same as typing @kbd{C-x @key{LEFT}} (@code{previous-buffer}), selecting the next tab is the same as @kbd{C-x @key{RIGHT}} (@code{next-buffer}). Both commands diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 1958f12975f..d7c3049270a 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -137,33 +137,38 @@ tab-line-close-highlight (defvar-keymap tab-line-tab-map :doc "Local keymap for `tab-line-mode' window tabs." - " " #'tab-line-select-tab - " " #'tab-line-close-tab - " " #'tab-line-tab-context-menu + " " #'tab-line-select-tab + " " #'tab-line-close-tab + " " #'tab-line-tab-context-menu + " " #'tab-line-select-tab "RET" #'tab-line-select-tab) (defvar-keymap tab-line-add-map :doc "Local keymap to add `tab-line-mode' window tabs." - " " #'tab-line-new-tab - " " #'tab-line-new-tab + " " #'tab-line-new-tab + " " #'tab-line-new-tab + " " #'tab-line-new-tab "RET" #'tab-line-new-tab) (defvar-keymap tab-line-tab-close-map :doc "Local keymap to close `tab-line-mode' window tabs." - " " #'tab-line-close-tab - " " #'tab-line-close-tab) + " " #'tab-line-close-tab + " " #'tab-line-close-tab + " " #'tab-line-close-tab) (defvar-keymap tab-line-left-map :doc "Local keymap to scroll `tab-line-mode' window tabs to the left." - " " #'tab-line-hscroll-left - " " #'tab-line-hscroll-left - "RET" #'tab-line-new-tab) + " " #'tab-line-hscroll-left + " " #'tab-line-hscroll-left + " " #'tab-line-hscroll-left + "RET" #'tab-line-new-tab) (defvar-keymap tab-line-right-map :doc "Local keymap to scroll `tab-line-mode' window tabs to the right." - " " #'tab-line-hscroll-right - " " #'tab-line-hscroll-right - "RET" #'tab-line-new-tab) + " " #'tab-line-hscroll-right + " " #'tab-line-hscroll-right + " " #'tab-line-hscroll-right + "RET" #'tab-line-new-tab) (defcustom tab-line-new-tab-choice t @@ -326,6 +331,48 @@ tab-line-tabs-buffer-list-function "Function to return a global list of buffers. Used only for `tab-line-tabs-mode-buffers' and `tab-line-tabs-buffer-groups'.") + + +;;; Touch screen support. + +(defun tab-line-track-tap (event &optional function) + "Track a tap starting from EVENT. +If EVENT is not a `touchscreen-begin' event, return t. +Otherwise, return t if the tap completes successfully, and nil if +the tap should be ignored. + +If FUNCTION is specified and the tap does not complete within +`touch-screen-delay' seconds, display the appropriate context +menu by calling FUNCTION with EVENT, and return nil." + (if (not (eq (car-safe event) 'touchscreen-begin)) + t + (let ((result (catch 'context-menu + (let (timer) + (unwind-protect + (progn + (when function + (setq timer + (run-at-time touch-screen-delay t + #'throw 'context-menu + 'context-menu))) + (touch-screen-track-tap event)) + (when timer + (cancel-timer timer))))))) + (cond ((eq result 'context-menu) + (prog1 nil + (funcall function event))) + (result t))))) + +(defun tab-line-event-start (event) + "Like `event-start'. +However, return the correct mouse position list if EVENT is a +`touchscreen-begin' event." + (or (and (eq (car-safe event) 'touchscreen-begin) + (cdadr event)) + (event-start event))) + + + (defun tab-line-tabs-buffer-list () (seq-filter (lambda (b) (and (buffer-live-p b) (/= (aref (buffer-name b) 0) ?\s))) @@ -719,17 +766,21 @@ tab-line-hscroll-right "Scroll the tab line ARG positions to the right. Interactively, ARG is the prefix numeric argument and defaults to 1." (interactive (list current-prefix-arg last-nonmenu-event)) - (let ((window (and (listp event) (posn-window (event-start event))))) - (tab-line-hscroll arg window) - (force-mode-line-update window))) + (when (tab-line-track-tap event) + (let ((window (and (listp event) + (posn-window (tab-line-event-start event))))) + (tab-line-hscroll arg window) + (force-mode-line-update window)))) (defun tab-line-hscroll-left (&optional arg event) "Scroll the tab line ARG positions to the left. Interactively, ARG is the prefix numeric argument and defaults to 1." (interactive (list current-prefix-arg last-nonmenu-event)) - (let ((window (and (listp event) (posn-window (event-start event))))) - (tab-line-hscroll (- (or arg 1)) window) - (force-mode-line-update window))) + (when (tab-line-track-tap event) + (let ((window (and (listp event) + (posn-window (tab-line-event-start event))))) + (tab-line-hscroll (- (or arg 1)) window) + (force-mode-line-update window)))) (defun tab-line-new-tab (&optional event) @@ -738,15 +789,16 @@ tab-line-new-tab on the tab line. Switching to another buffer also adds a new tab corresponding to the new buffer shown in the window." (interactive (list last-nonmenu-event)) - (if (functionp tab-line-new-tab-choice) - (funcall tab-line-new-tab-choice) - (let ((tab-line-tabs-buffer-groups mouse-buffer-menu-mode-groups)) - (if (and (listp event) - (display-popup-menus-p) - (not tty-menu-open-use-tmm)) - (mouse-buffer-menu event) ; like (buffer-menu-open) - ;; tty menu doesn't support mouse clicks, so use tmm - (tmm-prompt (mouse-buffer-menu-keymap)))))) + (when (tab-line-track-tap event) + (if (functionp tab-line-new-tab-choice) + (funcall tab-line-new-tab-choice) + (let ((tab-line-tabs-buffer-groups mouse-buffer-menu-mode-groups)) + (if (and (listp event) + (display-popup-menus-p) + (not tty-menu-open-use-tmm)) + (mouse-buffer-menu event) ; like (buffer-menu-open) + ;; tty menu doesn't support mouse clicks, so use tmm + (tmm-prompt (mouse-buffer-menu-keymap))))))) (defun tab-line-select-tab (&optional event) "Switch to the buffer specified by the tab on which you click. @@ -754,16 +806,17 @@ tab-line-select-tab So, for example, switching to a previous tab is equivalent to using the `previous-buffer' command." (interactive "e") - (let* ((posnp (event-start event)) - (tab (tab-line--get-tab-property 'tab (car (posn-string posnp)))) - (buffer (if (bufferp tab) tab (cdr (assq 'buffer tab))))) - (if buffer - (tab-line-select-tab-buffer buffer (posn-window posnp)) - (let ((select (cdr (assq 'select tab)))) - (when (functionp select) - (with-selected-window (posn-window posnp) - (funcall select) - (force-mode-line-update))))))) + (when (tab-line-track-tap event #'tab-line-tab-context-menu) + (let* ((posnp (tab-line-event-start event)) + (tab (tab-line--get-tab-property 'tab (car (posn-string posnp)))) + (buffer (if (bufferp tab) tab (cdr (assq 'buffer tab))))) + (if buffer + (tab-line-select-tab-buffer buffer (posn-window posnp)) + (let ((select (cdr (assq 'select tab)))) + (when (functionp select) + (with-selected-window (posn-window posnp) + (funcall select) + (force-mode-line-update)))))))) (defun tab-line-select-tab-buffer (buffer &optional window) (let* ((window-buffer (window-buffer window)) @@ -869,25 +922,27 @@ tab-line-close-tab right side of the tab. This command buries the buffer, so it goes out of sight of the tab line." (interactive (list last-nonmenu-event)) - (let* ((posnp (and (listp event) (event-start event))) - (window (and posnp (posn-window posnp))) - (tab (tab-line--get-tab-property 'tab (car (posn-string posnp)))) - (buffer (if (bufferp tab) tab (cdr (assq 'buffer tab)))) - (close-function (unless (bufferp tab) (cdr (assq 'close tab))))) - (with-selected-window (or window (selected-window)) - (cond - ((functionp close-function) - (funcall close-function)) - ((eq tab-line-close-tab-function 'kill-buffer) - (kill-buffer buffer)) - ((eq tab-line-close-tab-function 'bury-buffer) - (if (eq buffer (current-buffer)) - (bury-buffer) - (set-window-prev-buffers nil (assq-delete-all buffer (window-prev-buffers))) - (set-window-next-buffers nil (delq buffer (window-next-buffers))))) - ((functionp tab-line-close-tab-function) - (funcall tab-line-close-tab-function tab))) - (force-mode-line-update)))) + (when (tab-line-track-tap event) + (let* ((posnp (and (listp event) + (tab-line-event-start event))) + (window (and posnp (posn-window posnp))) + (tab (tab-line--get-tab-property 'tab (car (posn-string posnp)))) + (buffer (if (bufferp tab) tab (cdr (assq 'buffer tab)))) + (close-function (unless (bufferp tab) (cdr (assq 'close tab))))) + (with-selected-window (or window (selected-window)) + (cond + ((functionp close-function) + (funcall close-function)) + ((eq tab-line-close-tab-function 'kill-buffer) + (kill-buffer buffer)) + ((eq tab-line-close-tab-function 'bury-buffer) + (if (eq buffer (current-buffer)) + (bury-buffer) + (set-window-prev-buffers nil (assq-delete-all buffer (window-prev-buffers))) + (set-window-next-buffers nil (delq buffer (window-next-buffers))))) + ((functionp tab-line-close-tab-function) + (funcall tab-line-close-tab-function tab))) + (force-mode-line-update))))) (defun tab-line-tab-context-menu (&optional event) "Pop up the context menu for a tab-line tab." commit 074c0268fd32d6527e124cff386bb6b15cf90017 Merge: db48eff8cf4 5ef169ed701 Author: Po Lu Date: Thu May 18 09:04:57 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit db48eff8cf4a88393c0209f663ca194ee37fa747 Author: Po Lu Date: Wed May 17 09:18:35 2023 +0800 ; Update from Gnulib diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 1f59f02b1e9..25fd4af6259 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -1196,6 +1196,7 @@ LIB_WSOCK32 = @LIB_WSOCK32@ LIB_XATTR = @LIB_XATTR@ LIMITS_H = @LIMITS_H@ LN_S_FILEONLY = @LN_S_FILEONLY@ +LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@ LTLIBGMP = @LTLIBGMP@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ commit bb95cdaa0693ecea2953d14f2808a23b66ac9446 Merge: bb8bf9203ed 6cb963b73c3 Author: Po Lu Date: Wed May 17 09:16:48 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit bb8bf9203ed33de0bb269c8ff69067aa7b3a692a Author: Po Lu Date: Tue May 16 15:54:50 2023 +0800 Add touchscreen support to the tab bar * lisp/menu-bar.el (popup-menu-normalize-position): Normalize `touchscreen-begin' events correctly. * lisp/tab-bar.el (tab-bar-mouse-context-menu): New argument POSN. Use it if specified. (touch-screen-track-tap, tab-bar-handle-timeout) (tab-bar-touchscreen-begin): New functions. (tab-bar-map): Bind [tab-bar touchscreen-begin]. * lisp/touch-screen.el (touch-screen-track-drag): Fix doc string. * src/dispextern.h: Export `get_tab_bar_item_kbd'. * src/keyboard.c (coords_in_tab_bar_window): New function. (make_lispy_event): Adjust touchscreen begin event mouse position list for tab bar. * src/xdisp.c (tab_bar_item_info): Allow CLOSE_P to be NULL. (get_tab_bar_item): Adjust doc string. (get_tab_bar_item_kbd): New function. diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index 949d805465d..da002a46621 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el @@ -2669,20 +2669,25 @@ popup-menu-normalize-position POSITION can be an event, a posn- value, a value having the form ((XOFFSET YOFFSET) WINDOW), or nil. If nil, the current mouse position is used, or nil if there is no mouse." - (pcase position + (cond ;; nil -> mouse cursor position - ('nil + ((eq position nil) (let ((mp (mouse-pixel-position))) (list (list (cadr mp) (cddr mp)) (car mp)))) ;; Value returned from `event-end' or `posn-at-point'. - ((pred posnp) + ((posnp position) (let ((xy (posn-x-y position))) (list (list (car xy) (cdr xy)) (posn-window position)))) + ;; `touchscreen-begin' or `touchscreen-end' event. + ((or (eq (car-safe position) 'touchscreen-begin) + (eq (car-safe position) 'touchscreen-end)) + position) ;; Event. - ((pred eventp) + ((eventp position) (popup-menu-normalize-position (event-end position))) - (_ position))) + ;; Some other value. + (t position))) (defcustom tty-menu-open-use-tmm nil "If non-nil, \\[menu-bar-open] on a TTY will invoke `tmm-menubar'. diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 9d703b5d048..1a33eda0866 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -341,10 +341,12 @@ tab-bar-mouse-close-tab (unless (eq tab-number t) (tab-bar-close-tab tab-number)))) -(defun tab-bar-mouse-context-menu (event) - "Pop up the context menu for the tab on which you click." +(defun tab-bar-mouse-context-menu (event &optional posn) + "Pop up the context menu for the tab on which you click. +EVENT is a mouse or touch screen event. POSN is nil or the +position of EVENT." (interactive "e") - (let* ((item (tab-bar--event-to-item (event-start event))) + (let* ((item (tab-bar--event-to-item (or posn (event-start event)))) (tab-number (tab-bar--key-to-number (nth 0 item))) (menu (make-sparse-keymap (propertize "Context Menu" 'hide t)))) @@ -397,6 +399,78 @@ tab-bar-mouse-move-tab (tab-bar-move-tab-to (if (null to) (1+ (tab-bar--current-tab-index)) to) from)))) + + +;;; Tab bar touchscreen support. + +(declare-function touch-screen-track-tap "touch-screen.el") + +(defun tab-bar-handle-timeout () + "Handle a touch-screen timeout on the tab bar. +Beep, then throw to `context-menu' and return." + (beep) + (throw 'context-menu 'context-menu)) + +(defun tab-bar-touchscreen-begin (event) + "Handle a touchscreen begin EVENT on the tab bar. + +Determine where the touch was made. If it was made on a tab +itself, start a timer set to go off after a certain amount of +time, and wait for the touch point to be released, and either +display a context menu or select a tab as appropriate. + +Otherwise, if it was made on a button, close or create a tab as +appropriate." + (interactive "e") + (let* ((posn (cdadr event)) + (item (tab-bar--event-to-item posn)) + (number (tab-bar--key-to-number (car item))) + timer) + (when (eq (catch 'context-menu + (cond ((integerp number) + ;; The touch began on a tab. Start a context + ;; menu timer and start tracking the tap. + (unwind-protect + (progn + (setq timer (run-at-time touch-screen-delay nil + #'tab-bar-handle-timeout)) + ;; Now wait for the tap to complete. + (when (touch-screen-track-tap event) + ;; And select the tab, or close it, + ;; depending on whether or not the + ;; close button was pressed. + (if (caddr item) + (tab-bar-close-tab number) + (tab-bar-select-tab number)))) + ;; Cancel the timer. + (cancel-timer timer))) + ((and (memq (car item) '(add-tab history-back + history-forward)) + (functionp (cadr item))) + ;; This is some kind of button. Wait for the + ;; tap to complete and press it. + (when (touch-screen-track-tap event) + (call-interactively (cadr item)))) + (t + ;; The touch began on the tab bar itself. + ;; Start a context menu timer and start + ;; tracking the tap, but don't do anything + ;; afterwards. + (unwind-protect + (progn + (setq timer (run-at-time touch-screen-delay nil + #'tab-bar-handle-timeout)) + ;; Now wait for the tap to complete. + (touch-screen-track-tap event)) + ;; Cancel the timer. + (cancel-timer timer))))) + 'context-menu) + ;; Display the context menu in response to a time out waiting + ;; for the tap to complete. + (tab-bar-mouse-context-menu event posn)))) + + + (defvar-keymap tab-bar-map :doc "Keymap for the commands used on the tab bar." "" #'tab-bar-mouse-down-1 @@ -418,7 +492,8 @@ tab-bar-map "S-" #'tab-bar-move-tab-backward "S-" #'tab-bar-move-tab "S-" #'tab-bar-move-tab-backward - "S-" #'tab-bar-move-tab) + "S-" #'tab-bar-move-tab + "" #'tab-bar-touchscreen-begin) (global-set-key [tab-bar] `(menu-item ,(purecopy "tab bar") ,(make-sparse-keymap) diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 31d46b062ed..a7fa5b4829c 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -529,7 +529,7 @@ touch-screen-track-tap (defun touch-screen-track-drag (event update &optional data) "Track a single drag starting from EVENT. -EVENT should be a `touchscreen-end' event. +EVENT should be a `touchscreen-begin' event. Read touch screen events until a `touchscreen-end' event is received with the same ID as in EVENT. For each diff --git a/src/dispextern.h b/src/dispextern.h index 36e998070a4..402972d33d9 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -3528,6 +3528,7 @@ #define TTY_CAP_STRIKE_THROUGH 0x20 NativeRectangle *nr); extern Lisp_Object find_hot_spot (Lisp_Object, int, int); +extern int get_tab_bar_item_kbd (struct frame *, int, int, int *, bool *); extern Lisp_Object handle_tab_bar_click (struct frame *, int, int, bool, int); extern void handle_tool_bar_click (struct frame *, diff --git a/src/keyboard.c b/src/keyboard.c index 6e63cd71f30..c0d201f72ad 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -5875,6 +5875,25 @@ coords_in_menu_bar_window (struct frame *f, int x, int y) #endif +/* Return whether or not the coordinates X and Y are inside the + tab-bar window of the given frame F. */ + +static bool +coords_in_tab_bar_window (struct frame *f, int x, int y) +{ + struct window *window; + + if (!WINDOWP (f->tab_bar_window)) + return false; + + window = XWINDOW (f->tab_bar_window); + + return (y >= WINDOW_TOP_EDGE_Y (window) + && x >= WINDOW_LEFT_EDGE_X (window) + && y <= WINDOW_BOTTOM_EDGE_Y (window) + && x <= WINDOW_RIGHT_EDGE_X (window)); +} + /* Given a struct input_event, build the lisp event which represents it. If EVENT is 0, build a mouse movement event from the mouse movement buffer, which should have a movement event in it. @@ -6522,11 +6541,14 @@ make_lispy_event (struct input_event *event) case TOUCHSCREEN_END_EVENT: { Lisp_Object x, y, id, position; - struct frame *f = XFRAME (event->frame_or_window); + struct frame *f; + int tab_bar_item; + bool close; #if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR int column, row, dummy; -#endif +#endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */ + f = XFRAME (event->frame_or_window); id = event->arg; x = event->x; y = event->y; @@ -6589,10 +6611,53 @@ make_lispy_event (struct input_event *event) return Qnil; } -#endif +#endif /* defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR */ position = make_lispy_position (f, x, y, event->timestamp); +#ifdef HAVE_WINDOW_SYSTEM + + /* Now check if POSITION lies on the tab bar. If so, look up + the corresponding tab bar item's propertized string as the + OBJECT. */ + + if (coords_in_tab_bar_window (f, XFIXNUM (event->x), + XFIXNUM (event->y)) + /* `get_tab_bar_item_kbd' returns 0 if the item was + previously highlighted, 1 otherwise, and -1 if there is + no tab bar item. */ + && get_tab_bar_item_kbd (f, XFIXNUM (event->x), + XFIXNUM (event->y), &tab_bar_item, + &close) >= 0) + { + /* First, obtain the propertized string. */ + x = Fcopy_sequence (AREF (f->tab_bar_items, + (tab_bar_item + + TAB_BAR_ITEM_CAPTION))); + + /* Next, add the key binding. */ + AUTO_LIST2 (y, Qmenu_item, list3 (AREF (f->tab_bar_items, + (tab_bar_item + + TAB_BAR_ITEM_KEY)), + AREF (f->tab_bar_items, + (tab_bar_item + + TAB_BAR_ITEM_BINDING)), + close ? Qt : Qnil)); + + /* And add the new properties to the propertized string. */ + Fadd_text_properties (make_fixnum (0), + make_fixnum (SCHARS (x)), + y, x); + + /* Set the position to 0. */ + x = Fcons (x, make_fixnum (0)); + + /* Finally, add the OBJECT. */ + position = nconc2 (position, Fcons (x, Qnil)); + } + +#endif /* HAVE_WINDOW_SYSTEM */ + return list2 (((event->kind == TOUCHSCREEN_BEGIN_EVENT) ? Qtouchscreen_begin diff --git a/src/xdisp.c b/src/xdisp.c index 89b1ae77e6f..09b1cc616f2 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -14584,21 +14584,32 @@ tab_bar_item_info (struct frame *f, struct glyph *glyph, Qmenu_item, f->current_tab_bar_string); if (! FIXNUMP (prop)) return false; + *prop_idx = XFIXNUM (prop); - *close_p = !NILP (Fget_text_property (make_fixnum (charpos), - Qclose_tab, - f->current_tab_bar_string)); + if (close_p) + *close_p = !NILP (Fget_text_property (make_fixnum (charpos), + Qclose_tab, + f->current_tab_bar_string)); return true; } -/* Get information about the tab-bar item at position X/Y on frame F. - Return in *GLYPH a pointer to the glyph of the tab-bar item in - the current matrix of the tab-bar window of F, or NULL if not - on a tab-bar item. Return in *PROP_IDX the index of the tab-bar - item in F->tab_bar_items. Value is +/* Get information about the tab-bar item at position X/Y on frame F's + tab bar window. + + Set *GLYPH to a pointer to the glyph of the tab-bar item in the + current matrix of the tab-bar window of F, or NULL if not on a + tab-bar item. Return in *PROP_IDX the index of the tab-bar item in + F->tab_bar_items. + + Place the window-relative vpos of Y in *VPOS, and the + window-relative hpos of X in *HPOS. If CLOSE_P, set it to whether + or not the tab bar item represents a button that should close a + tab. + + Value is -1 if X/Y is not on a tab-bar item 0 if X/Y is on the same item that was highlighted before. @@ -14606,7 +14617,7 @@ tab_bar_item_info (struct frame *f, struct glyph *glyph, static int get_tab_bar_item (struct frame *f, int x, int y, struct glyph **glyph, - int *hpos, int *vpos, int *prop_idx, bool *close_p) + int *hpos, int *vpos, int *prop_idx, bool *close_p) { struct window *w = XWINDOW (f->tab_bar_window); int area; @@ -14624,6 +14635,38 @@ get_tab_bar_item (struct frame *f, int x, int y, struct glyph **glyph, return *prop_idx == f->last_tab_bar_item ? 0 : 1; } +/* EXPORT: + + Like `get_tab_bar_item'. However, don't return anything for GLYPH, + HPOS, or VPOS, and treat X and Y as relative to F itself, as + opposed to its tab bar window. */ + +int +get_tab_bar_item_kbd (struct frame *f, int x, int y, int *prop_idx, + bool *close_p) +{ + struct window *w; + int area, vpos, hpos; + struct glyph *glyph; + + w = XWINDOW (f->tab_bar_window); + + /* Convert X and Y to window coordinates. */ + frame_to_window_pixel_xy (w, &x, &y); + + /* Find the glyph under X/Y. */ + glyph = x_y_to_hpos_vpos (w, x, y, &hpos, &vpos, 0, + 0, &area); + if (glyph == NULL) + return -1; + + /* Get the start of this tab-bar item's properties in + f->tab_bar_items. */ + if (!tab_bar_item_info (f, glyph, prop_idx, close_p)) + return -1; + + return *prop_idx == f->last_tab_bar_item ? 0 : 1; +} /* EXPORT: Handle mouse button event on the tab-bar of frame F, at commit 44da7d75ed3fa6322d64d66d250bc78e91636ff5 Merge: 4247b2a723e 5289c2b3eba Author: Po Lu Date: Tue May 16 09:27:27 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 4247b2a723e5a3a04c4ea71ffc469cb4d9e094b1 Author: Po Lu Date: Mon May 15 11:31:16 2023 +0800 Fix year 2038 code for Android 4.4 and earlier * configure.ac: Also disable enable_year2038. diff --git a/configure.ac b/configure.ac index 65609ec8021..2a2d31bb72d 100644 --- a/configure.ac +++ b/configure.ac @@ -40,9 +40,9 @@ CFLAGS="$ANDROID_CFLAGS -Werror=implicit-function-declaration" # Don't explicitly enable support for large files unless Emacs is # being built for API 21 or later. Otherwise, mmap does not work. - if test "$ANDROID_SDK" -lt "21"; then + AS_IF([test "$ANDROID_SDK" -lt "21"], [ enable_largefile=no - fi + enable_year2038=no]) fi dnl Set emacs_config_options to the options of 'configure', quoted for the shell, commit 89584de2a54f90984dac2b8602d5cb08673cd7a9 Author: Po Lu Date: Mon May 15 11:25:23 2023 +0800 Fix the MS-DOS build * msdos/sed1v2.inp: Fix removal of ANDROID_BUILD_CFLAGS. * msdos/sedlibmk.inp: Clear DIR_HAS_FD_MEMBER and LOCALE_FR_UTF8. diff --git a/msdos/sed1v2.inp b/msdos/sed1v2.inp index 71aa27afce2..1b96cc7ddc4 100644 --- a/msdos/sed1v2.inp +++ b/msdos/sed1v2.inp @@ -206,7 +206,7 @@ s/ *@WEBP_LIBS@// /^ANDROID_OBJ *=/s/@ANDROID_OBJ@// /^ANDROID_LIBS *=/s/@ANDROID_LIBS@// /^ANDROID_LDFLAGS *=/s/@ANDROID_LDFLAGS@// -/^ANDROID_BUILD_CFLAGS *=/s/@ANDROID_CFLAGS@// +/^ANDROID_BUILD_CFLAGS *=/s/@ANDROID_BUILD_CFLAGS@// /^LIBGMP_CFLAGS *=/s/@LIBGMP_CFLAGS@// /^SQLITE3_CFLAGS *=/s/@SQLITE3_CFLAGS@// /^LIBSELINUX_CFLAGS *=/s/@LIBSELINUX_CFLAGS@// diff --git a/msdos/sedlibmk.inp b/msdos/sedlibmk.inp index cca2b46b018..09f98f8e0a2 100644 --- a/msdos/sedlibmk.inp +++ b/msdos/sedlibmk.inp @@ -200,6 +200,9 @@ s/@PACKAGE@/emacs/ /^GL_GNULIB_[^ =]* *= *@/s/@[^@\n]*@/0/ /^GL_GSETTINGS_CFLAGS *=/s/@[^@\n]*@// /^GL_GSETTINGS_LIBS *=/s/@[^@\n]*@// +# Miscellaneous variables. +/^DIR_HAS_FD_MEMBER *=/s/@DIR_HAS_FD_MEMBER@/0/ +/^LOCALE_FR_UTF8 *=/s/@LOCALE_FR_UTF8@/none/ # # Edit the HAVE_foo variables /^HAVE_ATOLL *=/s/@HAVE_ATOLL@/0/ commit 5a3f009ad70d87366889e977d3b7d13f01b93e69 Author: Po Lu Date: Mon May 15 10:39:43 2023 +0800 ; Update from Gnulib diff --git a/lib/fseterr.h b/lib/fseterr.h index 0e9d2060aef..87dc347332d 100644 --- a/lib/fseterr.h +++ b/lib/fseterr.h @@ -17,6 +17,11 @@ #ifndef _FSETERR_H #define _FSETERR_H +/* This file uses HAVE___FSETERR. */ +#if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." +#endif + #include /* Set the error indicator of the stream FP. diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index e447a2c4054..1f59f02b1e9 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -186,6 +186,8 @@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ ANDROID = @ANDROID@ ANDROID_ABI = @ANDROID_ABI@ ANDROID_BUILD_CFLAGS = @ANDROID_BUILD_CFLAGS@ +ANDROID_CC = @ANDROID_CC@ +ANDROID_CFLAGS = @ANDROID_CFLAGS@ ANDROID_DEBUGGABLE = @ANDROID_DEBUGGABLE@ ANDROID_JAR = @ANDROID_JAR@ ANDROID_LDFLAGS = @ANDROID_LDFLAGS@ @@ -194,6 +196,7 @@ ANDROID_MIN_SDK = @ANDROID_MIN_SDK@ ANDROID_OBJ = @ANDROID_OBJ@ ANDROID_SDK_18_OR_EARLIER = @ANDROID_SDK_18_OR_EARLIER@ ANDROID_SDK_8_OR_EARLIER = @ANDROID_SDK_8_OR_EARLIER@ +ANDROID_SHARED_USER_ID = @ANDROID_SHARED_USER_ID@ APKSIGNER = @APKSIGNER@ APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@ AR = @AR@ @@ -1193,7 +1196,6 @@ LIB_WSOCK32 = @LIB_WSOCK32@ LIB_XATTR = @LIB_XATTR@ LIMITS_H = @LIMITS_H@ LN_S_FILEONLY = @LN_S_FILEONLY@ -LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@ LTLIBGMP = @LTLIBGMP@ LTLIBINTL = @LTLIBINTL@ LTLIBOBJS = @LTLIBOBJS@ @@ -1495,11 +1497,8 @@ REPLACE_REMAINDERL = @REPLACE_REMAINDERL@ REPLACE_REMOVE = @REPLACE_REMOVE@ REPLACE_RENAME = @REPLACE_RENAME@ REPLACE_RENAMEAT = @REPLACE_RENAMEAT@ -<<<<<<< HEAD -REPLACE_RINTL = @REPLACE_RINTL@ -======= REPLACE_REWINDDIR = @REPLACE_REWINDDIR@ ->>>>>>> origin/master +REPLACE_RINTL = @REPLACE_RINTL@ REPLACE_RMDIR = @REPLACE_RMDIR@ REPLACE_ROUND = @REPLACE_ROUND@ REPLACE_ROUNDF = @REPLACE_ROUNDF@ @@ -1682,6 +1681,7 @@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ emacs_major_version = @emacs_major_version@ +emacs_use_mailutils = @emacs_use_mailutils@ etcdir = @etcdir@ etcdocdir = @etcdocdir@ exec_prefix = @exec_prefix@ diff --git a/lib/isnand-nolibm.h b/lib/isnand-nolibm.h index 9c75a8c47bf..bb5a38b39f0 100644 --- a/lib/isnand-nolibm.h +++ b/lib/isnand-nolibm.h @@ -14,6 +14,11 @@ You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ +/* This file uses HAVE_ISNAND_IN_LIBC. */ +#if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." +#endif + #if HAVE_ISNAND_IN_LIBC /* Get declaration of isnan macro. */ # include diff --git a/lib/isnanf-nolibm.h b/lib/isnanf-nolibm.h index cc6b4198ff1..f4bcba143e6 100644 --- a/lib/isnanf-nolibm.h +++ b/lib/isnanf-nolibm.h @@ -14,6 +14,11 @@ You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ +/* This file uses HAVE_ISNANF_IN_LIBC. */ +#if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." +#endif + #if HAVE_ISNANF_IN_LIBC /* Get declaration of isnan macro or (older) isnanf function. */ # include diff --git a/lib/isnanl-nolibm.h b/lib/isnanl-nolibm.h index f04c489bb8e..8becc5b409e 100644 --- a/lib/isnanl-nolibm.h +++ b/lib/isnanl-nolibm.h @@ -14,6 +14,11 @@ You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ +/* This file uses HAVE_ISNANL_IN_LIBC. */ +#if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." +#endif + #if HAVE_ISNANL_IN_LIBC /* Get declaration of isnan macro or (older) isnanl function. */ # include diff --git a/lib/math.in.h b/lib/math.in.h index 76d48a44437..f841a1356e4 100644 --- a/lib/math.in.h +++ b/lib/math.in.h @@ -48,14 +48,17 @@ #define _GL_INCLUDING_MATH_H #ifndef _@GUARD_PREFIX@_MATH_H #define _@GUARD_PREFIX@_MATH_H +/* This file uses _GL_INLINE_HEADER_BEGIN, _GL_INLINE, _GL_ATTRIBUTE_CONST, + GNULIB_POSIXCHECK, HAVE_RAW_DECL_*. */ +#if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." +#endif + /* On OpenVMS, NAN, INFINITY, and HUGEVAL macros are defined in . */ #if defined __VMS && ! defined NAN # include #endif -#ifndef _GL_INLINE_HEADER_BEGIN - #error "Please include config.h first." -#endif _GL_INLINE_HEADER_BEGIN #ifndef _GL_MATH_INLINE # define _GL_MATH_INLINE _GL_INLINE @@ -2630,6 +2633,11 @@ _GL_MATH_CXX_REAL_FLOATING_DECL_1 (isnan) _GL_MATH_CXX_REAL_FLOATING_DECL_2 (isnan, rpl_isnan, bool) # define isnan rpl_isnan # define GNULIB_NAMESPACE_LACKS_ISNAN 1 +# elif (defined __FreeBSD__ && __clang_major__ >= 14) + /* Neither of the two possible _GL_MATH_CXX_REAL_FLOATING_DECL_2 invocations + works. Inline functions are already present in /usr/include/c++/v1/math.h, + which comes from LLVM. */ +# define GNULIB_NAMESPACE_LACKS_ISNAN 1 # else _GL_MATH_CXX_REAL_FLOATING_DECL_2 (isnan, isnan, bool) # endif diff --git a/lib/printf-args.c b/lib/printf-args.c index 5e14f654794..b2b21aeec18 100644 --- a/lib/printf-args.c +++ b/lib/printf-args.c @@ -29,6 +29,9 @@ # include "printf-args.h" #endif +/* Get INT_WIDTH. */ +#include + #ifdef STATIC STATIC #endif @@ -71,6 +74,102 @@ PRINTF_FETCHARGS (va_list args, arguments *a) case TYPE_ULONGLONGINT: ap->a.a_ulonglongint = va_arg (args, unsigned long long int); break; + case TYPE_INT8_T: + #if INT8_WIDTH < INT_WIDTH + ap->a.a_int8_t = va_arg (args, /* int8_t */ int); + #else + ap->a.a_int8_t = va_arg (args, int8_t); + #endif + break; + case TYPE_UINT8_T: + #if UINT8_WIDTH < INT_WIDTH + ap->a.a_uint8_t = va_arg (args, /* uint8_t */ int); + #else + ap->a.a_uint8_t = va_arg (args, uint8_t); + #endif + break; + case TYPE_INT16_T: + #if INT16_WIDTH < INT_WIDTH + ap->a.a_int16_t = va_arg (args, /* int16_t */ int); + #else + ap->a.a_int16_t = va_arg (args, int16_t); + #endif + break; + case TYPE_UINT16_T: + #if UINT16_WIDTH < INT_WIDTH + ap->a.a_uint16_t = va_arg (args, /* uint16_t */ int); + #else + ap->a.a_uint16_t = va_arg (args, uint16_t); + #endif + break; + case TYPE_INT32_T: + #if INT32_WIDTH < INT_WIDTH + ap->a.a_int32_t = va_arg (args, /* int32_t */ int); + #else + ap->a.a_int32_t = va_arg (args, int32_t); + #endif + break; + case TYPE_UINT32_T: + #if UINT32_WIDTH < INT_WIDTH + ap->a.a_uint32_t = va_arg (args, /* uint32_t */ int); + #else + ap->a.a_uint32_t = va_arg (args, uint32_t); + #endif + break; + case TYPE_INT64_T: + ap->a.a_int64_t = va_arg (args, int64_t); + break; + case TYPE_UINT64_T: + ap->a.a_uint64_t = va_arg (args, uint64_t); + break; + case TYPE_INT_FAST8_T: + #if INT_FAST8_WIDTH < INT_WIDTH + ap->a.a_int_fast8_t = va_arg (args, /* int_fast8_t */ int); + #else + ap->a.a_int_fast8_t = va_arg (args, int_fast8_t); + #endif + break; + case TYPE_UINT_FAST8_T: + #if UINT_FAST8_WIDTH < INT_WIDTH + ap->a.a_uint_fast8_t = va_arg (args, /* uint_fast8_t */ int); + #else + ap->a.a_uint_fast8_t = va_arg (args, uint_fast8_t); + #endif + break; + case TYPE_INT_FAST16_T: + #if INT_FAST16_WIDTH < INT_WIDTH + ap->a.a_int_fast16_t = va_arg (args, /* int_fast16_t */ int); + #else + ap->a.a_int_fast16_t = va_arg (args, int_fast16_t); + #endif + break; + case TYPE_UINT_FAST16_T: + #if UINT_FAST16_WIDTH < INT_WIDTH + ap->a.a_uint_fast16_t = va_arg (args, /* uint_fast16_t */ int); + #else + ap->a.a_uint_fast16_t = va_arg (args, uint_fast16_t); + #endif + break; + case TYPE_INT_FAST32_T: + #if INT_FAST32_WIDTH < INT_WIDTH + ap->a.a_int_fast32_t = va_arg (args, /* int_fast32_t */ int); + #else + ap->a.a_int_fast32_t = va_arg (args, int_fast32_t); + #endif + break; + case TYPE_UINT_FAST32_T: + #if UINT_FAST32_WIDTH < INT_WIDTH + ap->a.a_uint_fast32_t = va_arg (args, /* uint_fast32_t */ int); + #else + ap->a.a_uint_fast32_t = va_arg (args, uint_fast32_t); + #endif + break; + case TYPE_INT_FAST64_T: + ap->a.a_int_fast64_t = va_arg (args, int_fast64_t); + break; + case TYPE_UINT_FAST64_T: + ap->a.a_uint_fast64_t = va_arg (args, uint_fast64_t); + break; case TYPE_DOUBLE: ap->a.a_double = va_arg (args, double); break; @@ -136,6 +235,30 @@ PRINTF_FETCHARGS (va_list args, arguments *a) case TYPE_COUNT_LONGLONGINT_POINTER: ap->a.a_count_longlongint_pointer = va_arg (args, long long int *); break; + case TYPE_COUNT_INT8_T_POINTER: + ap->a.a_count_int8_t_pointer = va_arg (args, int8_t *); + break; + case TYPE_COUNT_INT16_T_POINTER: + ap->a.a_count_int16_t_pointer = va_arg (args, int16_t *); + break; + case TYPE_COUNT_INT32_T_POINTER: + ap->a.a_count_int32_t_pointer = va_arg (args, int32_t *); + break; + case TYPE_COUNT_INT64_T_POINTER: + ap->a.a_count_int64_t_pointer = va_arg (args, int64_t *); + break; + case TYPE_COUNT_INT_FAST8_T_POINTER: + ap->a.a_count_int_fast8_t_pointer = va_arg (args, int_fast8_t *); + break; + case TYPE_COUNT_INT_FAST16_T_POINTER: + ap->a.a_count_int_fast16_t_pointer = va_arg (args, int_fast16_t *); + break; + case TYPE_COUNT_INT_FAST32_T_POINTER: + ap->a.a_count_int_fast32_t_pointer = va_arg (args, int_fast32_t *); + break; + case TYPE_COUNT_INT_FAST64_T_POINTER: + ap->a.a_count_int_fast64_t_pointer = va_arg (args, int_fast64_t *); + break; #if ENABLE_UNISTDIO /* The unistdio extensions. */ case TYPE_U8_STRING: diff --git a/lib/printf-args.h b/lib/printf-args.h index f303cb19e9b..11016102828 100644 --- a/lib/printf-args.h +++ b/lib/printf-args.h @@ -41,6 +41,9 @@ #define _PRINTF_ARGS_H # include #endif +/* Get intN_t, uintN_t, intN_fast_t, uintN_fast_t. */ +#include + /* Get va_list. */ #include @@ -59,6 +62,26 @@ #define _PRINTF_ARGS_H TYPE_ULONGINT, TYPE_LONGLONGINT, TYPE_ULONGLONGINT, + /* According to ISO C 23 § 7.23.6.1, "all exact-width integer types", + "all minimum-width integer types", and "all fastest minimum-width integer + types" defined in should be supported. But for portability + between platforms, we support only those with N = 8, 16, 32, 64. */ + TYPE_INT8_T, + TYPE_UINT8_T, + TYPE_INT16_T, + TYPE_UINT16_T, + TYPE_INT32_T, + TYPE_UINT32_T, + TYPE_INT64_T, + TYPE_UINT64_T, + TYPE_INT_FAST8_T, + TYPE_UINT_FAST8_T, + TYPE_INT_FAST16_T, + TYPE_UINT_FAST16_T, + TYPE_INT_FAST32_T, + TYPE_UINT_FAST32_T, + TYPE_INT_FAST64_T, + TYPE_UINT_FAST64_T, TYPE_DOUBLE, TYPE_LONGDOUBLE, TYPE_CHAR, @@ -74,7 +97,15 @@ #define _PRINTF_ARGS_H TYPE_COUNT_SHORT_POINTER, TYPE_COUNT_INT_POINTER, TYPE_COUNT_LONGINT_POINTER, - TYPE_COUNT_LONGLONGINT_POINTER + TYPE_COUNT_LONGLONGINT_POINTER, + TYPE_COUNT_INT8_T_POINTER, + TYPE_COUNT_INT16_T_POINTER, + TYPE_COUNT_INT32_T_POINTER, + TYPE_COUNT_INT64_T_POINTER, + TYPE_COUNT_INT_FAST8_T_POINTER, + TYPE_COUNT_INT_FAST16_T_POINTER, + TYPE_COUNT_INT_FAST32_T_POINTER, + TYPE_COUNT_INT_FAST64_T_POINTER #if ENABLE_UNISTDIO /* The unistdio extensions. */ , TYPE_U8_STRING @@ -99,7 +130,23 @@ #define _PRINTF_ARGS_H unsigned long int a_ulongint; long long int a_longlongint; unsigned long long int a_ulonglongint; - float a_float; + int8_t a_int8_t; + uint8_t a_uint8_t; + int16_t a_int16_t; + uint16_t a_uint16_t; + int32_t a_int32_t; + uint32_t a_uint32_t; + int64_t a_int64_t; + uint64_t a_uint64_t; + int_fast8_t a_int_fast8_t; + uint_fast8_t a_uint_fast8_t; + int_fast16_t a_int_fast16_t; + uint_fast16_t a_uint_fast16_t; + int_fast32_t a_int_fast32_t; + uint_fast32_t a_uint_fast32_t; + int_fast64_t a_int_fast64_t; + uint_fast64_t a_uint_fast64_t; + float a_float; /* unused */ double a_double; long double a_longdouble; int a_char; @@ -116,6 +163,14 @@ #define _PRINTF_ARGS_H int * a_count_int_pointer; long int * a_count_longint_pointer; long long int * a_count_longlongint_pointer; + int8_t * a_count_int8_t_pointer; + int16_t * a_count_int16_t_pointer; + int32_t * a_count_int32_t_pointer; + int64_t * a_count_int64_t_pointer; + int_fast8_t * a_count_int_fast8_t_pointer; + int_fast16_t * a_count_int_fast16_t_pointer; + int_fast32_t * a_count_int_fast32_t_pointer; + int_fast64_t * a_count_int_fast64_t_pointer; #if ENABLE_UNISTDIO /* The unistdio extensions. */ const uint8_t * a_u8_string; diff --git a/lib/printf-parse.c b/lib/printf-parse.c index 3040749abdf..d3f2c3cb5d1 100644 --- a/lib/printf-parse.c +++ b/lib/printf-parse.c @@ -326,226 +326,317 @@ #define REGISTER_ARG(_index_,_type_) \ arg_type type; /* Parse argument type/size specifiers. */ - { - int flags = 0; - - for (;;) - { - if (*cp == 'h') - { - flags |= (1 << (flags & 1)); - cp++; - } - else if (*cp == 'L') - { - flags |= 4; - cp++; - } - else if (*cp == 'l') - { - flags += 8; - cp++; - } - else if (*cp == 'j') - { - if (sizeof (intmax_t) > sizeof (long)) - { - /* intmax_t = long long */ - flags += 16; - } - else if (sizeof (intmax_t) > sizeof (int)) - { - /* intmax_t = long */ - flags += 8; - } - cp++; - } - else if (*cp == 'z' || *cp == 'Z') - { - /* 'z' is standardized in ISO C 99, but glibc uses 'Z' - because the warning facility in gcc-2.95.2 understands - only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */ - if (sizeof (size_t) > sizeof (long)) - { - /* size_t = long long */ - flags += 16; - } - else if (sizeof (size_t) > sizeof (int)) - { - /* size_t = long */ - flags += 8; - } - cp++; - } - else if (*cp == 't') - { - if (sizeof (ptrdiff_t) > sizeof (long)) - { - /* ptrdiff_t = long long */ - flags += 16; - } - else if (sizeof (ptrdiff_t) > sizeof (int)) - { - /* ptrdiff_t = long */ - flags += 8; - } - cp++; - } + /* Relevant for the conversion characters d, i. */ + arg_type signed_type = TYPE_INT; + /* Relevant for the conversion characters b, o, u, x, X. */ + arg_type unsigned_type = TYPE_UINT; + /* Relevant for the conversion characters n. */ + arg_type pointer_type = TYPE_COUNT_INT_POINTER; + /* Relevant for the conversion characters a, A, e, E, f, F, g, G. */ + arg_type floatingpoint_type = TYPE_DOUBLE; + + if (*cp == 'h') + { + if (cp[1] == 'h') + { + signed_type = TYPE_SCHAR; + unsigned_type = TYPE_UCHAR; + pointer_type = TYPE_COUNT_SCHAR_POINTER; + cp += 2; + } + else + { + signed_type = TYPE_SHORT; + unsigned_type = TYPE_USHORT; + pointer_type = TYPE_COUNT_SHORT_POINTER; + cp++; + } + } + else if (*cp == 'l') + { + if (cp[1] == 'l') + { + signed_type = TYPE_LONGLONGINT; + unsigned_type = TYPE_ULONGLONGINT; + pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; + /* For backward compatibility only. */ + floatingpoint_type = TYPE_LONGDOUBLE; + cp += 2; + } + else + { + signed_type = TYPE_LONGINT; + unsigned_type = TYPE_ULONGINT; + pointer_type = TYPE_COUNT_LONGINT_POINTER; + cp++; + } + } + else if (*cp == 'j') + { + if (sizeof (intmax_t) > sizeof (long)) + { + /* intmax_t = long long */ + signed_type = TYPE_LONGLONGINT; + unsigned_type = TYPE_ULONGLONGINT; + pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; + /* For backward compatibility only. */ + floatingpoint_type = TYPE_LONGDOUBLE; + } + else if (sizeof (intmax_t) > sizeof (int)) + { + /* intmax_t = long */ + signed_type = TYPE_LONGINT; + unsigned_type = TYPE_ULONGINT; + pointer_type = TYPE_COUNT_LONGINT_POINTER; + } + cp++; + } + else if (*cp == 'z' || *cp == 'Z') + { + /* 'z' is standardized in ISO C 99, but glibc uses 'Z' + because the warning facility in gcc-2.95.2 understands + only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */ + if (sizeof (size_t) > sizeof (long)) + { + /* size_t = unsigned long long */ + signed_type = TYPE_LONGLONGINT; + unsigned_type = TYPE_ULONGLONGINT; + pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; + /* For backward compatibility only. */ + floatingpoint_type = TYPE_LONGDOUBLE; + } + else if (sizeof (size_t) > sizeof (int)) + { + /* size_t = unsigned long */ + signed_type = TYPE_LONGINT; + unsigned_type = TYPE_ULONGINT; + pointer_type = TYPE_COUNT_LONGINT_POINTER; + } + cp++; + } + else if (*cp == 't') + { + if (sizeof (ptrdiff_t) > sizeof (long)) + { + /* ptrdiff_t = long long */ + signed_type = TYPE_LONGLONGINT; + unsigned_type = TYPE_ULONGLONGINT; + pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; + /* For backward compatibility only. */ + floatingpoint_type = TYPE_LONGDOUBLE; + } + else if (sizeof (ptrdiff_t) > sizeof (int)) + { + /* ptrdiff_t = long */ + signed_type = TYPE_LONGINT; + unsigned_type = TYPE_ULONGINT; + pointer_type = TYPE_COUNT_LONGINT_POINTER; + } + cp++; + } + else if (*cp == 'w') + { + /* wN and wfN are standardized in ISO C 23. */ + if (cp[1] == 'f') + { + if (cp[2] == '8') + { + signed_type = TYPE_INT_FAST8_T; + unsigned_type = TYPE_UINT_FAST8_T; + pointer_type = TYPE_COUNT_INT_FAST8_T_POINTER; + cp += 3; + } + else if (cp[2] == '1' && cp[3] == '6') + { + signed_type = TYPE_INT_FAST16_T; + unsigned_type = TYPE_UINT_FAST16_T; + pointer_type = TYPE_COUNT_INT_FAST16_T_POINTER; + cp += 4; + } + else if (cp[2] == '3' && cp[3] == '2') + { + signed_type = TYPE_INT_FAST32_T; + unsigned_type = TYPE_UINT_FAST32_T; + pointer_type = TYPE_COUNT_INT_FAST32_T_POINTER; + cp += 4; + } + else if (cp[2] == '6' && cp[3] == '4') + { + signed_type = TYPE_INT_FAST64_T; + unsigned_type = TYPE_UINT_FAST64_T; + pointer_type = TYPE_COUNT_INT_FAST64_T_POINTER; + cp += 4; + } + } + else + { + if (cp[1] == '8') + { + signed_type = TYPE_INT8_T; + unsigned_type = TYPE_UINT8_T; + pointer_type = TYPE_COUNT_INT8_T_POINTER; + cp += 2; + } + else if (cp[1] == '1' && cp[2] == '6') + { + signed_type = TYPE_INT16_T; + unsigned_type = TYPE_UINT16_T; + pointer_type = TYPE_COUNT_INT16_T_POINTER; + cp += 3; + } + else if (cp[1] == '3' && cp[2] == '2') + { + signed_type = TYPE_INT32_T; + unsigned_type = TYPE_UINT32_T; + pointer_type = TYPE_COUNT_INT32_T_POINTER; + cp += 3; + } + else if (cp[1] == '6' && cp[2] == '4') + { + signed_type = TYPE_INT64_T; + unsigned_type = TYPE_UINT64_T; + pointer_type = TYPE_COUNT_INT64_T_POINTER; + cp += 3; + } + } + } + else if (*cp == 'L') + { + signed_type = TYPE_LONGLONGINT; + unsigned_type = TYPE_ULONGLONGINT; + pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; + floatingpoint_type = TYPE_LONGDOUBLE; + cp++; + } #if defined __APPLE__ && defined __MACH__ - /* On Mac OS X 10.3, PRIdMAX is defined as "qd". - We cannot change it to "lld" because PRIdMAX must also - be understood by the system's printf routines. */ - else if (*cp == 'q') - { - if (64 / 8 > sizeof (long)) - { - /* int64_t = long long */ - flags += 16; - } - else - { - /* int64_t = long */ - flags += 8; - } - cp++; - } + /* On Mac OS X 10.3, PRIdMAX is defined as "qd". + We cannot change it to "lld" because PRIdMAX must also + be understood by the system's printf routines. */ + else if (*cp == 'q') + { + if (64 / 8 > sizeof (long)) + { + /* int64_t = long long */ + signed_type = TYPE_LONGLONGINT; + unsigned_type = TYPE_ULONGLONGINT; + pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; + /* For backward compatibility only. */ + floatingpoint_type = TYPE_LONGDOUBLE; + } + else + { + /* int64_t = long */ + signed_type = TYPE_LONGINT; + unsigned_type = TYPE_ULONGINT; + pointer_type = TYPE_COUNT_LONGINT_POINTER; + } + cp++; + } #endif #if defined _WIN32 && ! defined __CYGWIN__ - /* On native Windows, PRIdMAX is defined as "I64d". - We cannot change it to "lld" because PRIdMAX must also - be understood by the system's printf routines. */ - else if (*cp == 'I' && cp[1] == '6' && cp[2] == '4') - { - if (64 / 8 > sizeof (long)) - { - /* __int64 = long long */ - flags += 16; - } - else - { - /* __int64 = long */ - flags += 8; - } - cp += 3; - } + /* On native Windows, PRIdMAX is defined as "I64d". + We cannot change it to "lld" because PRIdMAX must also + be understood by the system's printf routines. */ + else if (*cp == 'I' && cp[1] == '6' && cp[2] == '4') + { + if (64 / 8 > sizeof (long)) + { + /* __int64_t = long long */ + signed_type = TYPE_LONGLONGINT; + unsigned_type = TYPE_ULONGLONGINT; + pointer_type = TYPE_COUNT_LONGLONGINT_POINTER; + /* For backward compatibility only. */ + floatingpoint_type = TYPE_LONGDOUBLE; + } + else + { + /* __int64_t = long */ + signed_type = TYPE_LONGINT; + unsigned_type = TYPE_ULONGINT; + pointer_type = TYPE_COUNT_LONGINT_POINTER; + } + cp++; + } #endif - else - break; - } - /* Read the conversion character. */ - c = *cp++; - switch (c) - { - case 'd': case 'i': - /* If 'long long' is larger than 'long': */ - if (flags >= 16 || (flags & 4)) - type = TYPE_LONGLONGINT; - else - /* If 'long long' is the same as 'long', we parse "lld" into - TYPE_LONGINT. */ - if (flags >= 8) - type = TYPE_LONGINT; - else if (flags & 2) - type = TYPE_SCHAR; - else if (flags & 1) - type = TYPE_SHORT; - else - type = TYPE_INT; - break; - case 'o': case 'u': case 'x': case 'X': - /* If 'unsigned long long' is larger than 'unsigned long': */ - if (flags >= 16 || (flags & 4)) - type = TYPE_ULONGLONGINT; - else - /* If 'unsigned long long' is the same as 'unsigned long', we - parse "llu" into TYPE_ULONGINT. */ - if (flags >= 8) - type = TYPE_ULONGINT; - else if (flags & 2) - type = TYPE_UCHAR; - else if (flags & 1) - type = TYPE_USHORT; - else - type = TYPE_UINT; - break; - case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': - case 'a': case 'A': - if (flags >= 16 || (flags & 4)) - type = TYPE_LONGDOUBLE; - else - type = TYPE_DOUBLE; - break; - case 'c': - if (flags >= 8) + /* Read the conversion character. */ + c = *cp++; + switch (c) + { + case 'd': case 'i': + type = signed_type; + break; + case 'b': case 'o': case 'u': case 'x': case 'X': + #if SUPPORT_GNU_PRINTF_DIRECTIVES \ + || (__GLIBC__ + (__GLIBC_MINOR__ >= 35) > 2) + case 'B': + #endif + type = unsigned_type; + break; + case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': + case 'a': case 'A': + type = floatingpoint_type; + break; + case 'c': + if (signed_type == TYPE_LONGINT + /* For backward compatibility only. */ + || signed_type == TYPE_LONGLONGINT) #if HAVE_WINT_T - type = TYPE_WIDE_CHAR; + type = TYPE_WIDE_CHAR; #else - goto error; + goto error; #endif - else - type = TYPE_CHAR; - break; + else + type = TYPE_CHAR; + break; #if HAVE_WINT_T - case 'C': - type = TYPE_WIDE_CHAR; - c = 'c'; - break; + case 'C': + type = TYPE_WIDE_CHAR; + c = 'c'; + break; #endif - case 's': - if (flags >= 8) + case 's': + if (signed_type == TYPE_LONGINT + /* For backward compatibility only. */ + || signed_type == TYPE_LONGLONGINT) #if HAVE_WCHAR_T - type = TYPE_WIDE_STRING; + type = TYPE_WIDE_STRING; #else - goto error; + goto error; #endif - else - type = TYPE_STRING; - break; + else + type = TYPE_STRING; + break; #if HAVE_WCHAR_T - case 'S': - type = TYPE_WIDE_STRING; - c = 's'; - break; + case 'S': + type = TYPE_WIDE_STRING; + c = 's'; + break; #endif - case 'p': - type = TYPE_POINTER; - break; - case 'n': - /* If 'long long' is larger than 'long': */ - if (flags >= 16 || (flags & 4)) - type = TYPE_COUNT_LONGLONGINT_POINTER; - else - /* If 'long long' is the same as 'long', we parse "lln" into - TYPE_COUNT_LONGINT_POINTER. */ - if (flags >= 8) - type = TYPE_COUNT_LONGINT_POINTER; - else if (flags & 2) - type = TYPE_COUNT_SCHAR_POINTER; - else if (flags & 1) - type = TYPE_COUNT_SHORT_POINTER; - else - type = TYPE_COUNT_INT_POINTER; - break; + case 'p': + type = TYPE_POINTER; + break; + case 'n': + type = pointer_type; + break; #if ENABLE_UNISTDIO - /* The unistdio extensions. */ - case 'U': - if (flags >= 16) - type = TYPE_U32_STRING; - else if (flags >= 8) - type = TYPE_U16_STRING; - else - type = TYPE_U8_STRING; - break; + /* The unistdio extensions. */ + case 'U': + if (signed_type == TYPE_LONGLONGINT) + type = TYPE_U32_STRING; + else if (signed_type == TYPE_LONGINT) + type = TYPE_U16_STRING; + else + type = TYPE_U8_STRING; + break; #endif - case '%': - type = TYPE_NONE; - break; - default: - /* Unknown conversion character. */ - goto error; - } - } + case '%': + type = TYPE_NONE; + break; + default: + /* Unknown conversion character. */ + goto error; + } if (type != TYPE_NONE) { diff --git a/lib/printf-parse.h b/lib/printf-parse.h index 1f86e32c99e..45febac1f04 100644 --- a/lib/printf-parse.h +++ b/lib/printf-parse.h @@ -61,7 +61,7 @@ #define N_DIRECT_ALLOC_DIRECTIVES 7 const char* precision_start; const char* precision_end; size_t precision_arg_index; - char conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */ + char conversion; /* d i b B o u x X f F e E g G a A c s p n U % but not C S */ size_t arg_index; } char_directive; @@ -91,7 +91,7 @@ #define N_DIRECT_ALLOC_DIRECTIVES 7 const uint8_t* precision_start; const uint8_t* precision_end; size_t precision_arg_index; - uint8_t conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */ + uint8_t conversion; /* d i b B o u x X f F e E g G a A c s p n U % but not C S */ size_t arg_index; } u8_directive; @@ -119,7 +119,7 @@ #define N_DIRECT_ALLOC_DIRECTIVES 7 const uint16_t* precision_start; const uint16_t* precision_end; size_t precision_arg_index; - uint16_t conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */ + uint16_t conversion; /* d i b B o u x X f F e E g G a A c s p n U % but not C S */ size_t arg_index; } u16_directive; @@ -147,7 +147,7 @@ #define N_DIRECT_ALLOC_DIRECTIVES 7 const uint32_t* precision_start; const uint32_t* precision_end; size_t precision_arg_index; - uint32_t conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */ + uint32_t conversion; /* d i b B o u x X f F e E g G a A c s p n U % but not C S */ size_t arg_index; } u32_directive; diff --git a/lib/size_max.h b/lib/size_max.h index 48af0250556..2cfd31a59b8 100644 --- a/lib/size_max.h +++ b/lib/size_max.h @@ -18,6 +18,11 @@ #ifndef GNULIB_SIZE_MAX_H #define GNULIB_SIZE_MAX_H +/* This file uses HAVE_STDINT_H. */ +#if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." +#endif + /* Get SIZE_MAX declaration on systems like Solaris 7/8/9. */ # include /* Get SIZE_MAX declaration on systems like glibc 2. */ diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c index 72b8cdbfa6b..802790e14e4 100644 --- a/lib/vasnprintf.c +++ b/lib/vasnprintf.c @@ -85,7 +85,7 @@ #include /* memcpy(), strlen() */ #include /* mbstate_t, mbrtowc(), mbrlen(), wcrtomb() */ #include /* errno */ -#include /* CHAR_BIT */ +#include /* CHAR_BIT, INT_WIDTH, LONG_WIDTH */ #include /* DBL_MAX_EXP, LDBL_MAX_EXP */ #if HAVE_NL_LANGINFO # include @@ -103,29 +103,29 @@ #include "attribute.h" -#if (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL +#if NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) # include # include "float+.h" #endif -#if (NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL +#if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE # include # include "isnand-nolibm.h" #endif -#if (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE) && !defined IN_LIBINTL +#if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) # include # include "isnanl-nolibm.h" # include "fpucw.h" #endif -#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL +#if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE # include # include "isnand-nolibm.h" # include "printf-frexp.h" #endif -#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL +#if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) # include # include "isnanl-nolibm.h" # include "printf-frexpl.h" @@ -138,8 +138,6 @@ # define VASNPRINTF vasnwprintf # define FCHAR_T wchar_t # define DCHAR_T wchar_t -# define TCHAR_T wchar_t -# define DCHAR_IS_TCHAR 1 # define DIRECTIVE wchar_t_directive # define DIRECTIVES wchar_t_directives # define PRINTF_PARSE wprintf_parse @@ -159,24 +157,32 @@ # endif #endif #if WIDE_CHAR_VERSION - /* TCHAR_T is wchar_t. */ -# define USE_SNPRINTF 1 -# if HAVE_DECL__SNWPRINTF - /* On Windows, the function swprintf() has a different signature than - on Unix; we use the function _snwprintf() or - on mingw - snwprintf() - instead. The mingw function snwprintf() has fewer bugs than the - MSVCRT function _snwprintf(), so prefer that. */ -# if defined __MINGW32__ -# define SNPRINTF snwprintf + /* DCHAR_T is wchar_t. */ +# if HAVE_DECL__SNWPRINTF || (HAVE_SWPRINTF && HAVE_WORKING_SWPRINTF) +# define TCHAR_T wchar_t +# define DCHAR_IS_TCHAR 1 +# define USE_SNPRINTF 1 +# if HAVE_DECL__SNWPRINTF + /* On Windows, the function swprintf() has a different signature than + on Unix; we use the function _snwprintf() or - on mingw - snwprintf() + instead. The mingw function snwprintf() has fewer bugs than the + MSVCRT function _snwprintf(), so prefer that. */ +# if defined __MINGW32__ +# define SNPRINTF snwprintf +# else +# define SNPRINTF _snwprintf +# define USE_MSVC__SNPRINTF 1 +# endif # else -# define SNPRINTF _snwprintf -# define USE_MSVC__SNPRINTF 1 + /* Unix. */ +# define SNPRINTF swprintf # endif # else - /* Unix. */ -# define SNPRINTF swprintf + /* Old platforms such as NetBSD 3.0, OpenBSD 3.8, HP-UX 11.00, IRIX 6.5. */ +# define TCHAR_T char # endif -#else +#endif +#if !WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR /* TCHAR_T is char. */ /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'. But don't use it on BeOS, since BeOS snprintf produces no output if the @@ -241,7 +247,7 @@ local_strnlen (const char *string, size_t maxlen) # endif #endif -#if (((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && WIDE_CHAR_VERSION) || ((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (NEED_PRINTF_DIRECTIVE_LS && !defined IN_LIBINTL)) && !WIDE_CHAR_VERSION && DCHAR_IS_TCHAR)) && HAVE_WCHAR_T +#if (((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && WIDE_CHAR_VERSION) || ((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS) && !WIDE_CHAR_VERSION && DCHAR_IS_TCHAR)) && HAVE_WCHAR_T # if HAVE_WCSLEN # define local_wcslen wcslen # else @@ -264,8 +270,8 @@ local_wcslen (const wchar_t *s) # endif #endif -#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && HAVE_WCHAR_T && WIDE_CHAR_VERSION -# if HAVE_WCSNLEN +#if (!USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && HAVE_WCHAR_T && WIDE_CHAR_VERSION +# if HAVE_WCSNLEN && HAVE_DECL_WCSNLEN # define local_wcsnlen wcsnlen # else # ifndef local_wcsnlen_defined @@ -283,7 +289,7 @@ local_wcsnlen (const wchar_t *s, size_t maxlen) # endif #endif -#if (((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (NEED_PRINTF_DIRECTIVE_LS && !defined IN_LIBINTL) || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) || (ENABLE_WCHAR_FALLBACK && HAVE_WINT_T)) && !WIDE_CHAR_VERSION +#if (((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T)) && !WIDE_CHAR_VERSION # if ENABLE_WCHAR_FALLBACK static size_t wctomb_fallback (char *s, wchar_t wc) @@ -351,7 +357,7 @@ local_wctomb (char *s, wchar_t wc) # endif #endif -#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL +#if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) /* Determine the decimal-point character according to the current locale. */ # ifndef decimal_point_char_defined # define decimal_point_char_defined 1 @@ -378,7 +384,7 @@ decimal_point_char (void) # endif #endif -#if NEED_PRINTF_INFINITE_DOUBLE && !NEED_PRINTF_DOUBLE && !defined IN_LIBINTL +#if NEED_PRINTF_INFINITE_DOUBLE && !NEED_PRINTF_DOUBLE /* Equivalent to !isfinite(x) || x == 0, but does not require libm. */ static int @@ -389,7 +395,7 @@ is_infinite_or_zero (double x) #endif -#if NEED_PRINTF_INFINITE_LONG_DOUBLE && !NEED_PRINTF_LONG_DOUBLE && !defined IN_LIBINTL +#if NEED_PRINTF_INFINITE_LONG_DOUBLE && !NEED_PRINTF_LONG_DOUBLE /* Equivalent to !isfinite(x) || x == 0, but does not require libm. */ static int @@ -400,7 +406,7 @@ is_infinite_or_zerol (long double x) #endif -#if (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL +#if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE /* Converting 'long double' to decimal without rare rounding bugs requires real bignums. We use the naming conventions of GNU gmp, but vastly simpler @@ -1604,7 +1610,7 @@ is_borderline (const char *digits, size_t precision) #endif -#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF +#if !USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF /* Use a different function name, to make it possible that the 'wchar_t' parametrization and the 'char' parametrization get compiled in the same @@ -1627,24 +1633,156 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, switch (conversion) { case 'd': case 'i': case 'u': - if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) - tmp_length = - (unsigned int) (sizeof (unsigned long long) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - else if (type == TYPE_LONGINT || type == TYPE_ULONGINT) - tmp_length = - (unsigned int) (sizeof (unsigned long) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ - else - tmp_length = - (unsigned int) (sizeof (unsigned int) * CHAR_BIT - * 0.30103 /* binary -> decimal */ - ) - + 1; /* turn floor into ceil */ + switch (type) + { + default: + tmp_length = + (unsigned int) (sizeof (unsigned int) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_LONGINT: + tmp_length = + (unsigned int) (sizeof (long int) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_ULONGINT: + tmp_length = + (unsigned int) (sizeof (unsigned long int) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_LONGLONGINT: + tmp_length = + (unsigned int) (sizeof (long long int) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_ULONGLONGINT: + tmp_length = + (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_INT8_T: + tmp_length = + (unsigned int) (sizeof (int8_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT8_T: + tmp_length = + (unsigned int) (sizeof (uint8_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_INT16_T: + tmp_length = + (unsigned int) (sizeof (int16_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT16_T: + tmp_length = + (unsigned int) (sizeof (uint16_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_INT32_T: + tmp_length = + (unsigned int) (sizeof (int32_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT32_T: + tmp_length = + (unsigned int) (sizeof (uint32_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_INT64_T: + tmp_length = + (unsigned int) (sizeof (int64_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT64_T: + tmp_length = + (unsigned int) (sizeof (uint64_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_INT_FAST8_T: + tmp_length = + (unsigned int) (sizeof (int_fast8_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST8_T: + tmp_length = + (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_INT_FAST16_T: + tmp_length = + (unsigned int) (sizeof (int_fast16_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST16_T: + tmp_length = + (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_INT_FAST32_T: + tmp_length = + (unsigned int) (sizeof (int_fast32_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST32_T: + tmp_length = + (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_INT_FAST64_T: + tmp_length = + (unsigned int) (sizeof (int_fast64_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST64_T: + tmp_length = + (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT + * 0.30103 /* binary -> decimal */ + ) + + 1; /* turn floor into ceil */ + break; + } if (tmp_length < precision) tmp_length = precision; /* Multiply by 2, as an estimate for FLAG_GROUP. */ @@ -1653,25 +1791,156 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, tmp_length = xsum (tmp_length, 1); break; + case 'b': + #if SUPPORT_GNU_PRINTF_DIRECTIVES \ + || (__GLIBC__ + (__GLIBC_MINOR__ >= 35) > 2) + case 'B': + #endif + switch (type) + { + default: + tmp_length = + (unsigned int) (sizeof (unsigned int) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_ULONGINT: + tmp_length = + (unsigned int) (sizeof (unsigned long int) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_ULONGLONGINT: + tmp_length = + (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT8_T: + tmp_length = + (unsigned int) (sizeof (uint8_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT16_T: + tmp_length = + (unsigned int) (sizeof (uint16_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT32_T: + tmp_length = + (unsigned int) (sizeof (uint32_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT64_T: + tmp_length = + (unsigned int) (sizeof (uint64_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST8_T: + tmp_length = + (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST16_T: + tmp_length = + (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST32_T: + tmp_length = + (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST64_T: + tmp_length = + (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + } + if (tmp_length < precision) + tmp_length = precision; + /* Add 2, to account for a prefix from the alternate form. */ + tmp_length = xsum (tmp_length, 2); + break; + case 'o': - if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) - tmp_length = - (unsigned int) (sizeof (unsigned long long) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - else if (type == TYPE_LONGINT || type == TYPE_ULONGINT) - tmp_length = - (unsigned int) (sizeof (unsigned long) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ - else - tmp_length = - (unsigned int) (sizeof (unsigned int) * CHAR_BIT - * 0.333334 /* binary -> octal */ - ) - + 1; /* turn floor into ceil */ + switch (type) + { + default: + tmp_length = + (unsigned int) (sizeof (unsigned int) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_ULONGINT: + tmp_length = + (unsigned int) (sizeof (unsigned long int) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_ULONGLONGINT: + tmp_length = + (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT8_T: + tmp_length = + (unsigned int) (sizeof (uint8_t) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT16_T: + tmp_length = + (unsigned int) (sizeof (uint16_t) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT32_T: + tmp_length = + (unsigned int) (sizeof (uint32_t) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT64_T: + tmp_length = + (unsigned int) (sizeof (uint64_t) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST8_T: + tmp_length = + (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST16_T: + tmp_length = + (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST32_T: + tmp_length = + (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST64_T: + tmp_length = + (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT + * 0.333334 /* binary -> octal */ + ) + + 1; /* turn floor into ceil */ + break; + } if (tmp_length < precision) tmp_length = precision; /* Add 1, to account for a leading sign. */ @@ -1679,27 +1948,89 @@ MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion, break; case 'x': case 'X': - if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) - tmp_length = - (unsigned int) (sizeof (unsigned long long) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - else if (type == TYPE_LONGINT || type == TYPE_ULONGINT) - tmp_length = - (unsigned int) (sizeof (unsigned long) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ - else - tmp_length = - (unsigned int) (sizeof (unsigned int) * CHAR_BIT - * 0.25 /* binary -> hexadecimal */ - ) - + 1; /* turn floor into ceil */ + switch (type) + { + default: + tmp_length = + (unsigned int) (sizeof (unsigned int) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_ULONGINT: + tmp_length = + (unsigned int) (sizeof (unsigned long int) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_ULONGLONGINT: + tmp_length = + (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT8_T: + tmp_length = + (unsigned int) (sizeof (uint8_t) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT16_T: + tmp_length = + (unsigned int) (sizeof (uint16_t) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT32_T: + tmp_length = + (unsigned int) (sizeof (uint32_t) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT64_T: + tmp_length = + (unsigned int) (sizeof (uint64_t) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST8_T: + tmp_length = + (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST16_T: + tmp_length = + (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST32_T: + tmp_length = + (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST64_T: + tmp_length = + (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT + * 0.25 /* binary -> hexadecimal */ + ) + + 1; /* turn floor into ceil */ + break; + } if (tmp_length < precision) tmp_length = precision; - /* Add 2, to account for a leading sign or alternate form. */ + /* Add 2, to account for a prefix from the alternate form. */ tmp_length = xsum (tmp_length, 2); break; @@ -2005,6 +2336,30 @@ #define ENSURE_ALLOCATION(needed) \ case TYPE_COUNT_LONGLONGINT_POINTER: *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length; break; + case TYPE_COUNT_INT8_T_POINTER: + *a.arg[dp->arg_index].a.a_count_int8_t_pointer = length; + break; + case TYPE_COUNT_INT16_T_POINTER: + *a.arg[dp->arg_index].a.a_count_int16_t_pointer = length; + break; + case TYPE_COUNT_INT32_T_POINTER: + *a.arg[dp->arg_index].a.a_count_int32_t_pointer = length; + break; + case TYPE_COUNT_INT64_T_POINTER: + *a.arg[dp->arg_index].a.a_count_int64_t_pointer = length; + break; + case TYPE_COUNT_INT_FAST8_T_POINTER: + *a.arg[dp->arg_index].a.a_count_int_fast8_t_pointer = length; + break; + case TYPE_COUNT_INT_FAST16_T_POINTER: + *a.arg[dp->arg_index].a.a_count_int_fast16_t_pointer = length; + break; + case TYPE_COUNT_INT_FAST32_T_POINTER: + *a.arg[dp->arg_index].a.a_count_int_fast32_t_pointer = length; + break; + case TYPE_COUNT_INT_FAST64_T_POINTER: + *a.arg[dp->arg_index].a.a_count_int_fast64_t_pointer = length; + break; default: abort (); } @@ -2394,7 +2749,150 @@ #define ENSURE_ALLOCATION(needed) \ } } #endif -#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (NEED_PRINTF_DIRECTIVE_LS && !defined IN_LIBINTL) || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T +#if WIDE_CHAR_VERSION && !DCHAR_IS_TCHAR + else if ((dp->conversion == 's' + && a.arg[dp->arg_index].type == TYPE_WIDE_STRING) + || (dp->conversion == 'c' + && a.arg[dp->arg_index].type == TYPE_WIDE_CHAR)) + { + /* %ls or %lc in vasnwprintf. See the specification of + fwprintf. */ + /* It would be silly to use snprintf ("%ls", ...) and then + convert back the result from a char[] to a wchar_t[]. + Instead, just copy the argument wchar_t[] to the result. */ + int flags = dp->flags; + size_t width; + + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + width = arg; + if (arg < 0) + { + /* "A negative field width is taken as a '-' flag + followed by a positive field width." */ + flags |= FLAG_LEFT; + width = -width; + } + } + else + { + const FCHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + } + + { + const wchar_t *ls_arg; + wchar_t lc_arg[1]; + size_t characters; + + if (dp->conversion == 's') + { + int has_precision; + size_t precision; + + has_precision = 0; + precision = 6; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + /* "A negative precision is taken as if the precision + were omitted." */ + if (arg >= 0) + { + precision = arg; + has_precision = 1; + } + } + else + { + const FCHAR_T *digitp = dp->precision_start + 1; + + precision = 0; + while (digitp != dp->precision_end) + precision = xsum (xtimes (precision, 10), *digitp++ - '0'); + has_precision = 1; + } + } + + ls_arg = a.arg[dp->arg_index].a.a_wide_string; + + if (has_precision) + { + /* Use only at most PRECISION wide characters, from + the left. */ + const wchar_t *ls_arg_end; + + ls_arg_end = ls_arg; + characters = 0; + for (; precision > 0; precision--) + { + if (*ls_arg_end == 0) + /* Found the terminating null wide character. */ + break; + ls_arg_end++; + characters++; + } + } + else + { + /* Use the entire string, and count the number of wide + characters. */ + characters = local_wcslen (ls_arg); + } + } + else /* dp->conversion == 'c' */ + { + lc_arg[0] = (wchar_t) a.arg[dp->arg_index].a.a_wide_char; + ls_arg = lc_arg; + characters = 1; + } + + { + size_t total = (characters < width ? width : characters); + ENSURE_ALLOCATION (xsum (length, total)); + + if (characters < width && !(flags & FLAG_LEFT)) + { + size_t n = width - characters; + DCHAR_SET (result + length, ' ', n); + length += n; + } + + if (characters > 0) + { + DCHAR_CPY (result + length, ls_arg, characters); + length += characters; + } + + if (characters < width && (flags & FLAG_LEFT)) + { + size_t n = width - characters; + DCHAR_SET (result + length, ' ', n); + length += n; + } + } + } + } +#endif +#if (!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T else if (dp->conversion == 's' # if WIDE_CHAR_VERSION && a.arg[dp->arg_index].type != TYPE_WIDE_STRING @@ -2602,10 +3100,12 @@ #define ENSURE_ALLOCATION(needed) \ # else count = mbtowc (&wc, arg, arg_end - arg); # endif - if (count <= 0) - /* mbrtowc not consistent with mbrlen, or mbtowc - not consistent with mblen. */ + if (count == 0) + /* mbrtowc not consistent with strlen. */ abort (); + if (count < 0) + /* Invalid or incomplete multibyte character. */ + goto fail_with_EILSEQ; ENSURE_ALLOCATION (xsum (length, 1)); result[length++] = wc; arg += count; @@ -2847,12 +3347,13 @@ #define ENSURE_ALLOCATION(needed) \ # endif } #endif -#if ENABLE_WCHAR_FALLBACK && HAVE_WINT_T && !WIDE_CHAR_VERSION +#if (NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T && !WIDE_CHAR_VERSION else if (dp->conversion == 'c' && a.arg[dp->arg_index].type == TYPE_WIDE_CHAR) { /* Implement the 'lc' directive ourselves, in order to provide - the fallback that avoids EILSEQ. */ + a correct behaviour for the null wint_t argument and/or the + fallback that avoids EILSEQ. */ int flags = dp->flags; int has_width; size_t width; @@ -2918,8 +3419,8 @@ #define ENSURE_ALLOCATION(needed) \ count = local_wcrtomb (cbuf, arg, &state); if (count < 0) - /* Inconsistency. */ - abort (); + /* Cannot convert. */ + goto fail_with_EILSEQ; characters = count; } } @@ -3017,9 +3518,9 @@ #define ENSURE_ALLOCATION(needed) \ # endif count = local_wcrtomb (cbuf, arg, &state); - if (count <= 0) - /* Inconsistency. */ - abort (); + if (count < 0) + /* Cannot convert. */ + goto fail_with_EILSEQ; ENSURE_ALLOCATION (xsum (length, count)); memcpy (result + length, cbuf, count); length += count; @@ -3043,31 +3544,14 @@ #define ENSURE_ALLOCATION(needed) \ } } #endif -#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL - else if ((dp->conversion == 'a' || dp->conversion == 'A') -# if !(NEED_PRINTF_DIRECTIVE_A || (NEED_PRINTF_LONG_DOUBLE && NEED_PRINTF_DOUBLE)) - && (0 -# if NEED_PRINTF_DOUBLE - || a.arg[dp->arg_index].type == TYPE_DOUBLE -# endif -# if NEED_PRINTF_LONG_DOUBLE - || a.arg[dp->arg_index].type == TYPE_LONGDOUBLE -# endif - ) -# endif - ) +#if NEED_WPRINTF_DIRECTIVE_C && WIDE_CHAR_VERSION + else if (dp->conversion == 'c' + && a.arg[dp->arg_index].type != TYPE_WIDE_CHAR) { - arg_type type = a.arg[dp->arg_index].type; + /* Implement the 'c' directive ourselves, in order to avoid + EILSEQ in the "C" locale. */ int flags = dp->flags; size_t width; - int has_precision; - size_t precision; - size_t tmp_length; - size_t count; - DCHAR_T tmpbuf[700]; - DCHAR_T *tmp; - DCHAR_T *pad_ptr; - DCHAR_T *p; width = 0; if (dp->width_start != dp->width_end) @@ -3098,20 +3582,422 @@ #define ENSURE_ALLOCATION(needed) \ } } - has_precision = 0; - precision = 0; - if (dp->precision_start != dp->precision_end) - { - if (dp->precision_arg_index != ARG_NONE) - { - int arg; + /* %c in vasnwprintf. See the specification of fwprintf. */ + { + char arg = (char) a.arg[dp->arg_index].a.a_char; + mbstate_t state; + wchar_t wc; - if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) - abort (); - arg = a.arg[dp->precision_arg_index].a.a_int; - /* "A negative precision is taken as if the precision - were omitted." */ - if (arg >= 0) + memset (&state, '\0', sizeof (mbstate_t)); + int count = mbrtowc (&wc, &arg, 1, &state); + if (count < 0) + /* Invalid or incomplete multibyte character. */ + goto fail_with_EILSEQ; + + if (1 < width && !(flags & FLAG_LEFT)) + { + size_t n = width - 1; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + + ENSURE_ALLOCATION (xsum (length, 1)); + result[length++] = wc; + + if (1 < width && (flags & FLAG_LEFT)) + { + size_t n = width - 1; + ENSURE_ALLOCATION (xsum (length, n)); + DCHAR_SET (result + length, ' ', n); + length += n; + } + } + } +#endif +#if NEED_PRINTF_DIRECTIVE_B || NEED_PRINTF_DIRECTIVE_UPPERCASE_B + else if (0 +# if NEED_PRINTF_DIRECTIVE_B + || (dp->conversion == 'b') +# endif +# if NEED_PRINTF_DIRECTIVE_UPPERCASE_B + || (dp->conversion == 'B') +# endif + ) + { + arg_type type = a.arg[dp->arg_index].type; + int flags = dp->flags; + int has_width; + size_t width; + int has_precision; + size_t precision; + size_t tmp_length; + size_t count; + DCHAR_T tmpbuf[700]; + DCHAR_T *tmp; + DCHAR_T *tmp_end; + DCHAR_T *tmp_start; + DCHAR_T *pad_ptr; + DCHAR_T *p; + + has_width = 0; + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + width = arg; + if (arg < 0) + { + /* "A negative field width is taken as a '-' flag + followed by a positive field width." */ + flags |= FLAG_LEFT; + width = -width; + } + } + else + { + const FCHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + has_width = 1; + } + + has_precision = 0; + precision = 1; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + /* "A negative precision is taken as if the precision + were omitted." */ + if (arg >= 0) + { + precision = arg; + has_precision = 1; + } + } + else + { + const FCHAR_T *digitp = dp->precision_start + 1; + + precision = 0; + while (digitp != dp->precision_end) + precision = xsum (xtimes (precision, 10), *digitp++ - '0'); + has_precision = 1; + } + } + + /* Allocate a temporary buffer of sufficient size. */ + switch (type) + { + default: + tmp_length = + (unsigned int) (sizeof (unsigned int) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_ULONGINT: + tmp_length = + (unsigned int) (sizeof (unsigned long int) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_ULONGLONGINT: + tmp_length = + (unsigned int) (sizeof (unsigned long long int) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT8_T: + tmp_length = + (unsigned int) (sizeof (uint8_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT16_T: + tmp_length = + (unsigned int) (sizeof (uint16_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT32_T: + tmp_length = + (unsigned int) (sizeof (uint32_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT64_T: + tmp_length = + (unsigned int) (sizeof (uint64_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST8_T: + tmp_length = + (unsigned int) (sizeof (uint_fast8_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST16_T: + tmp_length = + (unsigned int) (sizeof (uint_fast16_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST32_T: + tmp_length = + (unsigned int) (sizeof (uint_fast32_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + case TYPE_UINT_FAST64_T: + tmp_length = + (unsigned int) (sizeof (uint_fast64_t) * CHAR_BIT) + + 1; /* turn floor into ceil */ + break; + } + if (tmp_length < precision) + tmp_length = precision; + /* Add 2, to account for a prefix from the alternate form. */ + tmp_length = xsum (tmp_length, 2); + + if (tmp_length < width) + tmp_length = width; + + if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T)) + tmp = tmpbuf; + else + { + size_t tmp_memsize = xtimes (tmp_length, sizeof (DCHAR_T)); + + if (size_overflow_p (tmp_memsize)) + /* Overflow, would lead to out of memory. */ + goto out_of_memory; + tmp = (DCHAR_T *) malloc (tmp_memsize); + if (tmp == NULL) + /* Out of memory. */ + goto out_of_memory; + } + + tmp_end = tmp + tmp_length; + + unsigned long long arg; + switch (type) + { + case TYPE_UCHAR: + arg = a.arg[dp->arg_index].a.a_uchar; + break; + case TYPE_USHORT: + arg = a.arg[dp->arg_index].a.a_ushort; + break; + case TYPE_UINT: + arg = a.arg[dp->arg_index].a.a_uint; + break; + case TYPE_ULONGINT: + arg = a.arg[dp->arg_index].a.a_ulongint; + break; + case TYPE_ULONGLONGINT: + arg = a.arg[dp->arg_index].a.a_ulonglongint; + break; + case TYPE_UINT8_T: + arg = a.arg[dp->arg_index].a.a_uint8_t; + break; + case TYPE_UINT16_T: + arg = a.arg[dp->arg_index].a.a_uint16_t; + break; + case TYPE_UINT32_T: + arg = a.arg[dp->arg_index].a.a_uint32_t; + break; + case TYPE_UINT64_T: + arg = a.arg[dp->arg_index].a.a_uint64_t; + break; + case TYPE_UINT_FAST8_T: + arg = a.arg[dp->arg_index].a.a_uint_fast8_t; + break; + case TYPE_UINT_FAST16_T: + arg = a.arg[dp->arg_index].a.a_uint_fast16_t; + break; + case TYPE_UINT_FAST32_T: + arg = a.arg[dp->arg_index].a.a_uint_fast32_t; + break; + case TYPE_UINT_FAST64_T: + arg = a.arg[dp->arg_index].a.a_uint_fast64_t; + break; + default: + abort (); + } + int need_prefix = ((flags & FLAG_ALT) && arg != 0); + + p = tmp_end; + /* "The result of converting a zero value with a precision + of zero is no characters." */ + if (!(has_precision && precision == 0 && arg == 0)) + { + do + { + *--p = '0' + (arg & 1); + arg = arg >> 1; + } + while (arg != 0); + } + + if (has_precision) + { + DCHAR_T *digits_start = tmp_end - precision; + while (p > digits_start) + *--p = '0'; + } + + pad_ptr = p; + + if (need_prefix) + { +# if NEED_PRINTF_DIRECTIVE_B && !NEED_PRINTF_DIRECTIVE_UPPERCASE_B + *--p = 'b'; +# elif NEED_PRINTF_DIRECTIVE_UPPERCASE_B && !NEED_PRINTF_DIRECTIVE_B + *--p = 'B'; +# else + *--p = dp->conversion; +# endif + *--p = '0'; + } + tmp_start = p; + + /* The generated string now extends from tmp_start to tmp_end, + with the zero padding insertion point being at pad_ptr, + tmp_start <= pad_ptr <= tmp_end. */ + count = tmp_end - tmp_start; + + if (count < width) + { + size_t pad = width - count; + + if (flags & FLAG_LEFT) + { + /* Pad with spaces on the right. */ + for (p = tmp_start; p < tmp_end; p++) + *(p - pad) = *p; + for (p = tmp_end - pad; p < tmp_end; p++) + *p = ' '; + } + else if ((flags & FLAG_ZERO) + /* Neither ISO C nor POSIX specify that the '0' + flag is ignored when a width and a precision + are both present. But most implementations + do so. */ + && !(has_width && has_precision)) + { + /* Pad with zeroes. */ + for (p = tmp_start; p < pad_ptr; p++) + *(p - pad) = *p; + for (p = pad_ptr - pad; p < pad_ptr; p++) + *p = '0'; + } + else + { + /* Pad with spaces on the left. */ + for (p = tmp_start - pad; p < tmp_start; p++) + *p = ' '; + } + + tmp_start = tmp_start - pad; + } + + count = tmp_end - tmp_start; + + if (count > tmp_length) + /* tmp_length was incorrectly calculated - fix the + code above! */ + abort (); + + /* Make room for the result. */ + if (count >= allocated - length) + { + size_t n = xsum (length, count); + + ENSURE_ALLOCATION (n); + } + + /* Append the result. */ + memcpy (result + length, tmp_start, count * sizeof (DCHAR_T)); + if (tmp != tmpbuf) + free (tmp); + length += count; + } +#endif +#if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) + else if ((dp->conversion == 'a' || dp->conversion == 'A') +# if !(NEED_PRINTF_DIRECTIVE_A || (NEED_PRINTF_LONG_DOUBLE && NEED_PRINTF_DOUBLE)) + && (0 +# if NEED_PRINTF_DOUBLE + || a.arg[dp->arg_index].type == TYPE_DOUBLE +# endif +# if NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) + || a.arg[dp->arg_index].type == TYPE_LONGDOUBLE +# endif + ) +# endif + ) + { + arg_type type = a.arg[dp->arg_index].type; + int flags = dp->flags; + size_t width; + int has_precision; + size_t precision; + size_t tmp_length; + size_t count; + DCHAR_T tmpbuf[700]; + DCHAR_T *tmp; + DCHAR_T *pad_ptr; + DCHAR_T *p; + + width = 0; + if (dp->width_start != dp->width_end) + { + if (dp->width_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->width_arg_index].a.a_int; + width = arg; + if (arg < 0) + { + /* "A negative field width is taken as a '-' flag + followed by a positive field width." */ + flags |= FLAG_LEFT; + width = -width; + } + } + else + { + const FCHAR_T *digitp = dp->width_start; + + do + width = xsum (xtimes (width, 10), *digitp++ - '0'); + while (digitp != dp->width_end); + } + } + + has_precision = 0; + precision = 0; + if (dp->precision_start != dp->precision_end) + { + if (dp->precision_arg_index != ARG_NONE) + { + int arg; + + if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) + abort (); + arg = a.arg[dp->precision_arg_index].a.a_int; + /* "A negative precision is taken as if the precision + were omitted." */ + if (arg >= 0) { precision = arg; has_precision = 1; @@ -3170,7 +4056,7 @@ #define ENSURE_ALLOCATION(needed) \ p = tmp; if (type == TYPE_LONGDOUBLE) { -# if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE +# if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || (NEED_WPRINTF_DIRECTIVE_LA && WIDE_CHAR_VERSION) long double arg = a.arg[dp->arg_index].a.a_longdouble; if (isnanl (arg)) @@ -3290,7 +4176,7 @@ #define ENSURE_ALLOCATION(needed) \ } } *p++ = dp->conversion - 'A' + 'P'; -# if WIDE_CHAR_VERSION +# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR { static const wchar_t decimal_format[] = { '%', '+', 'd', '\0' }; @@ -3441,7 +4327,7 @@ #define ENSURE_ALLOCATION(needed) \ } } *p++ = dp->conversion - 'A' + 'P'; -# if WIDE_CHAR_VERSION +# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR { static const wchar_t decimal_format[] = { '%', '+', 'd', '\0' }; @@ -3533,7 +4419,7 @@ #define ENSURE_ALLOCATION(needed) \ length += count; } #endif -#if (NEED_PRINTF_INFINITE_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL +#if NEED_PRINTF_INFINITE_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_LONG_DOUBLE else if ((dp->conversion == 'f' || dp->conversion == 'F' || dp->conversion == 'e' || dp->conversion == 'E' || dp->conversion == 'g' || dp->conversion == 'G' @@ -3901,7 +4787,7 @@ #define ENSURE_ALLOCATION(needed) \ } *p++ = dp->conversion; /* 'e' or 'E' */ -# if WIDE_CHAR_VERSION +# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR { static const wchar_t decimal_format[] = { '%', '+', '.', '2', 'd', '\0' }; @@ -4082,7 +4968,7 @@ #define ENSURE_ALLOCATION(needed) \ } } *p++ = dp->conversion - 'G' + 'E'; /* 'e' or 'E' */ -# if WIDE_CHAR_VERSION +# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR { static const wchar_t decimal_format[] = { '%', '+', '.', '2', 'd', '\0' }; @@ -4359,7 +5245,7 @@ #define ENSURE_ALLOCATION(needed) \ } *p++ = dp->conversion; /* 'e' or 'E' */ -# if WIDE_CHAR_VERSION +# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR { static const wchar_t decimal_format[] = /* Produce the same number of exponent digits @@ -4552,7 +5438,7 @@ #define ENSURE_ALLOCATION(needed) \ } } *p++ = dp->conversion - 'G' + 'E'; /* 'e' or 'E' */ -# if WIDE_CHAR_VERSION +# if WIDE_CHAR_VERSION && DCHAR_IS_TCHAR { static const wchar_t decimal_format[] = /* Produce the same number of exponent digits @@ -4720,13 +5606,13 @@ #define ENSURE_ALLOCATION(needed) \ { arg_type type = a.arg[dp->arg_index].type; int flags = dp->flags; -#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION int has_width; #endif -#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +#if !USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION size_t width; #endif -#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_UNBOUNDED_PRECISION +#if !USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION int has_precision; size_t precision; #endif @@ -4735,7 +5621,7 @@ #define ENSURE_ALLOCATION(needed) \ #else # define prec_ourselves 0 #endif -#if NEED_PRINTF_FLAG_LEFTADJUST +#if (WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST # define pad_ourselves 1 #elif !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION int pad_ourselves; @@ -4752,10 +5638,10 @@ #define ENSURE_ALLOCATION(needed) \ TCHAR_T *tmp; #endif -#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION has_width = 0; #endif -#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +#if !USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION width = 0; if (dp->width_start != dp->width_end) { @@ -4783,13 +5669,13 @@ #define ENSURE_ALLOCATION(needed) \ width = xsum (xtimes (width, 10), *digitp++ - '0'); while (digitp != dp->width_end); } -#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +# if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION has_width = 1; -#endif +# endif } #endif -#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_UNBOUNDED_PRECISION +#if !USE_SNPRINTF || (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION has_precision = 0; precision = 6; if (dp->precision_start != dp->precision_end) @@ -4826,6 +5712,11 @@ #define ENSURE_ALLOCATION(needed) \ switch (dp->conversion) { case 'd': case 'i': case 'u': + case 'b': + #if SUPPORT_GNU_PRINTF_DIRECTIVES \ + || (__GLIBC__ + (__GLIBC_MINOR__ >= 35) > 2) + case 'B': + #endif case 'o': case 'x': case 'X': case 'p': prec_ourselves = has_precision && (precision > 0); @@ -4837,7 +5728,7 @@ #define ENSURE_ALLOCATION(needed) \ #endif /* Decide whether to perform the padding ourselves. */ -#if !NEED_PRINTF_FLAG_LEFTADJUST && (!DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION) +#if !((WIDE_CHAR_VERSION && MUSL_LIBC) || NEED_PRINTF_FLAG_LEFTADJUST) && (!DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION) switch (dp->conversion) { # if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO @@ -4956,6 +5847,54 @@ #define ENSURE_ALLOCATION(needed) \ { case TYPE_LONGLONGINT: case TYPE_ULONGLONGINT: + #if INT8_WIDTH > LONG_WIDTH + case TYPE_INT8_T: + #endif + #if UINT8_WIDTH > LONG_WIDTH + case TYPE_UINT8_T: + #endif + #if INT16_WIDTH > LONG_WIDTH + case TYPE_INT16_T: + #endif + #if UINT16_WIDTH > LONG_WIDTH + case TYPE_UINT16_T: + #endif + #if INT32_WIDTH > LONG_WIDTH + case TYPE_INT32_T: + #endif + #if UINT32_WIDTH > LONG_WIDTH + case TYPE_UINT32_T: + #endif + #if INT64_WIDTH > LONG_WIDTH + case TYPE_INT64_T: + #endif + #if UINT64_WIDTH > LONG_WIDTH + case TYPE_UINT64_T: + #endif + #if INT_FAST8_WIDTH > LONG_WIDTH + case TYPE_INT_FAST8_T: + #endif + #if UINT_FAST8_WIDTH > LONG_WIDTH + case TYPE_UINT_FAST8_T: + #endif + #if INT_FAST16_WIDTH > LONG_WIDTH + case TYPE_INT_FAST16_T: + #endif + #if UINT_FAST16_WIDTH > LONG_WIDTH + case TYPE_UINT_FAST16_T: + #endif + #if INT_FAST32_WIDTH > LONG_WIDTH + case TYPE_INT3_FAST2_T: + #endif + #if UINT_FAST32_WIDTH > LONG_WIDTH + case TYPE_UINT_FAST32_T: + #endif + #if INT_FAST64_WIDTH > LONG_WIDTH + case TYPE_INT_FAST64_T: + #endif + #if UINT_FAST64_WIDTH > LONG_WIDTH + case TYPE_UINT_FAST64_T: + #endif #if defined _WIN32 && ! defined __CYGWIN__ *fbp++ = 'I'; *fbp++ = '6'; @@ -4967,12 +5906,60 @@ #define ENSURE_ALLOCATION(needed) \ FALLTHROUGH; case TYPE_LONGINT: case TYPE_ULONGINT: -#if HAVE_WINT_T + #if INT8_WIDTH > INT_WIDTH && INT8_WIDTH <= LONG_WIDTH + case TYPE_INT8_T: + #endif + #if UINT8_WIDTH > INT_WIDTH && UINT8_WIDTH <= LONG_WIDTH + case TYPE_UINT8_T: + #endif + #if INT16_WIDTH > INT_WIDTH && INT16_WIDTH <= LONG_WIDTH + case TYPE_INT16_T: + #endif + #if UINT16_WIDTH > INT_WIDTH && UINT16_WIDTH <= LONG_WIDTH + case TYPE_UINT16_T: + #endif + #if INT32_WIDTH > INT_WIDTH && INT32_WIDTH <= LONG_WIDTH + case TYPE_INT32_T: + #endif + #if UINT32_WIDTH > INT_WIDTH && UINT32_WIDTH <= LONG_WIDTH + case TYPE_UINT32_T: + #endif + #if INT64_WIDTH > INT_WIDTH && INT64_WIDTH <= LONG_WIDTH + case TYPE_INT64_T: + #endif + #if UINT64_WIDTH > INT_WIDTH && UINT64_WIDTH <= LONG_WIDTH + case TYPE_UINT64_T: + #endif + #if INT_FAST8_WIDTH > INT_WIDTH && INT_FAST8_WIDTH <= LONG_WIDTH + case TYPE_INT_FAST8_T: + #endif + #if UINT_FAST8_WIDTH > INT_WIDTH && UINT_FAST8_WIDTH <= LONG_WIDTH + case TYPE_UINT_FAST8_T: + #endif + #if INT_FAST16_WIDTH > INT_WIDTH && INT_FAST16_WIDTH <= LONG_WIDTH + case TYPE_INT_FAST16_T: + #endif + #if UINT_FAST16_WIDTH > INT_WIDTH && UINT_FAST16_WIDTH <= LONG_WIDTH + case TYPE_UINT_FAST16_T: + #endif + #if INT_FAST32_WIDTH > INT_WIDTH && INT_FAST32_WIDTH <= LONG_WIDTH + case TYPE_INT_FAST32_T: + #endif + #if UINT_FAST32_WIDTH > INT_WIDTH && UINT_FAST32_WIDTH <= LONG_WIDTH + case TYPE_UINT_FAST32_T: + #endif + #if INT_FAST64_WIDTH > INT_WIDTH && INT_FAST64_WIDTH <= LONG_WIDTH + case TYPE_INT_FAST64_T: + #endif + #if UINT_FAST64_WIDTH > INT_WIDTH && UINT_FAST64_WIDTH <= LONG_WIDTH + case TYPE_UINT_FAST64_T: + #endif + #if HAVE_WINT_T case TYPE_WIDE_CHAR: -#endif -#if HAVE_WCHAR_T + #endif + #if HAVE_WCHAR_T case TYPE_WIDE_STRING: -#endif + #endif *fbp++ = 'l'; break; case TYPE_LONGDOUBLE: @@ -4988,47 +5975,74 @@ #define ENSURE_ALLOCATION(needed) \ #endif *fbp = dp->conversion; #if USE_SNPRINTF -# if ((HAVE_SNPRINTF_RETVAL_C99 && HAVE_SNPRINTF_TRUNCATION_C99) \ + /* Decide whether to pass %n in the format string + to SNPRINTF. */ +# if (((!WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR) \ + && (HAVE_SNPRINTF_RETVAL_C99 && HAVE_SNPRINTF_TRUNCATION_C99)) \ || ((__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) \ && !defined __UCLIBC__) \ || (defined __APPLE__ && defined __MACH__) \ + || defined __OpenBSD__ \ || defined __ANDROID__ \ - || (defined _WIN32 && ! defined __CYGWIN__)) - /* On systems where we know that snprintf's return value - conforms to ISO C 99 (HAVE_SNPRINTF_RETVAL_C99) and that - snprintf always produces NUL-terminated strings - (HAVE_SNPRINTF_TRUNCATION_C99), it is possible to avoid - using %n. And it is desirable to do so, because more and - more platforms no longer support %n, for "security reasons". - In particular, the following platforms: + || (defined _WIN32 && ! defined __CYGWIN__)) \ + || (WIDE_CHAR_VERSION && MUSL_LIBC) + /* We can avoid passing %n and instead rely on SNPRINTF's + return value if + - !WIDE_CHAR_VERSION || !DCHAR_IS_TCHAR, because otherwise, + when WIDE_CHAR_VERSION && DCHAR_IS_TCHAR, + snwprintf()/_snwprintf() (Windows) and swprintf() (Unix) + don't return the needed buffer size, + and + - we're compiling for a system where we know + - that snprintf's return value conforms to ISO C 99 + (HAVE_SNPRINTF_RETVAL_C99) and + - that snprintf always produces NUL-terminated strings + (HAVE_SNPRINTF_TRUNCATION_C99). + And it is desirable to do so, because more and more platforms + no longer support %n, for "security reasons". */ + /* On specific platforms, listed below, we *must* avoid %n. + In the case + !WIDE_CHAR_VERSION && HAVE_SNPRINTF_RETVAL_C99 && !USE_MSVC__SNPRINTF + we can rely on the return value of snprintf instead. Whereas + in the opposite case + WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF + we need to make room based on an estimation, computed by + MAX_ROOM_NEEDED. */ + /* The following platforms forbid %n: - On glibc2 systems from 2004-10-18 or newer, the use of %n in format strings in writable memory may crash the program (if compiled with _FORTIFY_SOURCE=2). - - On Mac OS X 10.13 or newer, the use of %n in format + - On macOS 10.13 or newer, the use of %n in format strings in writable memory by default crashes the program. + - On OpenBSD, since 2021-08-30, the use of %n in format + strings produces an abort (see + , + ). - On Android, starting on 2018-03-07, the use of %n in format strings produces a fatal error (see ). - On these platforms, HAVE_SNPRINTF_RETVAL_C99 and - HAVE_SNPRINTF_TRUNCATION_C99 are 1. We have listed them - explicitly in the condition above, in case of cross- - compilation (just to be sure). */ - /* On native Windows systems (such as mingw), we can avoid using - %n because: + - On native Windows systems (such as mingw) where the OS is + Windows Vista, the use of %n in format strings by default + crashes the program. See + and + + On the first four of these platforms, if !WIDE_CHAR_VERSION, + it is not a big deal to avoid %n, because on these platforms, + HAVE_SNPRINTF_RETVAL_C99 and HAVE_SNPRINTF_TRUNCATION_C99 are + 1. + On native Windows, if !WIDE_CHAR_VERSION, it's not a big deal + either because: - Although the gl_SNPRINTF_TRUNCATION_C99 test fails, snprintf does not write more than the specified number of bytes. (snprintf (buf, 3, "%d %d", 4567, 89) writes '4', '5', '6' into buf, not '4', '5', '\0'.) - Although the gl_SNPRINTF_RETVAL_C99 test fails, snprintf allows us to recognize the case of an insufficient - buffer size: it returns -1 in this case. - On native Windows systems (such as mingw) where the OS is - Windows Vista, the use of %n in format strings by default - crashes the program. See - and - - So we should avoid %n in this situation. */ + buffer size: it returns -1 in this case. */ + /* Additionally, in the WIDE_CHAR_VERSION case, we cannot use %n + on musl libc because we would run into an swprintf() bug. + See . */ fbp[1] = '\0'; # else /* AIX <= 5.1, HP-UX, IRIX, OSF/1, Solaris <= 9, BeOS */ fbp[1] = '%'; @@ -5189,6 +6203,102 @@ #define ENSURE_ALLOCATION(needed) \ SNPRINTF_BUF (arg); } break; + case TYPE_INT8_T: + { + int8_t arg = a.arg[dp->arg_index].a.a_int8_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UINT8_T: + { + uint8_t arg = a.arg[dp->arg_index].a.a_uint8_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_INT16_T: + { + int16_t arg = a.arg[dp->arg_index].a.a_int16_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UINT16_T: + { + uint16_t arg = a.arg[dp->arg_index].a.a_uint16_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_INT32_T: + { + int32_t arg = a.arg[dp->arg_index].a.a_int32_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UINT32_T: + { + uint32_t arg = a.arg[dp->arg_index].a.a_uint32_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_INT64_T: + { + int64_t arg = a.arg[dp->arg_index].a.a_int64_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UINT64_T: + { + uint64_t arg = a.arg[dp->arg_index].a.a_uint64_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_INT_FAST8_T: + { + int_fast8_t arg = a.arg[dp->arg_index].a.a_int_fast8_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UINT_FAST8_T: + { + uint_fast8_t arg = a.arg[dp->arg_index].a.a_uint_fast8_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_INT_FAST16_T: + { + int_fast16_t arg = a.arg[dp->arg_index].a.a_int_fast16_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UINT_FAST16_T: + { + uint_fast16_t arg = a.arg[dp->arg_index].a.a_uint_fast16_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_INT_FAST32_T: + { + int_fast32_t arg = a.arg[dp->arg_index].a.a_int_fast32_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UINT_FAST32_T: + { + uint_fast32_t arg = a.arg[dp->arg_index].a.a_uint_fast32_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_INT_FAST64_T: + { + int_fast64_t arg = a.arg[dp->arg_index].a.a_int_fast64_t; + SNPRINTF_BUF (arg); + } + break; + case TYPE_UINT_FAST64_T: + { + uint_fast64_t arg = a.arg[dp->arg_index].a.a_uint_fast64_t; + SNPRINTF_BUF (arg); + } + break; case TYPE_DOUBLE: { double arg = a.arg[dp->arg_index].a.a_double; @@ -5271,12 +6381,16 @@ #define ENSURE_ALLOCATION(needed) \ /* Look at the snprintf() return value. */ if (retcount < 0) { -# if !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF +# if (WIDE_CHAR_VERSION && DCHAR_IS_TCHAR) || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF /* HP-UX 10.20 snprintf() is doubly deficient: It doesn't understand the '%n' directive, *and* it returns -1 (rather than the length that would have been required) when the buffer is too small. + Likewise, in case of + WIDE_CHAR_VERSION && DCHAR_IS_TCHAR, the + functions snwprintf()/_snwprintf() (Windows) + or swprintf() (Unix). But a failure at this point can also come from other reasons than a too small buffer, such as an invalid wide string argument to @@ -5312,7 +6426,15 @@ #define ENSURE_ALLOCATION(needed) \ # endif } else - count = retcount; + { + count = retcount; +# if WIDE_CHAR_VERSION && defined __MINGW32__ + if (count == 0 && dp->conversion == 'c') + /* snwprintf returned 0 instead of 1. But it + wrote a null wide character. */ + count = 1; +# endif + } } } #endif @@ -5442,11 +6564,13 @@ #define ENSURE_ALLOCATION(needed) \ #if !DCHAR_IS_TCHAR /* Convert from TCHAR_T[] to DCHAR_T[]. */ - if (dp->conversion == 'c' || dp->conversion == 's') + if (dp->conversion == 'c' || dp->conversion == 's' +# if __GLIBC__ >= 2 && !defined __UCLIBC__ + || (flags & FLAG_LOCALIZED) +# endif + ) { - /* type = TYPE_CHAR or TYPE_WIDE_CHAR or TYPE_STRING - TYPE_WIDE_STRING. - The result string is not certainly ASCII. */ + /* The result string is not guaranteed to be ASCII. */ const TCHAR_T *tmpsrc; DCHAR_T *tmpdst; size_t tmpdst_len; @@ -5457,6 +6581,56 @@ #define ENSURE_ALLOCATION(needed) \ # else tmpsrc = tmp; # endif +# if WIDE_CHAR_VERSION + /* Convert tmpsrc[0..count-1] to a freshly allocated + wide character array. */ + mbstate_t state; + + memset (&state, '\0', sizeof (mbstate_t)); + tmpdst_len = 0; + { + const TCHAR_T *src = tmpsrc; + size_t srclen = count; + + for (; srclen > 0; tmpdst_len++) + { + /* Parse the next multibyte character. */ + size_t ret = mbrtowc (NULL, src, srclen, &state); + if (ret == (size_t)(-2) || ret == (size_t)(-1)) + goto fail_with_EILSEQ; + if (ret == 0) + ret = 1; + src += ret; + srclen -= ret; + } + } + + tmpdst = + (wchar_t *) malloc ((tmpdst_len + 1) * sizeof (wchar_t)); + if (tmpdst == NULL) + goto out_of_memory; + + memset (&state, '\0', sizeof (mbstate_t)); + { + DCHAR_T *destptr = tmpdst; + const TCHAR_T *src = tmpsrc; + size_t srclen = count; + + for (; srclen > 0; destptr++) + { + /* Parse the next multibyte character. */ + size_t ret = mbrtowc (destptr, src, srclen, &state); + if (ret == (size_t)(-2) || ret == (size_t)(-1)) + /* Should already have been caught in the first + loop, above. */ + abort (); + if (ret == 0) + ret = 1; + src += ret; + srclen -= ret; + } + } +# else tmpdst = DCHAR_CONV_FROM_ENCODING (locale_charset (), iconveh_question_mark, @@ -5465,6 +6639,7 @@ #define ENSURE_ALLOCATION(needed) \ NULL, &tmpdst_len); if (tmpdst == NULL) goto fail_with_errno; +# endif ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len), { free (tmpdst); goto out_of_memory; }); DCHAR_CPY (result + length, tmpdst, tmpdst_len); @@ -5531,7 +6706,7 @@ #define ENSURE_ALLOCATION(needed) \ /* Here count <= allocated - length. */ /* Perform padding. */ -#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION +#if (WIDE_CHAR_VERSION && MUSL_LIBC) || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION if (pad_ourselves && has_width) { size_t w; @@ -5590,6 +6765,22 @@ #define ENSURE_ALLOCATION(needed) \ if ((*pad_ptr >= 'A' && *pad_ptr <= 'Z') || (*pad_ptr >= 'a' && *pad_ptr <= 'z')) pad_ptr = NULL; + else + /* Do the zero-padding after the "0x" or + "0b" prefix, not before. */ + if (p - rp >= 2 + && *rp == '0' + && (((dp->conversion == 'a' + || dp->conversion == 'x') + && rp[1] == 'x') + || ((dp->conversion == 'A' + || dp->conversion == 'X') + && rp[1] == 'X') + || (dp->conversion == 'b' + && rp[1] == 'b') + || (dp->conversion == 'B' + && rp[1] == 'B'))) + pad_ptr += 2; } /* The generated string now extends from rp to p, with the zero padding insertion point being at @@ -5603,7 +6794,22 @@ #define ENSURE_ALLOCATION(needed) \ for (; pad > 0; pad--) *p++ = ' '; } - else if ((flags & FLAG_ZERO) && pad_ptr != NULL) + else if ((flags & FLAG_ZERO) && pad_ptr != NULL + /* ISO C says: "For d, i, o, u, x, and X + conversions, if a precision is + specified, the 0 flag is ignored. */ + && !(has_precision + && (dp->conversion == 'd' + || dp->conversion == 'i' + || dp->conversion == 'o' + || dp->conversion == 'u' + || dp->conversion == 'x' + || dp->conversion == 'X' + /* Although ISO C does not + require it, treat 'b' and 'B' + like 'x' and 'X'. */ + || dp->conversion == 'b' + || dp->conversion == 'B'))) { /* Pad with zeroes. */ DCHAR_T *q = end; @@ -5697,7 +6903,7 @@ #define ENSURE_ALLOCATION(needed) \ errno = ENOMEM; goto fail_with_errno; -#if ENABLE_UNISTDIO || ((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (NEED_PRINTF_DIRECTIVE_LS && !defined IN_LIBINTL) || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) +#if ENABLE_UNISTDIO || ((!USE_SNPRINTF || WIDE_CHAR_VERSION || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_DIRECTIVE_LS || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) || ((NEED_PRINTF_DIRECTIVE_LC || ENABLE_WCHAR_FALLBACK) && HAVE_WINT_T && !WIDE_CHAR_VERSION) || (NEED_WPRINTF_DIRECTIVE_C && WIDE_CHAR_VERSION) fail_with_EILSEQ: errno = EILSEQ; goto fail_with_errno; diff --git a/lib/vasnprintf.h b/lib/vasnprintf.h index f69649fb457..2d134070796 100644 --- a/lib/vasnprintf.h +++ b/lib/vasnprintf.h @@ -17,6 +17,11 @@ #ifndef _VASNPRINTF_H #define _VASNPRINTF_H +/* This file uses _GL_ATTRIBUTE_FORMAT. */ +#if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." +#endif + /* Get va_list. */ #include diff --git a/lib/xsize.h b/lib/xsize.h index 1ec78e776fc..5b08d61f2f7 100644 --- a/lib/xsize.h +++ b/lib/xsize.h @@ -18,6 +18,11 @@ #ifndef _XSIZE_H #define _XSIZE_H +/* This file uses _GL_INLINE_HEADER_BEGIN, _GL_INLINE, HAVE_STDINT_H. */ +#if !_GL_CONFIG_H_INCLUDED + #error "Please include config.h first." +#endif + /* Get size_t. */ #include @@ -30,9 +35,6 @@ #define _XSIZE_H /* Get ATTRIBUTE_PURE. */ #include "attribute.h" -#ifndef _GL_INLINE_HEADER_BEGIN - #error "Please include config.h first." -#endif _GL_INLINE_HEADER_BEGIN #ifndef XSIZE_INLINE # define XSIZE_INLINE _GL_INLINE diff --git a/m4/exponentd.m4 b/m4/exponentd.m4 index 2ef46437deb..163114b89ec 100644 --- a/m4/exponentd.m4 +++ b/m4/exponentd.m4 @@ -1,9 +1,9 @@ -# exponentd.m4 serial 3 +# exponentd.m4 serial 4 dnl Copyright (C) 2007-2008, 2010-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. -AC_DEFUN([gl_DOUBLE_EXPONENT_LOCATION], +AC_DEFUN_ONCE([gl_DOUBLE_EXPONENT_LOCATION], [ AC_CACHE_CHECK([where to find the exponent in a 'double'], [gl_cv_cc_double_expbit0], diff --git a/m4/exponentf.m4 b/m4/exponentf.m4 index f6395f23f27..e761883939b 100644 --- a/m4/exponentf.m4 +++ b/m4/exponentf.m4 @@ -1,9 +1,9 @@ -# exponentf.m4 serial 2 +# exponentf.m4 serial 3 dnl Copyright (C) 2007-2008, 2010-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. -AC_DEFUN([gl_FLOAT_EXPONENT_LOCATION], +AC_DEFUN_ONCE([gl_FLOAT_EXPONENT_LOCATION], [ AC_CACHE_CHECK([where to find the exponent in a 'float'], [gl_cv_cc_float_expbit0], diff --git a/m4/exponentl.m4 b/m4/exponentl.m4 index e1fa5c48cc7..bc5638737e5 100644 --- a/m4/exponentl.m4 +++ b/m4/exponentl.m4 @@ -1,9 +1,9 @@ -# exponentl.m4 serial 5 +# exponentl.m4 serial 6 dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. -AC_DEFUN([gl_LONG_DOUBLE_EXPONENT_LOCATION], +AC_DEFUN_ONCE([gl_LONG_DOUBLE_EXPONENT_LOCATION], [ AC_REQUIRE([gl_BIGENDIAN]) AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 9f701e95211..4c1e41daf51 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -232,12 +232,9 @@ AC_DEFUN # Code from module vla: # Code from module warnings: # Code from module xalloc-oversized: -<<<<<<< HEAD # Code from module xsize: -======= # Code from module year2038: AC_REQUIRE([AC_SYS_YEAR2038]) ->>>>>>> origin/master ]) # This macro should be invoked from ./configure.ac, in the section @@ -803,7 +800,7 @@ AC_DEFUN } func_gl_gnulib_m4code_fseterr () { - if ! $gl_gnulib_enabled_fseterr; then + if $gl_gnulib_enabled_fseterr; then :; else gl_FUNC_FSETERR gl_CONDITIONAL([GL_COND_OBJ_FSETERR], [test $ac_cv_func___fseterr = no]) gl_gnulib_enabled_fseterr=true @@ -811,7 +808,7 @@ AC_DEFUN } func_gl_gnulib_m4code_getdelim () { - if ! $gl_gnulib_enabled_getdelim; then + if $gl_gnulib_enabled_getdelim; then :; else gl_FUNC_GETDELIM gl_CONDITIONAL([GL_COND_OBJ_GETDELIM], [test $HAVE_GETDELIM = 0 || test $REPLACE_GETDELIM = 1]) @@ -892,7 +889,7 @@ AC_DEFUN } func_gl_gnulib_m4code_3f0e593033d1fc2c127581960f641b66 () { - if ! $gl_gnulib_enabled_3f0e593033d1fc2c127581960f641b66; then + if $gl_gnulib_enabled_3f0e593033d1fc2c127581960f641b66; then :; else gl_FUNC_ISNANF_NO_LIBM if test $gl_func_isnanf_no_libm != yes; then AC_LIBOBJ([isnanf]) @@ -1029,7 +1026,7 @@ AC_DEFUN } func_gl_gnulib_m4code_size_max () { - if ! $gl_gnulib_enabled_size_max; then + if $gl_gnulib_enabled_size_max; then :; else gl_SIZE_MAX gl_gnulib_enabled_size_max=true fi @@ -1056,7 +1053,7 @@ AC_DEFUN } func_gl_gnulib_m4code_vasnprintf () { - if ! $gl_gnulib_enabled_vasnprintf; then + if $gl_gnulib_enabled_vasnprintf; then :; else AC_REQUIRE([AC_C_RESTRICT]) gl_FUNC_VASNPRINTF gl_gnulib_enabled_vasnprintf=true @@ -1065,7 +1062,7 @@ AC_DEFUN } func_gl_gnulib_m4code_ed5616be3593d355b981ffab56b9f37b () { - if ! $gl_gnulib_enabled_ed5616be3593d355b981ffab56b9f37b; then + if $gl_gnulib_enabled_ed5616be3593d355b981ffab56b9f37b; then :; else gl_FUNC_VFPRINTF_POSIX gl_STDIO_MODULE_INDICATOR([vfprintf-posix]) gl_MODULE_INDICATOR([vfprintf-posix]) @@ -1086,7 +1083,7 @@ AC_DEFUN } func_gl_gnulib_m4code_xsize () { - if ! $gl_gnulib_enabled_xsize; then + if $gl_gnulib_enabled_xsize; then :; else gl_XSIZE gl_gnulib_enabled_xsize=true func_gl_gnulib_m4code_size_max @@ -1749,7 +1746,7 @@ AC_DEFUN m4/pipe2.m4 m4/printf-frexp.m4 m4/printf-frexpl.m4 - m4/printf-posix-rpl.m4 + m4/printf-posix.m4 m4/printf.m4 m4/pselect.m4 m4/pthread_sigmask.m4 diff --git a/m4/printf-posix.m4 b/m4/printf-posix.m4 new file mode 100644 index 00000000000..9aebf4002d6 --- /dev/null +++ b/m4/printf-posix.m4 @@ -0,0 +1,36 @@ +# printf-posix.m4 serial 5 +dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_PRINTF_POSIX], +[ + AC_REQUIRE([gl_FUNC_PRINTF_IS_POSIX]) + if test $gl_cv_func_printf_posix = no; then + gl_PREREQ_VASNPRINTF_WITH_POSIX_EXTRAS + gl_REPLACE_VASNPRINTF + gl_REPLACE_PRINTF + fi +]) + +dnl Test whether printf is POSIX compliant. +dnl Result is gl_cv_func_printf_posix. +AC_DEFUN([gl_FUNC_PRINTF_IS_POSIX], +[ + AC_REQUIRE([gl_FUNC_VFPRINTF_IS_POSIX]) + gl_cv_func_printf_posix="$gl_cv_func_vfprintf_posix" +]) + +AC_DEFUN([gl_REPLACE_PRINTF], +[ + AC_REQUIRE([gl_STDIO_H_DEFAULTS]) + AC_REQUIRE([gl_ASM_SYMBOL_PREFIX]) + AC_LIBOBJ([printf]) + REPLACE_PRINTF=1 + AC_DEFINE([REPLACE_PRINTF_POSIX], [1], + [Define if printf is overridden by a POSIX compliant gnulib implementation.]) + gl_PREREQ_PRINTF +]) + +AC_DEFUN([gl_PREREQ_PRINTF], [:]) diff --git a/m4/printf.m4 b/m4/printf.m4 index de98a870e98..efb85a57afd 100644 --- a/m4/printf.m4 +++ b/m4/printf.m4 @@ -1,4 +1,4 @@ -# printf.m4 serial 74 +# printf.m4 serial 82 dnl Copyright (C) 2003, 2007-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -101,6 +101,92 @@ AC_DEFUN ]) ]) +dnl Test whether the *printf family of functions supports the 'w8', 'w16', +dnl 'w32', 'w64', 'wf8', 'wf16', 'wf32', 'wf64' size specifiers. (ISO C23) +dnl Result is gl_cv_func_printf_sizes_c23. + +AC_DEFUN([gl_PRINTF_SIZES_C23], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([gl_AC_HEADER_STDINT_H]) + AC_REQUIRE([gl_AC_HEADER_INTTYPES_H]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_CACHE_CHECK([whether printf supports size specifiers as in C23], + [gl_cv_func_printf_sizes_c23], + [ + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include +#include +#include +#if HAVE_STDINT_H_WITH_UINTMAX +# include +#endif +#if HAVE_INTTYPES_H_WITH_UINTMAX +# include +#endif +static char buf[100]; +int main () +{ + int result = 0; + buf[0] = '\0'; + if (sprintf (buf, "%w8u %d", (uint8_t) 123, 33, 44, 55) < 0 + || strcmp (buf, "123 33") != 0) + result |= 1; + buf[0] = '\0'; + if (sprintf (buf, "%wf8u %d", (uint_fast8_t) 123, 33, 44, 55) < 0 + || strcmp (buf, "123 33") != 0) + result |= 1; + buf[0] = '\0'; + if (sprintf (buf, "%w16u %d", (uint16_t) 12345, 33, 44, 55) < 0 + || strcmp (buf, "12345 33") != 0) + result |= 2; + buf[0] = '\0'; + if (sprintf (buf, "%wf16u %d", (uint_fast16_t) 12345, 33, 44, 55) < 0 + || strcmp (buf, "12345 33") != 0) + result |= 2; + buf[0] = '\0'; + if (sprintf (buf, "%w32u %d", (uint32_t) 12345671, 33, 44, 55) < 0 + || strcmp (buf, "12345671 33") != 0) + result |= 4; + buf[0] = '\0'; + if (sprintf (buf, "%wf32u %d", (uint_fast32_t) 12345671, 33, 44, 55) < 0 + || strcmp (buf, "12345671 33") != 0) + result |= 4; +#if HAVE_STDINT_H_WITH_UINTMAX || HAVE_INTTYPES_H_WITH_UINTMAX + buf[0] = '\0'; + if (sprintf (buf, "%w64u %d", (uint64_t) 12345671, 33, 44, 55) < 0 + || strcmp (buf, "12345671 33") != 0) + result |= 8; + buf[0] = '\0'; + if (sprintf (buf, "%wf64u %d", (uint_fast64_t) 12345671, 33, 44, 55) < 0 + || strcmp (buf, "12345671 33") != 0) + result |= 8; +#else + result |= 8; +#endif + return result; +}]])], + [gl_cv_func_printf_sizes_c23=yes], + [gl_cv_func_printf_sizes_c23=no], + [ + case "$host_os" in + # Guess no on glibc systems. + *-gnu* | gnu*) gl_cv_func_printf_sizes_c23="guessing no";; + # Guess no on musl systems. + *-musl* | midipix*) gl_cv_func_printf_sizes_c23="guessing no";; + # Guess no on Android. + linux*-android*) gl_cv_func_printf_sizes_c23="guessing no";; + # Guess no on native Windows. + mingw*) gl_cv_func_printf_sizes_c23="guessing no";; + # If we don't know, obey --enable-cross-guesses. + *) gl_cv_func_printf_sizes_c23="$gl_cross_guess_normal";; + esac + ]) + ]) +]) + dnl Test whether the *printf family of functions supports 'long double' dnl arguments together with the 'L' size specifier. (ISO C99, POSIX:2001) dnl Result is gl_cv_func_printf_long_double. @@ -603,6 +689,116 @@ AC_DEFUN ]) ]) +dnl Test whether the *printf family of functions supports the 'b' conversion +dnl specifier for binary output of integers. +dnl (ISO C23) +dnl Result is gl_cv_func_printf_directive_b. + +AC_DEFUN([gl_PRINTF_DIRECTIVE_B], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_CACHE_CHECK([whether printf supports the 'b' directive], + [gl_cv_func_printf_directive_b], + [ + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include +static char buf[100]; +int main () +{ + int result = 0; + if (sprintf (buf, "%b %d", 12345, 33, 44, 55) < 0 + || strcmp (buf, "11000000111001 33") != 0) + result |= 1; + return result; +}]])], + [gl_cv_func_printf_directive_b=yes], + [gl_cv_func_printf_directive_b=no], + [ + case "$host_os" in + # Guess yes on glibc >= 2.35 systems. + *-gnu* | gnu*) + AC_EGREP_CPP([Lucky], [ + #include + #ifdef __GNU_LIBRARY__ + #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 35) || (__GLIBC__ > 2) + Lucky user + #endif + #endif + ], + [gl_cv_func_printf_directive_uppercase_b="guessing yes"], + [gl_cv_func_printf_directive_uppercase_b="guessing no"]) + ;; + # Guess no on musl systems. + *-musl* | midipix*) gl_cv_func_printf_directive_b="guessing no";; + # Guess no on Android. + linux*-android*) gl_cv_func_printf_directive_b="guessing no";; + # Guess no on native Windows. + mingw*) gl_cv_func_printf_directive_b="guessing no";; + # If we don't know, obey --enable-cross-guesses. + *) gl_cv_func_printf_directive_b="$gl_cross_guess_normal";; + esac + ]) + ]) +]) + +dnl Test whether the *printf family of functions supports the 'B' conversion +dnl specifier for binary output of integers. +dnl (GNU, encouraged by ISO C23 § 7.23.6.1) +dnl Result is gl_cv_func_printf_directive_uppercase_b. + +AC_DEFUN([gl_PRINTF_DIRECTIVE_UPPERCASE_B], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_CACHE_CHECK([whether printf supports the 'B' directive], + [gl_cv_func_printf_directive_uppercase_b], + [ + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include +static char buf[100]; +int main () +{ + int result = 0; + if (sprintf (buf, "%#B %d", 12345, 33, 44, 55) < 0 + || strcmp (buf, "0B11000000111001 33") != 0) + result |= 1; + return result; +}]])], + [gl_cv_func_printf_directive_uppercase_b=yes], + [gl_cv_func_printf_directive_uppercase_b=no], + [ + case "$host_os" in + # Guess yes on glibc >= 2.35 systems. + *-gnu* | gnu*) + AC_EGREP_CPP([Lucky], [ + #include + #ifdef __GNU_LIBRARY__ + #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 35) || (__GLIBC__ > 2) + Lucky user + #endif + #endif + ], + [gl_cv_func_printf_directive_uppercase_b="guessing yes"], + [gl_cv_func_printf_directive_uppercase_b="guessing no"]) + ;; + # Guess no on musl systems. + *-musl* | midipix*) gl_cv_func_printf_directive_uppercase_b="guessing no";; + # Guess no on Android. + linux*-android*) gl_cv_func_printf_directive_uppercase_b="guessing no";; + # Guess no on native Windows. + mingw*) gl_cv_func_printf_directive_uppercase_b="guessing no";; + # If we don't know, obey --enable-cross-guesses. + *) gl_cv_func_printf_directive_uppercase_b="$gl_cross_guess_normal";; + esac + ]) + ]) +]) + dnl Test whether the *printf family of functions supports the %F format dnl directive. (ISO C99, POSIX:2001) dnl Result is gl_cv_func_printf_directive_f. @@ -829,11 +1025,58 @@ AC_DEFUN ]) ]) +dnl Test whether the *printf family of functions supports the %lc format +dnl directive and in particular, when the argument is a null wide character, +dnl whether the functions don't produce a NUL byte. +dnl Result is gl_cv_func_printf_directive_lc. + +AC_DEFUN([gl_PRINTF_DIRECTIVE_LC], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_CACHE_CHECK([whether printf supports the 'lc' directive correctly], + [gl_cv_func_printf_directive_lc], + [ + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include +#include +int main () +{ + int result = 0; + char buf[100]; + /* This test fails on glibc 2.35, FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, + macOS 12.5, AIX 7.2, Solaris 11.4. + glibc 2.35 bug: */ + { + buf[0] = '\0'; + if (sprintf (buf, "%lc%lc%lc", (wint_t) 'a', (wint_t) 0, (wint_t) 'z') < 0 + || strcmp (buf, "az") != 0) + result |= 1; + } + return result; +}]])], + [gl_cv_func_printf_directive_lc=yes], + [gl_cv_func_printf_directive_lc=no], + [ +changequote(,)dnl + case "$host_os" in + # Guess yes on musl libc. + *-musl* | midipix*) gl_cv_func_printf_directive_lc="guessing yes";; + # Guess no otherwise. + *) gl_cv_func_printf_directive_lc="guessing no";; + esac +changequote([,])dnl + ]) + ]) +]) + dnl Test whether the *printf family of functions supports POSIX/XSI format dnl strings with positions. (POSIX:2001) dnl Result is gl_cv_func_printf_positions. -AC_DEFUN([gl_PRINTF_POSITIONS], +AC_DEFUN_ONCE([gl_PRINTF_POSITIONS], [ AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles @@ -1641,88 +1884,227 @@ AC_DEFUN ]) ]) +dnl Test whether the swprintf function works correctly when it produces output +dnl that contains null wide characters. +dnl Result is gl_cv_func_swprintf_works. + +AC_DEFUN([gl_SWPRINTF_WORKS], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_CHECK_FUNCS_ONCE([swprintf]) + AC_CACHE_CHECK([whether swprintf works], + [gl_cv_func_swprintf_works], + [ + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#ifndef __USE_MINGW_ANSI_STDIO +# define __USE_MINGW_ANSI_STDIO 1 +#endif +#include +#include +int main() +{ + int result = 0; + { /* This test fails on musl, FreeBSD, NetBSD, OpenBSD, macOS, AIX. */ + wchar_t buf[5] = { 0xBEEF, 0xBEEF, 0xBEEF, 0xBEEF, 0xBEEF }; + int ret = swprintf (buf, 4, L"%cz", '\0'); + /* Expected result: + ret = 2, buf[0] = 0x0, buf[1] = 0x7a, buf[2] = 0x0, buf[3] = 0xbeef + musl libc 1.2.3: + ret = 2, buf[0] = 0x0, buf[1] = 0x0, buf[2] = 0x0, buf[3] = 0x0 + Reported at . + FreeBSD 13.1, NetBSD 9.0, OpenBSD 7.2, macOS 12.5, AIX 7.2: + ret = 2, buf[0] = 0x0, buf[1] = 0xbeef, buf[2] = 0xbeef, buf[3] = 0xbeef + */ + if (ret < 0 || buf[1] != 'z') + result |= 1; + } + { /* This test fails on mingw. */ + wchar_t buf[2]; + int ret = swprintf (buf, 2, L"%lc", (wint_t)0); + /* Expected: ret = 1 + mingw: ret = 0 + */ + if (ret != 1) + result |= 2; + } + return result; +}]])], + [gl_cv_func_swprintf_works=yes], + [gl_cv_func_swprintf_works=no], + [case "$host_os" in + # Guess yes on glibc systems. + *-gnu* | gnu*) gl_cv_func_swprintf_works="guessing yes";; + # Guess no on musl systems. + *-musl* | midipix*) gl_cv_func_swprintf_works="guessing yes";; + # Guess no on FreeBSD, NetBSD, OpenBSD, macOS, AIX. + freebsd* | midnightbsd* | netbsd* | openbsd* | darwin* | aix*) + gl_cv_func_swprintf_works="guessing no";; + # Guess no on native Windows. + mingw* | pw*) gl_cv_func_swprintf_works="guessing no";; + # If we don't know, obey --enable-cross-guesses. + *) gl_cv_func_swprintf_works="$gl_cross_guess_normal";; + esac + ]) + ]) +]) + +dnl Test whether the *wprintf family of functions supports the 'a' and 'A' +dnl conversion specifier for hexadecimal output of 'long double' numbers. +dnl (ISO C99, POSIX:2001) +dnl Result is gl_cv_func_swprintf_directive_la. + +AC_DEFUN([gl_SWPRINTF_DIRECTIVE_LA], +[ + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_CACHE_CHECK([whether swprintf supports the 'La' and 'LA' directives], + [gl_cv_func_swprintf_directive_la], + [ + AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ +#include +#include +static wchar_t buf[100]; +int main () +{ + int result = 0; + /* This catches a glibc 2.15 and Haiku 2022 bug. */ + if (swprintf (buf, sizeof (buf) / sizeof (wchar_t), + L"%La %d", 3.1416015625L, 33, 44, 55) < 0 + || (wcscmp (buf, L"0x1.922p+1 33") != 0 + && wcscmp (buf, L"0x3.244p+0 33") != 0 + && wcscmp (buf, L"0x6.488p-1 33") != 0 + && wcscmp (buf, L"0xc.91p-2 33") != 0)) + result |= 1; + return result; +}]])], + [gl_cv_func_swprintf_directive_la=yes], + [gl_cv_func_swprintf_directive_la=no], + [case "$host_os" in + # Guess yes on glibc >= 2.17 systems. + *-gnu* | gnu*) + AC_EGREP_CPP([Unlucky], [ + #include + #ifdef __GNU_LIBRARY__ + #if ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 16) || (__GLIBC__ > 2)) && !defined __UCLIBC__ + Unlucky + #endif + #endif + ], + [gl_cv_func_swprintf_directive_la="guessing yes"], + [gl_cv_func_swprintf_directive_la="guessing no"]) + ;; + # Guess yes on musl systems. + *-musl* | midipix*) gl_cv_func_swprintf_directive_la="guessing yes";; + # Guess yes on Android. + linux*-android*) gl_cv_func_swprintf_directive_la="guessing no";; + # Guess yes on native Windows. + mingw*) gl_cv_func_swprintf_directive_la="guessing no";; + # If we don't know, obey --enable-cross-guesses. + *) gl_cv_func_swprintf_directive_la="$gl_cross_guess_normal";; + esac + ]) + ]) +]) + dnl The results of these tests on various platforms are: dnl dnl 1 = gl_PRINTF_SIZES_C99 -dnl 2 = gl_PRINTF_LONG_DOUBLE -dnl 3 = gl_PRINTF_INFINITE -dnl 4 = gl_PRINTF_INFINITE_LONG_DOUBLE -dnl 5 = gl_PRINTF_DIRECTIVE_A -dnl 6 = gl_PRINTF_DIRECTIVE_F -dnl 7 = gl_PRINTF_DIRECTIVE_N -dnl 8 = gl_PRINTF_DIRECTIVE_LS -dnl 9 = gl_PRINTF_POSITIONS -dnl 10 = gl_PRINTF_FLAG_GROUPING -dnl 11 = gl_PRINTF_FLAG_LEFTADJUST -dnl 12 = gl_PRINTF_FLAG_ZERO -dnl 13 = gl_PRINTF_PRECISION -dnl 14 = gl_PRINTF_ENOMEM -dnl 15 = gl_SNPRINTF_PRESENCE -dnl 16 = gl_SNPRINTF_TRUNCATION_C99 -dnl 17 = gl_SNPRINTF_RETVAL_C99 -dnl 18 = gl_SNPRINTF_DIRECTIVE_N -dnl 19 = gl_SNPRINTF_SIZE1 -dnl 20 = gl_VSNPRINTF_ZEROSIZE_C99 +dnl 2 = gl_PRINTF_SIZES_C23 +dnl 3 = gl_PRINTF_LONG_DOUBLE +dnl 4 = gl_PRINTF_INFINITE +dnl 5 = gl_PRINTF_INFINITE_LONG_DOUBLE +dnl 6 = gl_PRINTF_DIRECTIVE_A +dnl 7 = gl_PRINTF_DIRECTIVE_B +dnl 8 = gl_PRINTF_DIRECTIVE_UPPERCASE_B +dnl 9 = gl_PRINTF_DIRECTIVE_F +dnl 10 = gl_PRINTF_DIRECTIVE_N +dnl 11 = gl_PRINTF_DIRECTIVE_LS +dnl 12 = gl_PRINTF_DIRECTIVE_LC +dnl 13 = gl_PRINTF_POSITIONS +dnl 14 = gl_PRINTF_FLAG_GROUPING +dnl 15 = gl_PRINTF_FLAG_LEFTADJUST +dnl 16 = gl_PRINTF_FLAG_ZERO +dnl 17 = gl_PRINTF_PRECISION +dnl 18 = gl_PRINTF_ENOMEM +dnl 19 = gl_SNPRINTF_PRESENCE +dnl 20 = gl_SNPRINTF_TRUNCATION_C99 +dnl 21 = gl_SNPRINTF_RETVAL_C99 +dnl 22 = gl_SNPRINTF_DIRECTIVE_N +dnl 23 = gl_SNPRINTF_SIZE1 +dnl 24 = gl_VSNPRINTF_ZEROSIZE_C99 +dnl 25 = gl_SWPRINTF_WORKS +dnl 26 = gl_SWPRINTF_DIRECTIVE_LA dnl dnl 1 = checking whether printf supports size specifiers as in C99... -dnl 2 = checking whether printf supports 'long double' arguments... -dnl 3 = checking whether printf supports infinite 'double' arguments... -dnl 4 = checking whether printf supports infinite 'long double' arguments... -dnl 5 = checking whether printf supports the 'a' and 'A' directives... -dnl 6 = checking whether printf supports the 'F' directive... -dnl 7 = checking whether printf supports the 'n' directive... -dnl 8 = checking whether printf supports the 'ls' directive... -dnl 9 = checking whether printf supports POSIX/XSI format strings with positions... -dnl 10 = checking whether printf supports the grouping flag... -dnl 11 = checking whether printf supports the left-adjust flag correctly... -dnl 12 = checking whether printf supports the zero flag correctly... -dnl 13 = checking whether printf supports large precisions... -dnl 14 = checking whether printf survives out-of-memory conditions... -dnl 15 = checking for snprintf... -dnl 16 = checking whether snprintf truncates the result as in C99... -dnl 17 = checking whether snprintf returns a byte count as in C99... -dnl 18 = checking whether snprintf fully supports the 'n' directive... -dnl 19 = checking whether snprintf respects a size of 1... -dnl 20 = checking whether vsnprintf respects a zero size as in C99... +dnl 2 = checking whether printf supports size specifiers as in C23... +dnl 3 = checking whether printf supports 'long double' arguments... +dnl 4 = checking whether printf supports infinite 'double' arguments... +dnl 5 = checking whether printf supports infinite 'long double' arguments... +dnl 6 = checking whether printf supports the 'a' and 'A' directives... +dnl 7 = checking whether printf supports the 'b' directive... +dnl 8 = checking whether printf supports the 'B' directive... +dnl 9 = checking whether printf supports the 'F' directive... +dnl 10 = checking whether printf supports the 'n' directive... +dnl 11 = checking whether printf supports the 'ls' directive... +dnl 12 = checking whether printf supports the 'lc' directive correctly... +dnl 13 = checking whether printf supports POSIX/XSI format strings with positions... +dnl 14 = checking whether printf supports the grouping flag... +dnl 15 = checking whether printf supports the left-adjust flag correctly... +dnl 16 = checking whether printf supports the zero flag correctly... +dnl 17 = checking whether printf supports large precisions... +dnl 18 = checking whether printf survives out-of-memory conditions... +dnl 19 = checking for snprintf... +dnl 20 = checking whether snprintf truncates the result as in C99... +dnl 21 = checking whether snprintf returns a byte count as in C99... +dnl 22 = checking whether snprintf fully supports the 'n' directive... +dnl 23 = checking whether snprintf respects a size of 1... +dnl 24 = checking whether vsnprintf respects a zero size as in C99... +dnl 25 = checking whether swprintf works... +dnl 26 = checking whether swprintf supports the 'La' and 'LA' directives... dnl dnl . = yes, # = no. dnl -dnl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 -dnl glibc 2.5 . . . . . . . . . . . . . . . . . . . . -dnl glibc 2.3.6 . . . . # . . . . . . . . . . . . . . . -dnl FreeBSD 13.0 . . . . # . . . . . . . . # . . . . . . -dnl FreeBSD 5.4, 6.1 . . . . # . . . . . . # . # . . . . . . -dnl Mac OS X 10.13.5 . . . # # . # . . . . . . . . . . # . . -dnl Mac OS X 10.5.8 . . . # # . . . . . . # . . . . . . . . -dnl Mac OS X 10.3.9 . . . . # . . . . . . # . # . . . . . . -dnl OpenBSD 6.0, 6.7 . . . . # . . . . . . . . # . . . . . . -dnl OpenBSD 3.9, 4.0 . . # # # # . # . # . # . # . . . . . . -dnl Cygwin 1.7.0 (2009) . . . # . . . ? . . . . . ? . . . . . . -dnl Cygwin 1.5.25 (2008) . . . # # . . # . . . . . # . . . . . . -dnl Cygwin 1.5.19 (2006) # . . # # # . # . # . # # # . . . . . . -dnl Solaris 11.4 . . # # # . . # . . . # . . . . . . . . -dnl Solaris 11.3 . . . . # . . # . . . . . . . . . . . . -dnl Solaris 11.0 . . # # # . . # . . . # . . . . . . . . -dnl Solaris 10 . . # # # . . # . . . # # . . . . . . . -dnl Solaris 2.6 ... 9 # . # # # # . # . . . # # . . . # . . . -dnl Solaris 2.5.1 # . # # # # . # . . . # . . # # # # # # -dnl AIX 7.1 . . # # # . . . . . . # # . . . . . . . -dnl AIX 5.2 . . # # # . . . . . . # . . . . . . . . -dnl AIX 4.3.2, 5.1 # . # # # # . . . . . # . . . . # . . . -dnl HP-UX 11.31 . . . . # . . . . . . # . . . . # # . . -dnl HP-UX 11.{00,11,23} # . . . # # . . . . . # . . . . # # . # -dnl HP-UX 10.20 # . # . # # . ? . . # # . . . . # # ? # -dnl IRIX 6.5 # . # # # # . # . . . # . . . . # . . . -dnl OSF/1 5.1 # . # # # # . . . . . # . . . . # . . # -dnl OSF/1 4.0d # . # # # # . . . . . # . . # # # # # # -dnl NetBSD 9.0 . . . . # . . . . . . . . . . . . . . . -dnl NetBSD 5.0 . . . # # . . . . . . # . # . . . . . . -dnl NetBSD 4.0 . ? ? ? ? ? . ? . ? ? ? ? ? . . . ? ? ? -dnl NetBSD 3.0 . . . . # # . ? # # ? # . # . . . . . . -dnl Haiku . . . # # # . # . . . . . ? . . ? . . . -dnl BeOS # # . # # # . ? # . ? . # ? . . ? . . . -dnl Android 4.3 . . # # # # # # . # . # . # . . . # . . -dnl old mingw / msvcrt # # # # # # . . # # . # # ? . # # # . . -dnl MSVC 9 # # # # # # # . # # . # # ? # # # # . . -dnl mingw 2009-2011 . # . # . . . . # # . . . ? . . . . . . -dnl mingw-w64 2011 # # # # # # . . # # . # # ? . # # # . . +dnl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 +dnl musl libc 1.2.3 . # . . . . # # . . . . . . . . . . . . . . . . # . +dnl glibc 2.35 . # . . . . . . . . . # . . . . . . . . . . . . . . +dnl glibc 2.5 . # . . . . # # . . . # . . . . . . . . . . . . . # +dnl glibc 2.3.6 . # . . . # # # . . . # . . . . . . . . . . . . . # +dnl FreeBSD 13.0 . # . . . # # # . . . # . . . . . # . . . . . . # . +dnl FreeBSD 5.4, 6.1 . # . . . # # # . . . # . . . # . # . . . . . . # ? +dnl Mac OS X 10.13.5 . # . . # # # # . # . # . . . . . . . . . # . . # ? +dnl Mac OS X 10.5.8 . # . . # # # # . . . # . . . # . . . . . . . . # ? +dnl Mac OS X 10.3.9 . # . . . # # # . . . # . . . # . # . . . . . . # ? +dnl OpenBSD 6.0, 6.7 . # . . . # # # . . . # . . . . . # . . . . . . # . +dnl OpenBSD 3.9, 4.0 . # . # # # # # # . # # . # . # . # . . . . . . # ? +dnl Cygwin 1.7.0 (2009) . # . . # . # # . . ? ? . . . . . ? . . . . . . ? ? +dnl Cygwin 1.5.25 (2008) . # . . # # # # . . # ? . . . . . # . . . . . . ? ? +dnl Cygwin 1.5.19 (2006) # # . . # # # # # . # ? . # . # # # . . . . . . ? ? +dnl Solaris 11.4 . # . # # # # # . . # # . . . # . . . . . . . . . ? +dnl Solaris 11.3 . # . . . # # # . . # # . . . . . . . . . . . . . ? +dnl Solaris 11.0 . # . # # # # # . . # # . . . # . . . . . . . . ? ? +dnl Solaris 10 . # . # # # # # . . # # . . . # # . . . . . . . ? ? +dnl Solaris 2.6 ... 9 # # . # # # # # # . # # . . . # # . . . # . . . ? ? +dnl Solaris 2.5.1 # # . # # # # # # . # # . . . # . . # # # # # # ? ? +dnl AIX 7.1 . # . # # # # # . . . # . . . # # . . . . . . . # . +dnl AIX 5.2 . # . # # # # # . . . # . . . # . . . . . . . . # ? +dnl AIX 4.3.2, 5.1 # # . # # # # # # . . # . . . # . . . . # . . . # ? +dnl HP-UX 11.31 . # . . . # # # . . . ? . . . # . . . . # # . . ? ? +dnl HP-UX 11.{00,11,23} # # . . . # # # # . . ? . . . # . . . . # # . # ? ? +dnl HP-UX 10.20 # # . # . # # # # . ? ? . . # # . . . . # # ? # ? ? +dnl IRIX 6.5 # # . # # # # # # . # # . . . # . . . . # . . . # ? +dnl OSF/1 5.1 # # . # # # # # # . . ? . . . # . . . . # . . # ? ? +dnl OSF/1 4.0d # # . # # # # # # . . ? . . . # . . # # # # # # ? ? +dnl NetBSD 9.0 . # . . . # # # . . . # . . . . . . . . . . . . # . +dnl NetBSD 5.0 . # . . # # # # . . . # . . . # . # . . . . . . # ? +dnl NetBSD 4.0 . # ? ? ? ? # # ? . ? # . ? ? ? ? ? . . . ? ? ? # ? +dnl NetBSD 3.0 . # . . . # # # # . ? # # # ? # . # . . . . . . # ? +dnl Haiku . # . . # # # # # . # ? . . . . . ? . . ? . . . . # +dnl BeOS # # # . # # # # # . ? ? # . ? . # ? . . ? . . . ? ? +dnl Android 4.3 . # . # # # # # # # # ? . # . # . # . . . # . . ? ? +dnl old mingw / msvcrt # # # # # # # # # . . ? # # . # # ? . # # # . . # ? +dnl MSVC 9 # # # # # # # # # # . ? # # . # # ? # # # # . . # ? +dnl mingw 2009-2011 . # # . # . # # . . . ? # # . . . ? . . . . . . # ? +dnl mingw-w64 2011 # # # # # # # # # . . ? # # . # # ? . # # # . . # ? diff --git a/m4/vasnprintf.m4 b/m4/vasnprintf.m4 index fda90c402b4..639b29a1f39 100644 --- a/m4/vasnprintf.m4 +++ b/m4/vasnprintf.m4 @@ -1,4 +1,4 @@ -# vasnprintf.m4 serial 39 +# vasnprintf.m4 serial 49 dnl Copyright (C) 2002-2004, 2006-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -29,6 +29,15 @@ AC_DEFUN gl_PREREQ_ASNPRINTF ]) +AC_DEFUN([gl_FUNC_VASNWPRINTF], +[ + AC_LIBOBJ([printf-args]) + gl_PREREQ_PRINTF_ARGS + gl_PREREQ_PRINTF_PARSE + gl_PREREQ_VASNWPRINTF + gl_PREREQ_ASNPRINTF +]) + # Prerequisites of lib/printf-args.h, lib/printf-args.c. AC_DEFUN([gl_PREREQ_PRINTF_ARGS], [ @@ -37,6 +46,7 @@ AC_DEFUN ]) # Prerequisites of lib/printf-parse.h, lib/printf-parse.c. +# Prerequisites of lib/wprintf-parse.h, lib/wprintf-parse.c. AC_DEFUN([gl_PREREQ_PRINTF_PARSE], [ AC_REQUIRE([gl_FEATURES_H]) @@ -50,19 +60,13 @@ AC_DEFUN AC_REQUIRE([gt_AC_TYPE_INTMAX_T]) ]) -# Prerequisites of lib/vasnprintf.c. +# Prerequisites of lib/vasnprintf.c if !WIDE_CHAR_VERSION. AC_DEFUN_ONCE([gl_PREREQ_VASNPRINTF], [ - AC_REQUIRE([AC_FUNC_ALLOCA]) - AC_REQUIRE([gt_TYPE_WCHAR_T]) - AC_REQUIRE([gt_TYPE_WINT_T]) - AC_CHECK_FUNCS([snprintf strnlen wcslen wcsnlen mbrtowc wcrtomb]) + AC_CHECK_FUNCS([snprintf strnlen wcrtomb]) dnl Use the _snprintf function only if it is declared (because on NetBSD it dnl is defined as a weak alias of snprintf; we prefer to use the latter). AC_CHECK_DECLS([_snprintf], , , [[#include ]]) - dnl Knowing DBL_EXPBIT0_WORD and DBL_EXPBIT0_BIT enables an optimization - dnl in the code for NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE. - AC_REQUIRE([gl_DOUBLE_EXPONENT_LOCATION]) dnl We can avoid a lot of code by assuming that snprintf's return value dnl conforms to ISO C99. So check that. AC_REQUIRE([gl_SNPRINTF_RETVAL_C99]) @@ -84,6 +88,55 @@ AC_DEFUN_ONCE terminated.]) ;; esac + gl_PREREQ_VASNXPRINTF +]) + +# Prerequisites of lib/vasnwprintf.c. +AC_DEFUN_ONCE([gl_PREREQ_VASNWPRINTF], +[ + AC_CHECK_FUNCS_ONCE([swprintf wcsnlen mbrtowc]) + AC_CHECK_DECLS([_snwprintf], , , [[#include ]]) + AC_CHECK_DECLS([wcsnlen], , , [[#include ]]) + gl_SWPRINTF_WORKS + case "$gl_cv_func_swprintf_works" in + *yes) + AC_DEFINE([HAVE_WORKING_SWPRINTF], [1], + [Define if the swprintf function works correctly when it produces output + that contains null wide characters.]) + ;; + esac + gl_MBRTOWC_C_LOCALE + case "$gl_cv_func_mbrtowc_C_locale_sans_EILSEQ" in + *yes) ;; + *) + AC_DEFINE([NEED_WPRINTF_DIRECTIVE_C], [1], + [Define if the vasnwprintf implementation needs special code for + the 'c' directive.]) + ;; + esac + gl_SWPRINTF_DIRECTIVE_LA + case "$gl_cv_func_swprintf_directive_la" in + *yes) ;; + *) + AC_DEFINE([NEED_WPRINTF_DIRECTIVE_LA], [1], + [Define if the vasnwprintf implementation needs special code for + the 'a' directive with 'long double' arguments.]) + ;; + esac + gl_MUSL_LIBC + gl_PREREQ_VASNXPRINTF +]) + +# Common prerequisites of lib/vasnprintf.c and lib/vasnwprintf.c. +AC_DEFUN_ONCE([gl_PREREQ_VASNXPRINTF], +[ + AC_REQUIRE([AC_FUNC_ALLOCA]) + AC_REQUIRE([gt_TYPE_WCHAR_T]) + AC_REQUIRE([gt_TYPE_WINT_T]) + AC_CHECK_FUNCS([wcslen]) + dnl Knowing DBL_EXPBIT0_WORD and DBL_EXPBIT0_BIT enables an optimization + dnl in the code for NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE. + AC_REQUIRE([gl_DOUBLE_EXPONENT_LOCATION]) ]) # Extra prerequisites of lib/vasnprintf.c for supporting 'long double' @@ -157,6 +210,21 @@ AC_DEFUN esac ]) +# Extra prerequisites of lib/vasnprintf.c for supporting the 'b' directive. +AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_B], +[ + AC_REQUIRE([gl_PRINTF_DIRECTIVE_B]) + case "$gl_cv_func_printf_directive_b" in + *yes) + ;; + *) + AC_DEFINE([NEED_PRINTF_DIRECTIVE_B], [1], + [Define if the vasnprintf implementation needs special code for + the 'b' directive.]) + ;; + esac +]) + # Extra prerequisites of lib/vasnprintf.c for supporting the 'F' directive. AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_F], [ @@ -187,6 +255,21 @@ AC_DEFUN esac ]) +# Extra prerequisites of lib/vasnprintf.c for supporting the 'lc' directive. +AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_LC], +[ + AC_REQUIRE([gl_PRINTF_DIRECTIVE_LC]) + case "$gl_cv_func_printf_directive_lc" in + *yes) + ;; + *) + AC_DEFINE([NEED_PRINTF_DIRECTIVE_LC], [1], + [Define if the vasnprintf implementation needs special code for + the 'lc' directive.]) + ;; + esac +]) + # Extra prerequisites of lib/vasnprintf.c for supporting the ' flag. AC_DEFUN([gl_PREREQ_VASNPRINTF_FLAG_GROUPING], [ @@ -276,15 +359,17 @@ AC_DEFUN ]) # Prerequisites of lib/vasnprintf.c including all extras for POSIX compliance. -AC_DEFUN([gl_PREREQ_VASNPRINTF_WITH_EXTRAS], +AC_DEFUN([gl_PREREQ_VASNPRINTF_WITH_POSIX_EXTRAS], [ AC_REQUIRE([gl_PREREQ_VASNPRINTF]) gl_PREREQ_VASNPRINTF_LONG_DOUBLE gl_PREREQ_VASNPRINTF_INFINITE_DOUBLE gl_PREREQ_VASNPRINTF_INFINITE_LONG_DOUBLE gl_PREREQ_VASNPRINTF_DIRECTIVE_A + gl_PREREQ_VASNPRINTF_DIRECTIVE_B gl_PREREQ_VASNPRINTF_DIRECTIVE_F gl_PREREQ_VASNPRINTF_DIRECTIVE_LS + gl_PREREQ_VASNPRINTF_DIRECTIVE_LC gl_PREREQ_VASNPRINTF_FLAG_GROUPING gl_PREREQ_VASNPRINTF_FLAG_LEFTADJUST gl_PREREQ_VASNPRINTF_FLAG_ZERO @@ -292,7 +377,34 @@ AC_DEFUN gl_PREREQ_VASNPRINTF_ENOMEM ]) +# Extra prerequisites of lib/vasnprintf.c for supporting the 'B' directive. +AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_UPPERCASE_B], +[ + AC_REQUIRE([gl_PRINTF_DIRECTIVE_UPPERCASE_B]) + case "$gl_cv_func_printf_directive_uppercase_b" in + *yes) + ;; + *) + AC_DEFINE([NEED_PRINTF_DIRECTIVE_UPPERCASE_B], [1], + [Define if the vasnprintf implementation needs special code for + the 'B' directive.]) + ;; + esac +]) + +# Prerequisites of lib/vasnprintf.c including all extras for POSIX compliance +# and GNU compatibility. +AC_DEFUN([gl_PREREQ_VASNPRINTF_WITH_GNU_EXTRAS], +[ + gl_PREREQ_VASNPRINTF_WITH_POSIX_EXTRAS + AC_DEFINE([SUPPORT_GNU_PRINTF_DIRECTIVES], [1], + [Define if the vasnprintf implementation should support GNU compatible + printf directives.]) + gl_PREREQ_VASNPRINTF_DIRECTIVE_UPPERCASE_B +]) + # Prerequisites of lib/asnprintf.c. +# Prerequisites of lib/asnwprintf.c. AC_DEFUN([gl_PREREQ_ASNPRINTF], [ ]) diff --git a/m4/vasprintf-posix.m4 b/m4/vasprintf-posix.m4 index 7c198a64108..3c7a6540bd1 100644 --- a/m4/vasprintf-posix.m4 +++ b/m4/vasprintf-posix.m4 @@ -1,19 +1,34 @@ -# vasprintf-posix.m4 serial 13 +# vasprintf-posix.m4 serial 17 dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([gl_FUNC_VASPRINTF_POSIX], +[ + AC_REQUIRE([gl_FUNC_VASPRINTF_IS_POSIX]) + if test $gl_cv_func_vasprintf_posix = no; then + gl_PREREQ_VASNPRINTF_WITH_POSIX_EXTRAS + gl_REPLACE_VASNPRINTF + gl_REPLACE_VASPRINTF + fi +]) + +dnl Test whether vasprintf exists and is POSIX compliant. +dnl Result is gl_cv_func_vasprintf_posix. +AC_DEFUN([gl_FUNC_VASPRINTF_IS_POSIX], [ AC_REQUIRE([gl_PRINTF_SIZES_C99]) + AC_REQUIRE([gl_PRINTF_SIZES_C23]) AC_REQUIRE([gl_PRINTF_LONG_DOUBLE]) AC_REQUIRE([gl_PRINTF_INFINITE]) AC_REQUIRE([gl_PRINTF_INFINITE_LONG_DOUBLE]) AC_REQUIRE([gl_PRINTF_DIRECTIVE_A]) + AC_REQUIRE([gl_PRINTF_DIRECTIVE_B]) AC_REQUIRE([gl_PRINTF_DIRECTIVE_F]) AC_REQUIRE([gl_PRINTF_DIRECTIVE_N]) AC_REQUIRE([gl_PRINTF_DIRECTIVE_LS]) + AC_REQUIRE([gl_PRINTF_DIRECTIVE_LC]) AC_REQUIRE([gl_PRINTF_POSITIONS]) AC_REQUIRE([gl_PRINTF_FLAG_GROUPING]) AC_REQUIRE([gl_PRINTF_FLAG_LEFTADJUST]) @@ -24,37 +39,49 @@ AC_DEFUN AC_CHECK_FUNCS([vasprintf]) case "$gl_cv_func_printf_sizes_c99" in *yes) - case "$gl_cv_func_printf_long_double" in + case "$gl_cv_func_printf_sizes_c23" in *yes) - case "$gl_cv_func_printf_infinite" in + case "$gl_cv_func_printf_long_double" in *yes) - case "$gl_cv_func_printf_infinite_long_double" in + case "$gl_cv_func_printf_infinite" in *yes) - case "$gl_cv_func_printf_directive_a" in + case "$gl_cv_func_printf_infinite_long_double" in *yes) - case "$gl_cv_func_printf_directive_f" in + case "$gl_cv_func_printf_directive_a" in *yes) - case "$gl_cv_func_printf_directive_n" in + case "$gl_cv_func_printf_directive_b" in *yes) - case "$gl_cv_func_printf_directive_ls" in + case "$gl_cv_func_printf_directive_f" in *yes) - case "$gl_cv_func_printf_positions" in + case "$gl_cv_func_printf_directive_n" in *yes) - case "$gl_cv_func_printf_flag_grouping" in + case "$gl_cv_func_printf_directive_ls" in *yes) - case "$gl_cv_func_printf_flag_leftadjust" in + case "$gl_cv_func_printf_directive_lc" in *yes) - case "$gl_cv_func_printf_flag_zero" in + case "$gl_cv_func_printf_positions" in *yes) - case "$gl_cv_func_printf_precision" in + case "$gl_cv_func_printf_flag_grouping" in *yes) - case "$gl_cv_func_printf_enomem" in + case "$gl_cv_func_printf_flag_leftadjust" in *yes) - if test $ac_cv_func_vasprintf = yes; then - # vasprintf exists and is - # already POSIX compliant. - gl_cv_func_vasprintf_posix=yes - fi + case "$gl_cv_func_printf_flag_zero" in + *yes) + case "$gl_cv_func_printf_precision" in + *yes) + case "$gl_cv_func_printf_enomem" in + *yes) + if test $ac_cv_func_vasprintf = yes; then + # vasprintf exists and is + # already POSIX compliant. + gl_cv_func_vasprintf_posix=yes + fi + ;; + esac + ;; + esac + ;; + esac ;; esac ;; @@ -83,19 +110,4 @@ AC_DEFUN esac ;; esac - if test $gl_cv_func_vasprintf_posix = no; then - gl_PREREQ_VASNPRINTF_LONG_DOUBLE - gl_PREREQ_VASNPRINTF_INFINITE_DOUBLE - gl_PREREQ_VASNPRINTF_INFINITE_LONG_DOUBLE - gl_PREREQ_VASNPRINTF_DIRECTIVE_A - gl_PREREQ_VASNPRINTF_DIRECTIVE_F - gl_PREREQ_VASNPRINTF_DIRECTIVE_LS - gl_PREREQ_VASNPRINTF_FLAG_GROUPING - gl_PREREQ_VASNPRINTF_FLAG_LEFTADJUST - gl_PREREQ_VASNPRINTF_FLAG_ZERO - gl_PREREQ_VASNPRINTF_PRECISION - gl_PREREQ_VASNPRINTF_ENOMEM - gl_REPLACE_VASNPRINTF - gl_REPLACE_VASPRINTF - fi ]) diff --git a/m4/vfprintf-posix.m4 b/m4/vfprintf-posix.m4 index ec680522142..6b51c50adab 100644 --- a/m4/vfprintf-posix.m4 +++ b/m4/vfprintf-posix.m4 @@ -1,19 +1,34 @@ -# vfprintf-posix.m4 serial 14 +# vfprintf-posix.m4 serial 18 dnl Copyright (C) 2007-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. AC_DEFUN([gl_FUNC_VFPRINTF_POSIX], +[ + AC_REQUIRE([gl_FUNC_VFPRINTF_IS_POSIX]) + if test $gl_cv_func_vfprintf_posix = no; then + gl_PREREQ_VASNPRINTF_WITH_POSIX_EXTRAS + gl_REPLACE_VASNPRINTF + gl_REPLACE_VFPRINTF + fi +]) + +dnl Test whether vfprintf is POSIX compliant. +dnl Result is gl_cv_func_vfprintf_posix. +AC_DEFUN([gl_FUNC_VFPRINTF_IS_POSIX], [ AC_REQUIRE([gl_PRINTF_SIZES_C99]) + AC_REQUIRE([gl_PRINTF_SIZES_C23]) AC_REQUIRE([gl_PRINTF_LONG_DOUBLE]) AC_REQUIRE([gl_PRINTF_INFINITE]) AC_REQUIRE([gl_PRINTF_INFINITE_LONG_DOUBLE]) AC_REQUIRE([gl_PRINTF_DIRECTIVE_A]) + AC_REQUIRE([gl_PRINTF_DIRECTIVE_B]) AC_REQUIRE([gl_PRINTF_DIRECTIVE_F]) AC_REQUIRE([gl_PRINTF_DIRECTIVE_N]) AC_REQUIRE([gl_PRINTF_DIRECTIVE_LS]) + AC_REQUIRE([gl_PRINTF_DIRECTIVE_LC]) AC_REQUIRE([gl_PRINTF_POSITIONS]) AC_REQUIRE([gl_PRINTF_FLAG_GROUPING]) AC_REQUIRE([gl_PRINTF_FLAG_LEFTADJUST]) @@ -23,35 +38,47 @@ AC_DEFUN gl_cv_func_vfprintf_posix=no case "$gl_cv_func_printf_sizes_c99" in *yes) - case "$gl_cv_func_printf_long_double" in + case "$gl_cv_func_printf_sizes_c23" in *yes) - case "$gl_cv_func_printf_infinite" in + case "$gl_cv_func_printf_long_double" in *yes) - case "$gl_cv_func_printf_infinite_long_double" in + case "$gl_cv_func_printf_infinite" in *yes) - case "$gl_cv_func_printf_directive_a" in + case "$gl_cv_func_printf_infinite_long_double" in *yes) - case "$gl_cv_func_printf_directive_f" in + case "$gl_cv_func_printf_directive_a" in *yes) - case "$gl_cv_func_printf_directive_n" in + case "$gl_cv_func_printf_directive_b" in *yes) - case "$gl_cv_func_printf_directive_ls" in + case "$gl_cv_func_printf_directive_f" in *yes) - case "$gl_cv_func_printf_positions" in + case "$gl_cv_func_printf_directive_n" in *yes) - case "$gl_cv_func_printf_flag_grouping" in + case "$gl_cv_func_printf_directive_ls" in *yes) - case "$gl_cv_func_printf_flag_leftadjust" in + case "$gl_cv_func_printf_directive_lc" in *yes) - case "$gl_cv_func_printf_flag_zero" in + case "$gl_cv_func_printf_positions" in *yes) - case "$gl_cv_func_printf_precision" in + case "$gl_cv_func_printf_flag_grouping" in *yes) - case "$gl_cv_func_printf_enomem" in + case "$gl_cv_func_printf_flag_leftadjust" in *yes) - # vfprintf exists and is - # already POSIX compliant. - gl_cv_func_vfprintf_posix=yes + case "$gl_cv_func_printf_flag_zero" in + *yes) + case "$gl_cv_func_printf_precision" in + *yes) + case "$gl_cv_func_printf_enomem" in + *yes) + # vfprintf exists and is + # already POSIX compliant. + gl_cv_func_vfprintf_posix=yes + ;; + esac + ;; + esac + ;; + esac ;; esac ;; @@ -80,21 +107,6 @@ AC_DEFUN esac ;; esac - if test $gl_cv_func_vfprintf_posix = no; then - gl_PREREQ_VASNPRINTF_LONG_DOUBLE - gl_PREREQ_VASNPRINTF_INFINITE_DOUBLE - gl_PREREQ_VASNPRINTF_INFINITE_LONG_DOUBLE - gl_PREREQ_VASNPRINTF_DIRECTIVE_A - gl_PREREQ_VASNPRINTF_DIRECTIVE_F - gl_PREREQ_VASNPRINTF_DIRECTIVE_LS - gl_PREREQ_VASNPRINTF_FLAG_GROUPING - gl_PREREQ_VASNPRINTF_FLAG_LEFTADJUST - gl_PREREQ_VASNPRINTF_FLAG_ZERO - gl_PREREQ_VASNPRINTF_PRECISION - gl_PREREQ_VASNPRINTF_ENOMEM - gl_REPLACE_VASNPRINTF - gl_REPLACE_VFPRINTF - fi ]) AC_DEFUN([gl_REPLACE_VFPRINTF], commit a46e231a5f27c46933cc53865cee452ad1a0c0d3 Merge: 45ab9158cf1 ebf5e4ca1cd Author: Po Lu Date: Mon May 15 10:38:10 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 45ab9158cf1a94041ad65c66818dde56e7c1c43b Merge: 841b0e22011 e7dc30c1d58 Author: Po Lu Date: Mon May 15 09:10:49 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 841b0e220111869fcaf26468d88c7dd18e3caac1 Author: Po Lu Date: Sun May 14 11:12:54 2023 +0800 Implement document moving on Android * java/org/gnu/emacs/EmacsDocumentsProvider.java (notifyChangeByName): New function. (queryDocument1): Set FLAG_SUPPORTS_MOVE where necessary. (moveDocument): Implement new function. diff --git a/java/org/gnu/emacs/EmacsDocumentsProvider.java b/java/org/gnu/emacs/EmacsDocumentsProvider.java index a92a1a5d330..b4ac4624829 100644 --- a/java/org/gnu/emacs/EmacsDocumentsProvider.java +++ b/java/org/gnu/emacs/EmacsDocumentsProvider.java @@ -38,7 +38,9 @@ import android.net.Uri; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.IOException; /* ``Documents provider''. This allows Emacs's home directory to be @@ -155,6 +157,22 @@ public final class EmacsDocumentsProvider extends DocumentsProvider context.getContentResolver ().notifyChange (updatedUri, null); } + /* Inform the system that FILE's contents (or FILE itself) has + changed. FILE is a string describing containing the file name of + a directory as opposed to a File. */ + + private void + notifyChangeByName (String file) + { + Uri updatedUri; + Context context; + + context = getContext (); + updatedUri + = buildChildDocumentsUri ("org.gnu.emacs", file); + context.getContentResolver ().notifyChange (updatedUri, null); + } + /* Return the MIME type of a file FILE. */ private String @@ -212,6 +230,9 @@ public final class EmacsDocumentsProvider extends DocumentsProvider if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) flags |= Document.FLAG_SUPPORTS_RENAME; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + flags |= Document.FLAG_SUPPORTS_MOVE; } } else if (file.canWrite ()) @@ -224,7 +245,10 @@ else if (file.canWrite ()) flags |= Document.FLAG_SUPPORTS_RENAME; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - flags |= Document.FLAG_SUPPORTS_REMOVE; + { + flags |= Document.FLAG_SUPPORTS_REMOVE; + flags |= Document.FLAG_SUPPORTS_MOVE; + } } displayName = file.getName (); @@ -460,4 +484,92 @@ else if (file.canWrite ()) { return documentId.startsWith (parentDocumentId); } + + @Override + public String + moveDocument (String sourceDocumentId, + String sourceParentDocumentId, + String targetParentDocumentId) + throws FileNotFoundException + { + File file, newName; + FileInputStream inputStream; + FileOutputStream outputStream; + byte buffer[]; + int length; + + file = new File (sourceDocumentId); + + /* Now, create the file name of the parent document. */ + newName = new File (targetParentDocumentId, + file.getName ()); + + /* Try to perform a simple rename, before falling back to + copying. */ + + if (file.renameTo (newName)) + { + notifyChangeByName (file.getParent ()); + notifyChangeByName (targetParentDocumentId); + return newName.getAbsolutePath (); + } + + /* If that doesn't work, create the new file and copy over the old + file's contents. */ + + inputStream = null; + outputStream = null; + + try + { + if (!newName.createNewFile () + || !newName.setWritable (true) + || !newName.setReadable (true)) + throw new FileNotFoundException ("failed to create new file"); + + /* Open the file in preparation for a copy. */ + + inputStream = new FileInputStream (file); + outputStream = new FileOutputStream (newName); + + /* Allocate the buffer used to hold data. */ + + buffer = new byte[4096]; + + while ((length = inputStream.read (buffer)) > 0) + outputStream.write (buffer, 0, length); + } + catch (IOException e) + { + throw new FileNotFoundException ("IOException: " + e); + } + finally + { + try + { + if (inputStream != null) + inputStream.close (); + } + catch (IOException e) + { + + } + + try + { + if (outputStream != null) + outputStream.close (); + } + catch (IOException e) + { + + } + } + + file.delete (); + notifyChangeByName (file.getParent ()); + notifyChangeByName (targetParentDocumentId); + + return newName.getAbsolutePath (); + } } commit 4d4a96b2061a463fced1add3b52beb3398c39810 Merge: 67db3bfdc27 a7dcc0d55c6 Author: Po Lu Date: Sun May 14 08:30:51 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 67db3bfdc27a5db27ffbe346387f604cf21182b7 Merge: 7ac8bcaacc1 3e132b972e3 Author: Po Lu Date: Sat May 13 08:53:57 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 7ac8bcaacc1f3dbbda3febe09dde6d18a2d69729 Merge: 140c5bffc64 30501083f2f Author: Po Lu Date: Fri May 12 10:43:52 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 140c5bffc6441e6cee9ba60af114392729239abe Merge: 4fbaf6d9fe1 e4c8ba6c058 Author: Po Lu Date: Thu May 11 08:58:57 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 4fbaf6d9fe1923d03c971a970ad4d2eef075aef3 Merge: 1230521f713 7791907c385 Author: Po Lu Date: Wed May 10 08:51:27 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 1230521f7130a3ad8fe4033817f6d936675742c1 Merge: 428c59180d5 b625ccff870 Author: Po Lu Date: Tue May 9 09:10:52 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 428c59180d51ab6fa4ac820bb49189ae81ded3cb Author: Po Lu Date: Mon May 8 08:34:27 2023 +0800 Update Android port * java/Makefile.in (install_temp/assets/version): Fix generation in out of tree builds. diff --git a/java/Makefile.in b/java/Makefile.in index 3b06fc1d4cc..84173cd9655 100644 --- a/java/Makefile.in +++ b/java/Makefile.in @@ -233,7 +233,8 @@ install_temp/assets/directory-tree: install_temp/assets/directory-tree install_temp/assets/version: install_temp - $(AM_V_GEN) { (git rev-parse HEAD || echo "Unknown") \ + $(AM_V_GEN) { (cd $(top_srcdir) \ + && git rev-parse HEAD || echo "Unknown") \ && (git rev-parse --abbrev-ref HEAD \ || echo "Unknown") } 2> /dev/null > $@ commit 9a90aeae07d1954aa1d1e626d1b5b1ce35a4c981 Merge: 889b61b9991 3adc1e7f379 Author: Po Lu Date: Mon May 8 08:31:59 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 889b61b99918d1c6313d4f884de2e2cb3ab466c9 Author: Po Lu Date: Sun May 7 11:09:56 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsInputConnection.java (requestCursorUpdates): * java/org/gnu/emacs/EmacsNative.java (requestCursorUpdates): * java/org/gnu/emacs/EmacsService.java (updateCursorAnchorInfo): New functions. * src/android.c (struct android_emacs_service) (android_init_emacs_service): Add new method. (android_update_cursor_anchor_info): New function. * src/androidfns.c (android_set_preeditarea): New function. * src/androidgui.h (enum android_ime_operation): New operation `REQUEST_CURSOR_UPDATES'. (struct android_ime_event): Document new meaning of `length'. * src/androidterm.c (android_request_cursor_updates): New function. (android_handle_ime_event): Handle new operations. (handle_one_android_event, android_draw_window_cursor): Update the preedit area if needed, like on X. (requestCursorUpdates): New function. * src/androidterm.h (struct android_output): New field `need_cursor_updates'. diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index d13b48288ce..21bbaca5d07 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -324,6 +324,17 @@ public final class EmacsInputConnection extends BaseInputConnection return this.deleteSurroundingText (beforeLength, afterLength); } + @Override + public boolean + requestCursorUpdates (int cursorUpdateMode) + { + if (EmacsService.DEBUG_IC) + Log.d (TAG, "requestCursorUpdates: " + cursorUpdateMode); + + EmacsNative.requestCursorUpdates (windowHandle, cursorUpdateMode); + return true; + } + /* Override functions which are not implemented. */ diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 7d13ff99abb..e699dda9ad4 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -209,6 +209,7 @@ public static native ExtractedText getExtractedText (short window, ExtractedTextRequest req, int flags); public static native void requestSelectionUpdate (short window); + public static native void requestCursorUpdates (short window, int mode); /* Return the current value of the selection, or -1 upon diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 33436892caa..30ef71540a9 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -25,10 +25,12 @@ import java.util.List; +import android.graphics.Matrix; import android.graphics.Point; import android.view.InputDevice; import android.view.KeyEvent; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.ExtractedText; import android.app.Notification; @@ -635,6 +637,36 @@ invocation of app_process (through android-emacs) can window.view.imManager.restartInput (window.view); } + public void + updateCursorAnchorInfo (EmacsWindow window, float x, + float y, float yBaseline, + float yBottom) + { + CursorAnchorInfo info; + CursorAnchorInfo.Builder builder; + Matrix matrix; + int[] offsets; + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + return; + + offsets = new int[2]; + builder = new CursorAnchorInfo.Builder (); + matrix = new Matrix (window.view.getMatrix ()); + window.view.getLocationOnScreen (offsets); + matrix.postTranslate (offsets[0], offsets[1]); + builder.setMatrix (matrix); + builder.setInsertionMarkerLocation (x, y, yBaseline, yBottom, + 0); + info = builder.build (); + + if (DEBUG_IC) + Log.d (TAG, ("updateCursorAnchorInfo: " + x + " " + y + + " " + yBaseline + "-" + yBottom)); + + window.view.imManager.updateCursorAnchorInfo (window.view, info); + } + /* Open a content URI described by the bytes BYTES, a non-terminated string; make it writable if WRITABLE, and readable if READABLE. Truncate the file if TRUNCATE. diff --git a/src/android.c b/src/android.c index 129ad6b5767..8a41a7cdec5 100644 --- a/src/android.c +++ b/src/android.c @@ -113,6 +113,7 @@ #define ANDROID_MAX_ASSET_FD 65535 jmethodID query_battery; jmethodID display_toast; jmethodID update_extracted_text; + jmethodID update_cursor_anchor_info; }; struct android_emacs_pixmap @@ -2209,6 +2210,8 @@ #define FIND_METHOD(c_name, name, signature) \ FIND_METHOD (update_extracted_text, "updateExtractedText", "(Lorg/gnu/emacs/EmacsWindow;" "Landroid/view/inputmethod/ExtractedText;I)V"); + FIND_METHOD (update_cursor_anchor_info, "updateCursorAnchorInfo", + "(Lorg/gnu/emacs/EmacsWindow;FFFF)V"); #undef FIND_METHOD } @@ -6277,6 +6280,37 @@ android_update_extracted_text (android_window window, void *text, android_exception_check_1 (text); } +/* Report the position of the cursor to the input method connection on + WINDOW. + + X is the horizontal position of the end of the insertion marker. Y + is the top of the insertion marker. Y_BASELINE is the baseline of + the row containing the insertion marker, and Y_BOTTOM is the bottom + of the insertion marker. */ + +void +android_update_cursor_anchor_info (android_window window, float x, + float y, float y_baseline, + float y_bottom) +{ + jobject object; + jmethodID method; + + object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); + method = service_class.update_cursor_anchor_info; + + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + method, + object, + (jfloat) x, + (jfloat) y, + (jfloat) y_baseline, + (jfloat) y_bottom); + android_exception_check (); +} + /* Window decoration management functions. */ diff --git a/src/androidfns.c b/src/androidfns.c index 3bd34edd5b9..60b0549e7d1 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -3000,6 +3000,34 @@ DEFUN ("android-query-battery", Fandroid_query_battery, make_fixnum (state.temperature)); } + + +/* Miscellaneous input method related stuff. */ + +/* Report X, Y, by the phys cursor width and height as the cursor + anchor rectangle for W's frame. */ + +void +android_set_preeditarea (struct window *w, int x, int y) +{ + struct frame *f; + + f = WINDOW_XFRAME (w); + + /* Convert the window coordinates to the frame's coordinate + space. */ + x = (WINDOW_TO_FRAME_PIXEL_X (w, x) + + WINDOW_LEFT_FRINGE_WIDTH (w) + + WINDOW_LEFT_MARGIN_WIDTH (w)); + y = WINDOW_TO_FRAME_PIXEL_Y (w, y); + + /* Note that calculating the baseline is too hard, so the bottom of + the cursor is used instead. */ + android_update_cursor_anchor_info (FRAME_ANDROID_WINDOW (f), x, + y, y + w->phys_cursor_height, + y + w->phys_cursor_height); +} + #endif diff --git a/src/androidgui.h b/src/androidgui.h index ddd8e9fcf72..6db25098398 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -432,6 +432,13 @@ #define ANDROID_IS_MODIFIER_KEY(key) \ ANDROID_IME_START_BATCH_EDIT, ANDROID_IME_END_BATCH_EDIT, ANDROID_IME_REQUEST_SELECTION_UPDATE, + ANDROID_IME_REQUEST_CURSOR_UPDATES, + }; + +enum + { + ANDROID_CURSOR_UPDATE_IMMEDIATE = 1, + ANDROID_CURSOR_UPDATE_MONITOR = (1 << 1), }; struct android_ime_event @@ -452,7 +459,11 @@ #define ANDROID_IS_MODIFIER_KEY(key) \ indices, and may actually mean ``left'' and ``right''. */ ptrdiff_t start, end, position; - /* The number of characters in TEXT. */ + /* The number of characters in TEXT. + + If OPERATION is ANDROID_IME_REQUEST_CURSOR_UPDATES, then this is + actually the cursor update mode associated with that + operation. */ size_t length; /* TEXT is either NULL, or a pointer to LENGTH bytes of malloced @@ -620,6 +631,8 @@ #define ANDROID_IS_MODIFIER_KEY(key) \ extern void android_reset_ic (android_window, enum android_ic_mode); extern void android_update_extracted_text (android_window, void *, int); +extern void android_update_cursor_anchor_info (android_window, float, + float, float, float); extern int android_set_fullscreen (android_window, bool); enum android_cursor_shape diff --git a/src/androidterm.c b/src/androidterm.c index 8ba7fb6a798..6f7c06875ca 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -632,6 +632,40 @@ android_decode_utf16 (unsigned short *utf16, size_t n) return coding.dst_object; } +/* Handle a cursor update request for F from the input method. + MODE specifies whether or not an update should be sent immediately, + and whether or not they are needed in the future. + + If MODE & ANDROID_CURSOR_UPDATE_IMMEDIATE, report the position of + F's old selected window's phys cursor now. + + If MODE & ANDROID_CURSOR_UPDATE_MONITOR, set + `need_cursor_updates'. */ + +static void +android_request_cursor_updates (struct frame *f, int mode) +{ + struct window *w; + + if (mode & ANDROID_CURSOR_UPDATE_IMMEDIATE + && WINDOWP (WINDOW_LIVE_P (f->old_selected_window) + ? f->old_selected_window + : f->selected_window)) + { + /* Prefer the old selected window, as its selection is what was + reported to the IME previously. */ + + w = XWINDOW (WINDOW_LIVE_P (f->old_selected_window) + ? f->old_selected_window + : f->selected_window); + android_set_preeditarea (w, w->cursor.x, w->cursor.y); + } + + /* Now say whether or not updates are needed in the future. */ + FRAME_OUTPUT_DATA (f)->need_cursor_updates + = (mode & ANDROID_CURSOR_UPDATE_MONITOR); +} + /* Handle a single input method event EVENT, delivered to the frame F. @@ -705,6 +739,10 @@ android_handle_ime_event (union android_event *event, struct frame *f) case ANDROID_IME_REQUEST_SELECTION_UPDATE: request_point_update (f, event->ime.counter); break; + + case ANDROID_IME_REQUEST_CURSOR_UPDATES: + android_request_cursor_updates (f, event->ime.length); + break; } } @@ -724,6 +762,7 @@ handle_one_android_event (struct android_display_info *dpyinfo, double scroll_unit; int keysym; ptrdiff_t nchars, i; + struct window *w; /* It is okay for this to not resemble handle_one_xevent so much. Differences in event handling code are much less nasty than @@ -812,6 +851,12 @@ handle_one_android_event (struct android_display_info *dpyinfo, inev.ie.kind = MOVE_FRAME_EVENT; XSETFRAME (inev.ie.frame_or_window, f); } + + if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates) + { + w = XWINDOW (f->selected_window); + android_set_preeditarea (w, w->cursor.x, w->cursor.y); + } } goto OTHER; @@ -954,6 +999,16 @@ handle_one_android_event (struct android_display_info *dpyinfo, goto done_keysym; done_keysym: + + /* Now proceed to tell the input method the current position of + the cursor, if required. */ + + if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates) + { + w = XWINDOW (f->selected_window); + android_set_preeditarea (w, w->cursor.x, w->cursor.y); + } + goto OTHER; case ANDROID_FOCUS_IN: @@ -4321,6 +4376,10 @@ android_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, int x, int y, enum text_cursor_kinds cursor_type, int cursor_width, bool on_p, bool active_p) { + struct frame *f; + + f = WINDOW_XFRAME (w); + if (on_p) { w->phys_cursor_type = cursor_type; @@ -4362,6 +4421,13 @@ android_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, emacs_abort (); } } + + /* Now proceed to tell the input method the current position of + the cursor, if required. */ + + if (FRAME_OUTPUT_DATA (f)->need_cursor_updates + && w == XWINDOW (f->selected_window)) + android_set_preeditarea (w, x, y); } } @@ -5404,6 +5470,28 @@ NATIVE_NAME (requestSelectionUpdate) (JNIEnv *env, jobject object, android_write_event (&event); } +JNIEXPORT void JNICALL +NATIVE_NAME (requestCursorUpdates) (JNIEnv *env, jobject object, + jshort window, jint mode) +{ + JNI_STACK_ALIGNMENT_PROLOGUE; + + union android_event event; + + event.ime.type = ANDROID_INPUT_METHOD; + event.ime.serial = ++event_serial; + event.ime.window = window; + event.ime.operation = ANDROID_IME_REQUEST_CURSOR_UPDATES; + event.ime.start = 0; + event.ime.end = 0; + event.ime.length = mode; + event.ime.position = 0; + event.ime.text = NULL; + event.ime.counter = ++edit_counter; + + android_write_event (&event); +} + #ifdef __clang__ #pragma clang diagnostic pop #else diff --git a/src/androidterm.h b/src/androidterm.h index 9396d5fe315..e3738fb2192 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -231,6 +231,10 @@ #define _ANDROID_TERM_H_ because the frame contents have been dirtied. */ bool_bf need_buffer_flip : 1; + /* Whether or not the input method should be notified every time the + position of this frame's selected window changes. */ + bool_bf need_cursor_updates : 1; + /* Relief GCs, colors etc. */ struct relief { struct android_gc *gc; @@ -383,6 +387,7 @@ #define XSCROLL_BAR(vec) ((struct scroll_bar *) XVECTOR (vec)) extern void android_free_gcs (struct frame *); extern void android_default_font_parameter (struct frame *, Lisp_Object); +extern void android_set_preeditarea (struct window *, int, int); /* Defined in androidterm.c. */ commit c0a52c6cef77b8bc83e9d373ac0d0899c93f7a37 Merge: 11cb9cc5988 d5ab8b6f245 Author: Po Lu Date: Sun May 7 08:33:48 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 11cb9cc598811ed36ba8aa2e5dafddeecaf95971 Merge: c7ca46b0a7c 9b66a64d9c2 Author: Po Lu Date: Sat May 6 20:35:42 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit c7ca46b0a7c7184ff580f930649bfc432954d0d5 Author: Po Lu Date: Sat May 6 20:32:08 2023 +0800 Update Android port * configure.ac (LIBGMP_CFLAGS): Avoid non portable test expression. diff --git a/configure.ac b/configure.ac index 750220b5129..832e5a6bafd 100644 --- a/configure.ac +++ b/configure.ac @@ -7261,7 +7261,7 @@ AC_DEFUN # Set up libgmp on Android. Make sure to override what gnulib has # found. LIBGMP_CFLAGS= -if test "$REALLY_ANDROID" == "yes" && test "$with_libgmp" != "no"; then +if test "$REALLY_ANDROID" = "yes" && test "$with_libgmp" != "no"; then HAVE_LIBGMP=no ndk_SEARCH_MODULE([libgmp], [LIBGMP], [HAVE_LIBGMP=yes]) commit 3198b7dc565e0e41d40b6c23509c285e96044a83 Author: Po Lu Date: Sat May 6 11:32:56 2023 +0800 Update Android port * cross/verbose.mk.android: Get rid of badly aligned ANDROID_CC messages. * java/org/gnu/emacs/EmacsInputConnection.java (syncAfterCommit) (extractAbsoluteOffsets): Add workarounds for several kinds of machines. (commitText, getExtractedText): Likewise. * src/textconv.c (really_commit_text): Improve definition of POSITION. (get_extracted_text): Default to providing at least 4 characters. diff --git a/cross/verbose.mk.android b/cross/verbose.mk.android index 7d07b978de2..998f9843c7d 100644 --- a/cross/verbose.mk.android +++ b/cross/verbose.mk.android @@ -27,16 +27,7 @@ AM_V_CC = AM_V_CXX = AM_V_CCLD = AM_V_CXXLD = -AM_V_ELC = -AM_V_ELN = AM_V_GEN = -AM_V_GLOBALS = -AM_V_NO_PD = -AM_V_RC = -AM_V_JAVAC = -AM_V_DX = -AM_V_AAPT = -AM_V_ZIPALIGN = else # Whether $(info ...) works. This is to work around a bug in GNU Make @@ -53,13 +44,12 @@ have_working_info = $(filter notintermediate,$(value .FEATURES)) # The workaround is done only for AM_V_ELC and AM_V_ELN, # since the bug is not annoying elsewhere. -AM_V_AR = @$(info $ AR $@) +AM_V_AR = @$(info $ AR $@) AM_V_at = @ -AM_V_CC = @$(info $ ANDROID_CC $@) -AM_V_CXX = @$(info $ ANDROID_CXX $@) -AM_V_CCLD = @$(info $ CCLD $@) -AM_V_CXXLD = @$(info $ CXXLD $@) - -AM_V_GEN = @$(info $ GEN $@) +AM_V_CC = @$(info $ CC $@) +AM_V_CXX = @$(info $ CXX $@) +AM_V_CCLD = @$(info $ CCLD $@) +AM_V_CXXLD = @$(info $ CXXLD $@) +AM_V_GEN = @$(info $ GEN $@) AM_V_NO_PD = --no-print-directory endif diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index 7b40fcff99e..d13b48288ce 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -26,6 +26,8 @@ import android.view.inputmethod.TextSnapshot; import android.view.KeyEvent; +import android.os.Build; + import android.util.Log; /* Android input methods, take number six. See textconv.c for more @@ -37,6 +39,34 @@ public final class EmacsInputConnection extends BaseInputConnection private EmacsView view; private short windowHandle; + /* Whether or not to synchronize and call `updateIC' with the + selection position after committing text. + + This helps with on screen keyboard programs found in some vendor + versions of Android, which rely on immediate updates to the point + position after text is commited in order to place the cursor + within that text. */ + + private static boolean syncAfterCommit; + + /* Whether or not to return empty text with the offset set to zero + if a request arrives that has no flags set and has requested no + characters at all. + + This is necessary with on screen keyboard programs found in some + vendor versions of Android which don't rely on the documented + meaning of `ExtractedText.startOffset', and instead take the + selection offset inside at face value. */ + + private static boolean extractAbsoluteOffsets; + + static + { + if (Build.MANUFACTURER.equalsIgnoreCase ("Huawei") + || Build.MANUFACTURER.equalsIgnoreCase ("Honor")) + extractAbsoluteOffsets = syncAfterCommit = true; + }; + public EmacsInputConnection (EmacsView view) { @@ -85,11 +115,32 @@ public final class EmacsInputConnection extends BaseInputConnection public boolean commitText (CharSequence text, int newCursorPosition) { + int[] selection; + if (EmacsService.DEBUG_IC) Log.d (TAG, "commitText: " + text + " " + newCursorPosition); EmacsNative.commitText (windowHandle, text.toString (), newCursorPosition); + + if (syncAfterCommit) + { + /* Synchronize with the Emacs thread, obtain the new + selection, and report it immediately. */ + + selection = EmacsNative.getSelection (windowHandle); + + if (EmacsService.DEBUG_IC && selection != null) + Log.d (TAG, "commitText: new selection is " + selection[0] + + ", by " + selection[1]); + + if (selection != null) + /* N.B. that the composing region is removed after text is + committed. */ + view.imManager.updateSelection (view, selection[0], + selection[1], -1, -1); + } + return true; } @@ -203,16 +254,42 @@ public final class EmacsInputConnection extends BaseInputConnection getExtractedText (ExtractedTextRequest request, int flags) { ExtractedText text; + int[] selection; if (EmacsService.DEBUG_IC) - Log.d (TAG, "getExtractedText: " + request + " " + flags); - - text = EmacsNative.getExtractedText (windowHandle, request, - flags); + Log.d (TAG, "getExtractedText: " + request.hintMaxChars + ", " + + request.hintMaxLines + " " + flags); + + /* If a request arrives with hintMaxChars, hintMaxLines and flags + set to 0, and the system is known to be buggy, return an empty + extracted text object with the absolute selection positions. */ + + if (extractAbsoluteOffsets + && request.hintMaxChars == 0 + && request.hintMaxLines == 0 + && flags == 0) + { + /* Obtain the selection. */ + selection = EmacsNative.getSelection (windowHandle); + if (selection == null) + return null; + + /* Create the workaround extracted text. */ + text = new ExtractedText (); + text.partialStartOffset = -1; + text.partialEndOffset = -1; + text.text = ""; + text.selectionStart = selection[0]; + text.selectionEnd = selection[1]; + } + else + text = EmacsNative.getExtractedText (windowHandle, request, + flags); if (EmacsService.DEBUG_IC) Log.d (TAG, "getExtractedText: " + text.text + " @" - + text.startOffset + ":" + text.selectionStart); + + text.startOffset + ":" + text.selectionStart + + ", " + text.selectionEnd); return text; } diff --git a/src/textconv.c b/src/textconv.c index 4fa92f43ecd..e1a73e91397 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -540,7 +540,11 @@ restore_selected_window (Lisp_Object window) /* Commit the given text in the composing region. If there is no composing region, then insert the text after F's selected window's - last point instead. Finally, remove the composing region. */ + last point instead. Finally, remove the composing region. + + Then, move point to POSITION relative to TEXT. If POSITION is + greater than zero, it is relative to the character at the end of + TEXT; otherwise, it is relative to the start of TEXT. */ static void really_commit_text (struct frame *f, EMACS_INT position, @@ -577,14 +581,16 @@ really_commit_text (struct frame *f, EMACS_INT position, Finsert (1, &text); record_buffer_change (start, PT, text); - /* Move to a the position specified in POSITION. */ + /* Move to a the position specified in POSITION. If POSITION is + less than zero, it is relative to the start of the text that + was inserted. */ - if (position < 0) + if (position <= 0) { wanted = marker_position (f->conversion.compose_region_start); - if (INT_SUBTRACT_WRAPV (wanted, position, &wanted) + if (INT_ADD_WRAPV (wanted, position, &wanted) || wanted < BEGV) wanted = BEGV; @@ -595,6 +601,9 @@ really_commit_text (struct frame *f, EMACS_INT position, } else { + /* Otherwise, it is relative to the last character in + TEXT. */ + wanted = marker_position (f->conversion.compose_region_end); @@ -631,9 +640,9 @@ really_commit_text (struct frame *f, EMACS_INT position, Finsert (1, &text); record_buffer_change (wanted, PT, text); - if (position < 0) + if (position <= 0) { - if (INT_SUBTRACT_WRAPV (wanted, position, &wanted) + if (INT_ADD_WRAPV (wanted, position, &wanted) || wanted < BEGV) wanted = BEGV; @@ -1533,8 +1542,9 @@ get_extracted_text (struct frame *f, ptrdiff_t n, /* Figure out the bounds of the text to return. */ if (n != -1) { - /* Make sure n is at least 2. */ - n = max (2, n); + /* Make sure n is at least 4, leaving two characters around + PT. */ + n = max (4, n); start = PT - n / 2; end = PT + n - n / 2; commit 96f9fe6514a2139373c50e19a77fd217ef62e6ef Merge: 0fbe79727b0 1ef219e220c Author: Po Lu Date: Sat May 6 07:55:02 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 0fbe79727b07879cb4f0a5cb8d7288353c082bd0 Author: Po Lu Date: Fri May 5 19:04:32 2023 +0800 Fix execution of /proc/self/exe within child processes * exec/exec.h (struct exec_tracee): New field `new_child'. Also, make `waiting_for_syscall' a bitfield. * exec/trace.c (PTRACE_GETEVENTMSG): New declaration. (MAX_TRACEES): Bump to 4096. (handle_clone_prepare): New function. (handle_clone): If required, set `new_child' and wait for a ptrace event describing the parent to arrive. (after_fork): Clear new field. (exec_waitpid): Upon a ptrace event describing a clone, create the child's tracee if it doesn't already exist. Otherwise, copy over the parent's cmdline and start running it. diff --git a/exec/exec.h b/exec/exec.h index 625ad0bb219..8ee74d7ca8b 100644 --- a/exec/exec.h +++ b/exec/exec.h @@ -153,7 +153,11 @@ #define _EXEC_H_ /* Whether or not the tracee is currently waiting for a system call to complete. */ - bool waiting_for_syscall; + bool waiting_for_syscall : 1; + + /* Whether or not the tracee has been created but is not yet + processed by `handle_clone'. */ + bool new_child : 1; #ifndef REENTRANT /* Name of the executable being run. */ diff --git a/exec/trace.c b/exec/trace.c index b765b5cffa4..974df1dd5e1 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -50,6 +50,10 @@ Copyright (C) 2023 Free Software Foundation, Inc. #define SYS_SECCOMP 1 #endif /* SYS_SECCOMP */ +#ifndef PTRACE_GETEVENTMSG +#define PTRACE_GETEVENTMSG 0x4201 +#endif /* PTRACE_GETEVENTMSG */ + /* Program tracing functions. @@ -63,7 +67,7 @@ #define SYS_SECCOMP 1 /* Number of tracees children are allowed to create. */ -#define MAX_TRACEES 1024 +#define MAX_TRACEES 4096 #ifdef __aarch64__ @@ -381,23 +385,66 @@ remove_tracee (struct exec_tracee *tracee) /* Child process tracing. */ -/* Handle the completion of a `clone' or `clone3' system call, - resulting in the creation of the process PID. Allocate a new - tracee structure from a static area for the processes's pid. +/* Array of `struct exec_tracees' that they are allocated from. */ +static struct exec_tracee static_tracees[MAX_TRACEES]; - Value is 0 upon success, 1 otherwise. */ +/* Number of tracees currently allocated. */ +static int tracees; -static int -handle_clone (pid_t pid) +/* Return the `struct exec_tracee' corresponding to the specified + PROCESS. */ + +static struct exec_tracee * +find_tracee (pid_t process) { - static struct exec_tracee static_tracees[MAX_TRACEES]; - static int tracees; struct exec_tracee *tracee; + + for (tracee = tracing_processes; tracee; tracee = tracee->next) + { + if (tracee->pid == process) + return tracee; + } + + return NULL; +} + +/* Prepare to handle the completion of a `clone' system call. + + If the new clone is not yet being traced, create a new tracee for + PARENT's child, copying over its current command line. Then, set + `new_child' in the new tracee. Otherwise, continue it until the + next syscall. */ + +static void +handle_clone_prepare (struct exec_tracee *parent) +{ +#ifndef REENTRANT long rc; - int flags; + unsigned long pid; + struct exec_tracee *tracee; - /* Now allocate a new tracee, either from static_tracees or the free - list. */ + rc = ptrace (PTRACE_GETEVENTMSG, parent->pid, NULL, + &pid); + if (rc) + return; + + /* See if the tracee already exists. */ + tracee = find_tracee (pid); + + if (tracee) + { + /* Continue the tracee. Record its command line, as that has + not yet been done. */ + + assert (tracee->new_child); + tracee->new_child = false; + tracee->exec_file = NULL; + ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0); + + if (parent->exec_file) + tracee->exec_file = strdup (parent->exec_file); + return; + } if (free_tracees) { @@ -410,13 +457,75 @@ handle_clone (pid_t pid) tracees++; } else - return 1; + return; tracee->pid = pid; tracee->next = tracing_processes; tracee->waiting_for_syscall = false; + tracee->new_child = true; + tracee->exec_file = NULL; tracing_processes = tracee; + /* Copy over the command line. */ + + if (parent->exec_file) + tracee->exec_file = strdup (parent->exec_file); +#endif /* REENTRANT */ +} + +/* Handle the completion of a `clone' or `clone3' system call, + resulting in the creation of the process PID. If TRACEE is NULL, + allocate a new tracee structure from a static area for the + processes's pid, then set TRACEE->new_child to true and await the + parent's corresponding ptrace event to arrive; otherwise, just + clear TRACEE->new_child. + + Value is 0 upon success, 2 if TRACEE should remain suspended until + the parent's ptrace-stop, and 1 otherwise. */ + +static int +handle_clone (struct exec_tracee *tracee, pid_t pid) +{ + long rc; + int flags, value; + + /* Now allocate a new tracee, either from static_tracees or the free + list, if no tracee was supplied. */ + + value = 0; + + if (!tracee) + { + if (free_tracees) + { + tracee = free_tracees; + free_tracees = free_tracees->next; + } + else if (tracees < MAX_TRACEES) + { + tracee = &static_tracees[tracees]; + tracees++; + } + else + return 1; + + tracee->pid = pid; + tracee->next = tracing_processes; + tracee->waiting_for_syscall = false; +#ifndef REENTRANT + tracee->exec_file = NULL; +#endif /* REENTRANT */ + tracing_processes = tracee; + tracee->new_child = true; + + /* Wait for the ptrace-stop to happen in the parent. */ + value = 2; + } + else + /* Clear the flag saying that this is a newly created child + process. */ + tracee->new_child = false; + /* Apply required options to the child, so that the kernel automatically traces children and makes it easy to differentiate between system call traps and other kinds of traps. */ @@ -432,15 +541,18 @@ handle_clone (pid_t pid) if (rc) goto bail; - /* The new tracee is currently stopped. Continue it until the next - system call. */ + if (value != 2) + { + /* The new tracee is currently stopped. Continue it until the next + system call. */ - rc = ptrace (PTRACE_SYSCALL, pid, 0, 0); + rc = ptrace (PTRACE_SYSCALL, pid, 0, 0); - if (rc) - goto bail; + if (rc) + goto bail; + } - return 0; + return value; bail: remove_tracee (tracee); @@ -1148,6 +1260,7 @@ after_fork (pid_t pid) tracee->pid = pid; tracee->next = tracing_processes; tracee->waiting_for_syscall = false; + tracee->new_child = false; #ifndef REENTRANT tracee->exec_file = NULL; #endif /* REENTRANT */ @@ -1155,23 +1268,6 @@ after_fork (pid_t pid) return 0; } -/* Return the `struct exec_tracee' corresponding to the specified - PROCESS. */ - -static struct exec_tracee * -find_tracee (pid_t process) -{ - struct exec_tracee *tracee; - - for (tracee = tracing_processes; tracee; tracee = tracee->next) - { - if (tracee->pid == process) - return tracee; - } - - return NULL; -} - /* Wait for a child process to exit, like `waitpid'. However, if a child stops to perform a system call, send it on its way and return -1. OPTIONS must not contain WUNTRACED. */ @@ -1199,12 +1295,12 @@ exec_waitpid (pid_t pid, int *wstatus, int options) { tracee = find_tracee (pid); - if (!tracee) + if (!tracee || tracee->new_child) { if (WSTOPSIG (status) == SIGSTOP) /* A new process has been created and stopped. Record it now. */ - handle_clone (pid); + handle_clone (tracee, pid); return -1; } @@ -1248,6 +1344,21 @@ exec_waitpid (pid_t pid, int *wstatus, int options) case SIGTRAP | (PTRACE_EVENT_FORK << 8): case SIGTRAP | (PTRACE_EVENT_VFORK << 8): case SIGTRAP | (PTRACE_EVENT_CLONE << 8): + + /* Both PTRACE_EVENT_CLONE and SIGSTOP must arrive before a + process is continued. Otherwise, its parent's cmdline + cannot be obtained and propagated. + + If the PID of the new process is currently not being + traced, create a new tracee. Set `new_child' to true, + and copy over the old command line in preparation for a + SIGSTOP signal being delivered to it. + + Otherwise, start the tracee running until the next + syscall. */ + + handle_clone_prepare (tracee); + /* These events are handled by tracing SIGSTOP signals sent to unknown tracees. Make sure not to pass through status, as there's no signal really being delivered. */ commit 2ba6c5035c904426d564eac47381480158cbbb9e Author: Po Lu Date: Fri May 5 12:10:14 2023 +0800 Update Android port * doc/emacs/android.texi (Android Environment): Document lossage with SIGSTOP. * exec/exec.c (exec_0): Check X_OK on file being opened. Also handle /proc/self/exe. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index f2bcc50df23..d7fadd69e4b 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -293,6 +293,11 @@ Android Environment functions will work correctly, but using the process ID returned by @code{process-id} for other purposes will not. + One side effect of the mechanism by which process tracing is carried +out is that job control facilities will not be able to stop +subprocesses, and the @code{SIGSTOP} signal will appear to have no +effect. + In addition, Android 12 also terminates subprocesses which are consuming CPU while Emacs itself is in the background. The system determines which processes are consuming too much CPU in intervals of diff --git a/exec/exec.c b/exec/exec.c index 17051428658..a15386b51bb 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -961,52 +961,68 @@ exec_0 (char *name, struct exec_tracee *tracee, ssize_t link_size; size_t remaining; - /* If name is not absolute, then make it relative to TRACEE's - cwd. Use stpcpy, as sprintf is not reentrant. */ + /* If the process is trying to run /proc/self/exe, make it run + itself instead. */ - if (name[0] && name[0] != '/') + if (!strcmp (name, "/proc/self/exe") && tracee->exec_file) { - /* Clear `buffer'. */ - memset (buffer, 0, sizeof buffer); - memset (buffer1, 0, sizeof buffer); + strncpy (name, tracee->exec_file, PATH_MAX - 1); + name[PATH_MAX] = '\0'; + } + else + { + /* If name is not absolute, then make it relative to TRACEE's + cwd. Use stpcpy, as sprintf is not reentrant. */ - /* Copy over /proc, the PID, and /cwd/. */ - rewrite = stpcpy (buffer, "/proc/"); - rewrite = format_pid (rewrite, tracee->pid); - stpcpy (rewrite, "/cwd"); + if (name[0] && name[0] != '/') + { + /* Clear `buffer'. */ + memset (buffer, 0, sizeof buffer); + memset (buffer1, 0, sizeof buffer); - /* Resolve this symbolic link. */ + /* Copy over /proc, the PID, and /cwd/. */ + rewrite = stpcpy (buffer, "/proc/"); + rewrite = format_pid (rewrite, tracee->pid); + stpcpy (rewrite, "/cwd"); - link_size = readlink (buffer, buffer1, - PATH_MAX + 1); + /* Resolve this symbolic link. */ - if (link_size < 0) - return NULL; + link_size = readlink (buffer, buffer1, + PATH_MAX + 1); - /* Check that the name is a reasonable size. */ + if (link_size < 0) + return NULL; - if (link_size > PATH_MAX) - { - /* The name is too long. */ - errno = ENAMETOOLONG; - return NULL; - } + /* Check that the name is a reasonable size. */ + + if (link_size > PATH_MAX) + { + /* The name is too long. */ + errno = ENAMETOOLONG; + return NULL; + } - /* Add a directory separator if necessary. */ + /* Add a directory separator if necessary. */ - if (!link_size || buffer1[link_size - 1] != '/') - buffer1[link_size] = '/', link_size++; + if (!link_size || buffer1[link_size - 1] != '/') + buffer1[link_size] = '/', link_size++; - rewrite = buffer1 + link_size; - remaining = buffer1 + sizeof buffer1 - rewrite - 1; - rewrite = stpncpy (rewrite, name, remaining); + rewrite = buffer1 + link_size; + remaining = buffer1 + sizeof buffer1 - rewrite - 1; + rewrite = stpncpy (rewrite, name, remaining); - /* Replace name with buffer1. */ + /* Replace name with buffer1. */ #ifndef REENTRANT - strcpy (name, buffer1); + strcpy (name, buffer1); #endif /* REENTRANT */ + } } + /* Check that the file is accessible and executable. */ + + if (access (name, X_OK)) + return NULL; + fd = open (name, O_RDONLY); if (fd < 0) return NULL; commit d5414f1797467b00ca4f75faf39c774b150fc509 Author: Po Lu Date: Fri May 5 10:57:26 2023 +0800 Update Android port * exec/trace.c (SYS_SECCOMP): Define when not present. diff --git a/exec/trace.c b/exec/trace.c index f9dd4d419f4..b765b5cffa4 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -46,6 +46,10 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include /* for process_vm_readv */ #endif /* HAVE_SYS_UIO_H */ +#ifndef SYS_SECCOMP +#define SYS_SECCOMP 1 +#endif /* SYS_SECCOMP */ + /* Program tracing functions. commit daccdf7e6d4bc42ffe8ffbc9db137bd44b073343 Merge: ccef1ff072e 34ac7d90876 Author: Po Lu Date: Fri May 5 08:27:30 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit ccef1ff072ef0eaac085aeffc7321dcf1fae2f0f Author: Po Lu Date: Thu May 4 11:06:18 2023 +0800 Document another misfeature of Android * doc/emacs/android.texi (Android Environment): Describe how to turn off process killing. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 1f609b4ecb0..f2bcc50df23 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -293,6 +293,20 @@ Android Environment functions will work correctly, but using the process ID returned by @code{process-id} for other purposes will not. + In addition, Android 12 also terminates subprocesses which are +consuming CPU while Emacs itself is in the background. The system +determines which processes are consuming too much CPU in intervals of +five minutes, and terminates the process that has consumed the most +CPU time. + + Android 12.1 and Android 13 provide an option to disable this +behavior; to use it, enable ``USB debugging'' (@pxref{Android +Startup}) connect the Android system to another computer, and run: + +@example +$ adb shell "settings put global settings_enable_monitor_phantom_procs false" +@end example + @section Running Emacs in the background @cindex emacs killed, android @cindex emacs in the background, android commit 339cdef28e6c78e71b310ade3ffd22333cbb0089 Author: Po Lu Date: Thu May 4 09:12:26 2023 +0800 Update Android port * exec/trace.c (check_signal): New function. (handle_exec, process_system_call): Handle signal-delivery-stop while waiting synchronously for syscall completion. diff --git a/exec/trace.c b/exec/trace.c index 579a62f6c5e..f9dd4d419f4 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -451,6 +451,8 @@ handle_clone (pid_t pid) /* File name of the loader binary. */ static const char *loader_name; + + /* Return whether or not the trap signal described by SIGNAL is generated by a system call being attempted by a tracee. */ @@ -463,6 +465,79 @@ syscall_trap_p (siginfo_t *signal) || signal->si_code == (SIGTRAP | SI_KERNEL)); } +/* Check if the wait status STATUS indicates a system call trap. + TRACEE is the process whose stop STATUS describes. If TRACEE exits + while this information is being determined, return -1; if STATUS + indicates some other kind of stop, return 1 after continuing + TRACEE. Value is 0 otherwise. */ + +static int +check_signal (struct exec_tracee *tracee, int status) +{ + siginfo_t siginfo; + + switch ((status & 0xfff00) >> 8) + { + case SIGTRAP: + /* Now, use PTRACE_GETSIGINFO to determine whether or not the + signal was delivered in response to a system call. */ + + if (ptrace (PTRACE_GETSIGINFO, tracee->pid, 0, &siginfo)) + return -1; + + if (!syscall_trap_p (&siginfo)) + { + if (siginfo.si_code < 0) + /* SIGTRAP delivered from userspace. Pass it on. */ + ptrace (PTRACE_SYSCALL, tracee->pid, 0, SIGTRAP); + else + ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0); + + return 1; + } + + case SIGTRAP | 0x80: /* SIGTRAP | 0x80 specifically refers to + system call traps. */ + break; + +#ifdef SIGSYS + case SIGSYS: + if (ptrace (PTRACE_GETSIGINFO, tracee->pid, 0, &siginfo)) + return -1; + + /* Continue the process until the next syscall, but don't + pass through the signal if an emulated syscall led to + it. */ +#ifdef HAVE_SIGINFO_T_SI_SYSCALL +#ifndef __arm__ + ptrace (PTRACE_SYSCALL, tracee->pid, + 0, ((siginfo.si_code == SYS_SECCOMP + && siginfo.si_syscall == -1) + ? 0 : status)); +#else /* __arm__ */ + ptrace (PTRACE_SYSCALL, tracee->pid, + 0, ((siginfo.si_code == SYS_SECCOMP + && siginfo.si_syscall == 222) + ? 0 : status)); +#endif /* !__arm__ */ +#else /* !HAVE_SIGINFO_T_SI_SYSCALL */ + /* Drop this signal, since what caused it is unknown. */ + ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0); +#endif /* HAVE_SIGINFO_T_SI_SYSCALL */ + return 1; +#endif /* SIGSYS */ + + default: + /* Continue the process until the next syscall. */ + ptrace (PTRACE_SYSCALL, tracee->pid, 0, status); + return 1; + } + + return 0; +} + + + /* Handle an `exec' system call from the given TRACEE. REGS are the tracee's current user-mode registers. @@ -591,6 +666,15 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) return 2; else { + /* Then, check if STATUS is not a syscall-stop, and try again if + it isn't. */ + rc = check_signal (tracee, wstatus); + + if (rc == -1) + return 2; + else if (rc) + goto again; + /* Retrieve the signal information and determine whether or not the system call has completed. */ @@ -777,9 +861,6 @@ process_system_call (struct exec_tracee *tracee) USER_REGS_STRUCT regs; int rc, wstatus, save_errno; USER_WORD callno, sp; -#ifdef __aarch64__ - USER_WORD old_w1, old_w2; -#endif /* __aarch64__ */ USER_WORD result; bool reporting_error; @@ -876,19 +957,7 @@ process_system_call (struct exec_tracee *tracee) /* First, save errno; system calls below will clobber it. */ save_errno = errno; -#ifndef __aarch64__ regs.SYSCALL_NUM_REG = -1; -#else /* __aarch64__ */ - /* ARM also requires the system call number to be valid. However, I - can't find any unused system call, so use fcntl instead, with - invalid arguments. */ - regs.SYSCALL_NUM_REG = 72; - old_w1 = regs.regs[1]; - old_w2 = regs.regs[2]; - regs.regs[0] = -1; - regs.regs[1] = -1; - regs.regs[2] = -1; -#endif /* !__aarch64__ */ regs.STACK_POINTER = sp; #ifdef __aarch64__ @@ -924,6 +993,19 @@ process_system_call (struct exec_tracee *tracee) if (rc == -1) return; + /* If the process received a signal, see if the signal is SIGSYS and + from seccomp. If so, discard it. */ + + if (WIFSTOPPED (wstatus)) + { + rc = check_signal (tracee, wstatus); + + if (rc == -1) + return; + else if (rc) + goto again1; + } + if (!WIFSTOPPED (wstatus)) /* The process has been killed in response to a signal. In this case, simply unlink the tracee and return. */ @@ -940,9 +1022,6 @@ process_system_call (struct exec_tracee *tracee) /* Report errno. */ #ifdef __aarch64__ - /* Restore x1 and x2. x0 is clobbered by errno. */ - regs.regs[1] = old_w1; - regs.regs[2] = old_w2; aarch64_set_regs (tracee->pid, ®s, false); #else /* !__aarch64__ */ ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s); @@ -966,9 +1045,6 @@ process_system_call (struct exec_tracee *tracee) /* Report errno. */ #ifdef __aarch64__ - /* Restore x1 and x2. x0 is clobbered by errno. */ - regs.regs[1] = old_w1; - regs.regs[2] = old_w2; aarch64_set_regs (tracee->pid, ®s, false); #else /* !__aarch64__ */ ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s); commit 19210f8b771947ad10fdaaf27c4f2a177ece3631 Merge: b0d6c673726 b28d44d4226 Author: Po Lu Date: Thu May 4 08:14:56 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit b0d6c6737260f10407a734b2e4811afa1516d79a Author: Po Lu Date: Wed May 3 17:01:44 2023 +0800 Update Android port * exec/config.h.in: Autoheader. * exec/configure.ac: Check for siginfo_t.si_syscall. * exec/trace.c (exec_waitpid): If SIGSYS is received, and caused by seccomp, drop it should the call number be the invalid system call used by Emacs. diff --git a/exec/config.h.in b/exec/config.h.in index 6301275fd8a..3e04af37f79 100644 --- a/exec/config.h.in +++ b/exec/config.h.in @@ -58,6 +58,9 @@ along with GNU Emacs. If not, see . */ /* Define to 1 if process_vm_readv is available. */ #undef HAVE_PROCESS_VM +/* Define to 1 if `si_syscall' is a member of `siginfo_t'. */ +#undef HAVE_SIGINFO_T_SI_SYSCALL + /* Define to 1 if stdbool.h conforms to C99. */ #undef HAVE_STDBOOL_H diff --git a/exec/configure.ac b/exec/configure.ac index efefc6c7dbc..e78d8ebea90 100644 --- a/exec/configure.ac +++ b/exec/configure.ac @@ -73,6 +73,10 @@ #include ]])])]) AC_CHECK_HEADERS([sys/param.h sys/uio.h]) +AC_CHECK_MEMBERS([siginfo_t.si_syscall], [], [], + [[ +#include + ]]) AH_BOTTOM([ #ifdef HAVE_STDBOOL_H diff --git a/exec/trace.c b/exec/trace.c index 8d107696423..579a62f6c5e 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -1174,6 +1174,31 @@ exec_waitpid (pid_t pid, int *wstatus, int options) ptrace (PTRACE_SYSCALL, pid, 0, 0); return -1; +#ifdef SIGSYS + case SIGSYS: + if (ptrace (PTRACE_GETSIGINFO, pid, 0, &siginfo)) + return -1; + + /* Continue the process until the next syscall, but don't + pass through the signal if an emulated syscall led to + it. */ +#ifdef HAVE_SIGINFO_T_SI_SYSCALL +#ifndef __arm__ + ptrace (PTRACE_SYSCALL, pid, 0, ((siginfo.si_code == SYS_SECCOMP + && siginfo.si_syscall == -1) + ? 0 : status)); +#else /* __arm__ */ + ptrace (PTRACE_SYSCALL, pid, 0, ((siginfo.si_code == SYS_SECCOMP + && siginfo.si_syscall == 222) + ? 0 : status)); +#endif /* !__arm__ */ +#else /* !HAVE_SIGINFO_T_SI_SYSCALL */ + /* Drop this signal, since what caused it is unknown. */ + ptrace (PTRACE_SYSCALL, pid, 0, 0); +#endif /* HAVE_SIGINFO_T_SI_SYSCALL */ + return -1; +#endif /* SIGSYS */ + default: /* Continue the process until the next syscall. */ ptrace (PTRACE_SYSCALL, pid, 0, status); commit 7b3c774bcee29fa0a13f38a60ddebc6fbdbedd0e Author: Po Lu Date: Wed May 3 16:00:13 2023 +0800 Update Android port * exec/config.h.in: Autoheader. * exec/configure.ac: Use system extensions. (HAVE_PROCESS_VM): Define if process_vm_readv and process_vm_writev are available. * exec/trace.c (read_memory, user_copy): Implement in terms of process_vm if possible. diff --git a/exec/config.h.in b/exec/config.h.in index c360e54a4ba..6301275fd8a 100644 --- a/exec/config.h.in +++ b/exec/config.h.in @@ -52,6 +52,12 @@ along with GNU Emacs. If not, see . */ /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H +/* Define to 1 if you have the header file. */ +#undef HAVE_MINIX_CONFIG_H + +/* Define to 1 if process_vm_readv is available. */ +#undef HAVE_PROCESS_VM + /* Define to 1 if stdbool.h conforms to C99. */ #undef HAVE_STDBOOL_H @@ -85,12 +91,18 @@ along with GNU Emacs. If not, see . */ /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UIO_H + /* Define to 1 if the system has the type `uintptr_t'. */ #undef HAVE_UINTPTR_T /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H +/* Define to 1 if you have the header file. */ +#undef HAVE_WCHAR_H + /* Define to 1 if the system has the type `_Bool'. */ #undef HAVE__BOOL @@ -168,6 +180,94 @@ along with GNU Emacs. If not, see . */ /* Define to word type used by tracees. */ #undef USER_WORD +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif +/* Enable general extensions on macOS. */ +#ifndef _DARWIN_C_SOURCE +# undef _DARWIN_C_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif +/* Enable X/Open compliant socket functions that do not require linking + with -lxnet on HP-UX 11.11. */ +#ifndef _HPUX_ALT_XOPEN_SOCKET_API +# undef _HPUX_ALT_XOPEN_SOCKET_API +#endif +/* Identify the host operating system as Minix. + This macro does not affect the system headers' behavior. + A future release of Autoconf may stop defining this macro. */ +#ifndef _MINIX +# undef _MINIX +#endif +/* Enable general extensions on NetBSD. + Enable NetBSD compatibility extensions on Minix. */ +#ifndef _NETBSD_SOURCE +# undef _NETBSD_SOURCE +#endif +/* Enable OpenBSD compatibility extensions on NetBSD. + Oddly enough, this does nothing on OpenBSD. */ +#ifndef _OPENBSD_SOURCE +# undef _OPENBSD_SOURCE +#endif +/* Define to 1 if needed for POSIX-compatible behavior. */ +#ifndef _POSIX_SOURCE +# undef _POSIX_SOURCE +#endif +/* Define to 2 if needed for POSIX-compatible behavior. */ +#ifndef _POSIX_1_SOURCE +# undef _POSIX_1_SOURCE +#endif +/* Enable POSIX-compatible threading on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */ +#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ +# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */ +#ifndef __STDC_WANT_IEC_60559_BFP_EXT__ +# undef __STDC_WANT_IEC_60559_BFP_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */ +#ifndef __STDC_WANT_IEC_60559_DFP_EXT__ +# undef __STDC_WANT_IEC_60559_DFP_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */ +#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__ +# undef __STDC_WANT_IEC_60559_FUNCS_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */ +#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__ +# undef __STDC_WANT_IEC_60559_TYPES_EXT__ +#endif +/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */ +#ifndef __STDC_WANT_LIB_EXT2__ +# undef __STDC_WANT_LIB_EXT2__ +#endif +/* Enable extensions specified by ISO/IEC 24747:2009. */ +#ifndef __STDC_WANT_MATH_SPEC_FUNCS__ +# undef __STDC_WANT_MATH_SPEC_FUNCS__ +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable X/Open extensions. Define to 500 only if necessary + to make mbstate_t available. */ +#ifndef _XOPEN_SOURCE +# undef _XOPEN_SOURCE +#endif + + /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD @@ -198,6 +298,12 @@ along with GNU Emacs. If not, see . */ /* Define as a signed integer type capable of holding a process identifier. */ #undef pid_t +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to `int' if does not define. */ +#undef ssize_t + /* Define to the type of an unsigned integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #undef uint16_t diff --git a/exec/configure.ac b/exec/configure.ac index b948e184896..efefc6c7dbc 100644 --- a/exec/configure.ac +++ b/exec/configure.ac @@ -47,6 +47,7 @@ [Generate library which can be used within a signal handler.])], [AC_DEFINE([REENTRANT], [1])]) +AC_USE_SYSTEM_EXTENSIONS AC_PROG_CC AC_PROG_CPP AC_PROG_INSTALL @@ -56,12 +57,22 @@ AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_UINTPTR_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T AC_TYPE_PID_T AC_HEADER_STDBOOL AC_CHECK_FUNCS([getpagesize stpcpy stpncpy]) AC_CHECK_DECLS([stpcpy, stpncpy]) -AC_CHECK_HEADERS([sys/param.h]) dnl for MIN and MAX +AC_CHECK_FUNC([process_vm_readv], + [AC_CHECK_FUNC([process_vm_writev], + [AC_CHECK_DECL([process_vm_readv], + [AC_DEFINE([HAVE_PROCESS_VM], [1], + [Define to 1 if process_vm_readv is available.])], + [], [[ +#include + ]])])]) +AC_CHECK_HEADERS([sys/param.h sys/uio.h]) AH_BOTTOM([ #ifdef HAVE_STDBOOL_H diff --git a/exec/trace.c b/exec/trace.c index cb0839c9cd9..8d107696423 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -42,6 +42,10 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include /* for NT_* */ #endif /* __aarch64__ */ +#ifdef HAVE_SYS_UIO_H +#include /* for process_vm_readv */ +#endif /* HAVE_SYS_UIO_H */ + /* Program tracing functions. @@ -122,7 +126,10 @@ aarch64_set_regs (pid_t pid, USER_REGS_STRUCT *regs, /* Read N bytes from TRACEE's memory, starting at the specified user - ADDRESS. Return its contents in BUFFER. */ + ADDRESS. Return its contents in BUFFER. + + If there are unreadable pages within ADDRESS + N, the contents of + BUFFER after the first such page becomes undefined. */ static void read_memory (struct exec_tracee *tracee, char *buffer, @@ -130,6 +137,25 @@ read_memory (struct exec_tracee *tracee, char *buffer, { USER_WORD word, n_words, n_bytes, i; long rc; +#ifdef HAVE_PROCESS_VM + struct iovec iov, remote; + + /* If `process_vm_readv' is available, use it instead. */ + + iov.iov_base = buffer; + iov.iov_len = n; + remote.iov_base = (void *) address; + remote.iov_len = n; + + /* Return immediately if successful. As long as some bytes were + read, consider the read to have been a success. */ + + if (n <= SSIZE_MAX + && ((size_t) process_vm_readv (tracee->pid, &iov, 1, + &remote, 1, 0) != -1)) + return; + +#endif /* HAVE_PROCESS_VM */ /* First, read entire words from the tracee. */ n_words = n & ~(sizeof (USER_WORD) - 1); @@ -248,6 +274,22 @@ user_copy (struct exec_tracee *tracee, const unsigned char *buffer, { USER_WORD start, end, word; unsigned char *bytes; +#ifdef HAVE_PROCESS_VM + struct iovec iov, remote; + + /* Try to use `process_vm_writev' if possible, but fall back to + ptrace if something bad happens. */ + + iov.iov_base = (void *) buffer; + iov.iov_len = n; + remote.iov_base = (void *) address; + remote.iov_len = n; + + if (n <= SSIZE_MAX + && ((size_t) process_vm_writev (tracee->pid, &iov, 1, + &remote, 1, 0) == n)) + return 0; +#endif /* HAVE_PROCESS_VM */ /* Calculate the start and end positions for the write. */ commit 35eae084bcd2ece057e2e5fa89a11281c40e51f7 Author: Po Lu Date: Wed May 3 09:23:06 2023 +0800 Remove extra debugging code * exec/loader-mipsel.s (__start): Remove extraneous debugging code. diff --git a/exec/loader-mipsel.s b/exec/loader-mipsel.s index 8537a0d2fe2..baba3f05a94 100644 --- a/exec/loader-mipsel.s +++ b/exec/loader-mipsel.s @@ -24,10 +24,10 @@ include(`config-mips.m4') .section .text .global __start __start: - li $v0, SYSCALL_nanosleep # SYS_nanosleep - la $a0, .timespec # rqtp - li $a1, 0 # rmtp - syscall # syscall +dnl li $v0, SYSCALL_nanosleep # SYS_nanosleep +dnl la $a0, .timespec # rqtp +dnl li $a1, 0 # rmtp +dnl syscall # syscall lw $s6, ($sp) # original stack pointer addi $s0, $sp, 8 # start of load area addi $sp, -8 # primary fd, secondary fd commit d5b92bce5b5732c43f277e243c305020b9ba1aba Author: Po Lu Date: Wed May 3 07:53:38 2023 +0800 Update Android port * exec/Makefile.in: (.PHONY): Add `bootstrap-clean' and `extraclean'. (bootstrap-clean): New rule. diff --git a/exec/Makefile.in b/exec/Makefile.in index ae6bdf00415..b2f134e85e5 100644 --- a/exec/Makefile.in +++ b/exec/Makefile.in @@ -114,7 +114,7 @@ exec1: # Set up targets for cleaning. -.PHONY: clean distclean maintainer-clean +.PHONY: clean distclean maintainer-clean extraclean bootstrap-clean clean: rm -f *.o *.a loader test *.s.s ifeq ($(AUTO_DEPEND),yes) @@ -137,3 +137,4 @@ extraclean: -[ "$(srcdir)" = "." ] || \ find $(srcdir) '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE) -find . '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE) +bootstrap-clean: extraclean commit 258c98240d27ce5e5192bc7dd8e2aa6072af41f4 Author: Po Lu Date: Wed May 3 07:50:29 2023 +0800 Update Android port * java/Makefile.in (FIND_DELETE): New substitution. (clean): Use FIND_DELETE. diff --git a/java/Makefile.in b/java/Makefile.in index 4e137157b69..3b06fc1d4cc 100644 --- a/java/Makefile.in +++ b/java/Makefile.in @@ -46,6 +46,7 @@ ANDROID_SDK_18_OR_EARLIER = ANDROID_SDK_8_OR_EARLIER = @ANDROID_SDK_8_OR_EARLIER@ WARN_JAVAFLAGS = @WARN_JAVAFLAGS@ JAVAFLAGS = $(WARN_JAVAFLAGS) -classpath "$(ANDROID_JAR):$(srcdir)" +FIND_DELETE = @FIND_DELETE@ # Android 4.3 and earlier require Emacs to be signed with a different # digital signature algorithm. @@ -323,7 +324,7 @@ TAGS: clean: rm -f *.apk emacs.apk-in *.dex *.unaligned *.class *.idsig rm -rf install-temp $(RESOURCE_FILE) TAGS - find . -name '*.class' -delete + find . -name '*.class' $(FIND_DELETE) maintainer-clean distclean bootstrap-clean: clean rm -f Makefile ndk-build.mk commit 9283471cb48e995f30985bd72b0b234ae6f513f3 Merge: f4a5e6d0e5c fa33a14ebe5 Author: Po Lu Date: Wed May 3 07:45:49 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit f4a5e6d0e5ce05cf97afb0ddb45dc0c0c2ea23a7 Author: Po Lu Date: Tue May 2 21:13:42 2023 +0800 * doc/emacs/android.texi (Android Environment): Improve doc. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index e1c644d6043..1f609b4ecb0 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -276,14 +276,22 @@ Android Environment @cindex call-process, Android @vindex android-use-exec-loader - Android 10 and later versions of the system also prohibit Emacs -itself from running executables inside the app data directory. On -these systems, Emacs normally applies a workaround; however, this -workaround requires running all sub-processes in another subprocess, -and applying process tracing to all executables, which may prove to be -problematic for various different reasons. In that case, the -workaround can be disabled by changing the variable -@code{android-use-exec-loader} to @code{nil}. + Android 10 and later also prohibit Emacs itself from running +executables inside the app data directory, obstensibly for security +readers. On these systems, Emacs normally applies a workaround; +however, this workaround requires running all sub-processes through +another subprocess which implements an executable loader and applies +process tracing to all its children, which may prove to be problematic +for various different reasons. In that case, the workaround can be +disabled by changing the variable @code{android-use-exec-loader} to +@code{nil}. + + When this workaround is in effect, process IDs retrieved through the +@code{process-id} function will be that of the executable loader +process; its child will belong to the same process group as the +loader. As a result, @code{interrupt-process}, and other related +functions will work correctly, but using the process ID returned by +@code{process-id} for other purposes will not. @section Running Emacs in the background @cindex emacs killed, android commit c47716f95b8fda9438047d2683a415a65c18ecbd Author: Po Lu Date: Tue May 2 20:45:57 2023 +0800 Update Android port * exec/config.h.in (__bool_true_false_are_defined): * exec/configure.ac (REENTRANT): New definition. (READLINKAT_SYSCALL, READLINK_SYSCALL): New defines. Set on all hosts. * exec/exec.c (MIN, MAX): Remove redundant declarations. Move to config.h. (exec_0): Copy name of executable into NAME when !REENTRANT. * exec/exec.h (struct exec_tracee): New struct `exec_file'. * exec/trace.c (remove_tracee, handle_exec, handle_readlinkat) (process_system_call, after_fork): Handle readlinkat system calls. diff --git a/exec/config.h.in b/exec/config.h.in index d8c225b631d..c360e54a4ba 100644 --- a/exec/config.h.in +++ b/exec/config.h.in @@ -76,6 +76,9 @@ along with GNU Emacs. If not, see . */ /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_PARAM_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H @@ -115,6 +118,15 @@ along with GNU Emacs. If not, see . */ /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* Define to number of the `readlinkat' system call. */ +#undef READLINKAT_SYSCALL + +/* Define to number of the `readlink' system call. */ +#undef READLINK_SYSCALL + +/* Define to 1 if the library is used within a signal handler. */ +#undef REENTRANT + /* Define to 1 if the stack grows downwards. */ #undef STACK_GROWS_DOWNWARDS @@ -129,6 +141,12 @@ along with GNU Emacs. If not, see . */ /* Define to register holding arg1 to system calls. */ #undef SYSCALL_ARG1_REG +/* Define to register holding arg2 to system calls. */ +#undef SYSCALL_ARG2_REG + +/* Define to register holding arg3 to system calls. */ +#undef SYSCALL_ARG3_REG + /* Define to register holding arg0 to system calls. */ #undef SYSCALL_ARG_REG @@ -217,3 +235,15 @@ typedef bool _Bool; # define __bool_true_false_are_defined 1 #endif +#ifdef HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif /* MAX */ + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /* MIN */ + diff --git a/exec/configure.ac b/exec/configure.ac index f3eef60173c..b948e184896 100644 --- a/exec/configure.ac +++ b/exec/configure.ac @@ -42,6 +42,11 @@ You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */]) +AC_ARG_WITH([reentrancy], + [AS_HELP_STRING([--with-reentrancy], + [Generate library which can be used within a signal handler.])], + [AC_DEFINE([REENTRANT], [1])]) + AC_PROG_CC AC_PROG_CPP AC_PROG_INSTALL @@ -56,6 +61,7 @@ AC_HEADER_STDBOOL AC_CHECK_FUNCS([getpagesize stpcpy stpncpy]) AC_CHECK_DECLS([stpcpy, stpncpy]) +AC_CHECK_HEADERS([sys/param.h]) dnl for MIN and MAX AH_BOTTOM([ #ifdef HAVE_STDBOOL_H @@ -73,6 +79,18 @@ # define true 1 # define __bool_true_false_are_defined 1 #endif + +#ifdef HAVE_SYS_PARAM_H +#include +#endif /* HAVE_SYS_PARAM_H */ + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif /* MAX */ + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /* MIN */ ]) AC_C_BIGENDIAN @@ -83,6 +101,8 @@ AH_TEMPLATE([SYSCALL_NUM_REG], [Define to register holding the system call number.]) AH_TEMPLATE([SYSCALL_ARG_REG], [Define to register holding arg0 to system calls.]) AH_TEMPLATE([SYSCALL_ARG1_REG], [Define to register holding arg1 to system calls.]) +AH_TEMPLATE([SYSCALL_ARG2_REG], [Define to register holding arg2 to system calls.]) +AH_TEMPLATE([SYSCALL_ARG3_REG], [Define to register holding arg3 to system calls.]) AH_TEMPLATE([SYSCALL_RET_REG], [Define to register holding value of system calls.]) AH_TEMPLATE([STACK_POINTER], [Define to register holding the stack pointer.]) AH_TEMPLATE([EXEC_SYSCALL], [Define to number of the `exec' system call.]) @@ -94,6 +114,9 @@ AH_TEMPLATE([INTERPRETER_BASE], [Virtual address for loading PIC interpreters]) AH_TEMPLATE([CLONE_SYSCALL], [Define to number of the `clone' system call.]) AH_TEMPLATE([CLONE3_SYSCALL], [Define to number of the `clone3' system call.]) +AH_TEMPLATE([READLINK_SYSCALL], [Define to number of the `readlink' system call.]) +AH_TEMPLATE([READLINKAT_SYSCALL], [Define to number of the `readlinkat' system call.]) +AH_TEMPLATE([REENTRANT], [Define to 1 if the library is used within a signal handler.]) AC_CANONICAL_HOST @@ -206,6 +229,8 @@ AC_DEFUN AC_DEFINE([SYSCALL_RET_REG], [rax]) AC_DEFINE([SYSCALL_ARG_REG], [rdi]) AC_DEFINE([SYSCALL_ARG1_REG], [rsi]) + AC_DEFINE([SYSCALL_ARG2_REG], [rdx]) + AC_DEFINE([SYSCALL_ARG3_REG], [r10]) AC_DEFINE([STACK_POINTER], [rsp]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) @@ -215,6 +240,8 @@ AC_DEFUN AC_DEFINE([INTERPRETER_BASE], [0x600000000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) + AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) exec_CHECK_LINUX_CLONE3 # Make sure the loader doesn't conflict with other position # dependent code. @@ -232,6 +259,8 @@ AC_DEFUN AC_DEFINE([SYSCALL_RET_REG], [eax]) AC_DEFINE([SYSCALL_ARG_REG], [ebx]) AC_DEFINE([SYSCALL_ARG1_REG], [ecx]) + AC_DEFINE([SYSCALL_ARG2_REG], [edx]) + AC_DEFINE([SYSCALL_ARG3_REG], [esi]) AC_DEFINE([STACK_POINTER], [esp]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) @@ -239,6 +268,8 @@ AC_DEFUN AC_DEFINE([INTERPRETER_BASE], [0xaf000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) + AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) exec_CHECK_LINUX_CLONE3 # Make sure the loader doesn't conflict with other position # dependent code. @@ -256,6 +287,8 @@ AC_DEFUN AC_DEFINE([SYSCALL_RET_REG], [[regs[0]]]) AC_DEFINE([SYSCALL_ARG_REG], [[regs[0]]]) AC_DEFINE([SYSCALL_ARG1_REG], [[regs[1]]]) + AC_DEFINE([SYSCALL_ARG2_REG], [[regs[2]]]) + AC_DEFINE([SYSCALL_ARG3_REG], [[regs[3]]]) AC_DEFINE([STACK_POINTER], [sp]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) @@ -264,6 +297,8 @@ AC_DEFUN AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + # Note that aarch64 has no `readlink'. + AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) exec_CHECK_LINUX_CLONE3 # Make sure the loader doesn't conflict with other position # dependent code. ARM places rather significant restrictions on @@ -282,6 +317,8 @@ AC_DEFUN AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]]) AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]]) AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]]) + AC_DEFINE([SYSCALL_ARG2_REG], [[uregs[2]]]) + AC_DEFINE([SYSCALL_ARG3_REG], [[uregs[3]]]) AC_DEFINE([STACK_POINTER], [[uregs[13]]]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) @@ -289,6 +326,8 @@ AC_DEFUN AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) + AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) exec_CHECK_LINUX_CLONE3 LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" exec_loader=loader-armeabi.s], @@ -300,6 +339,8 @@ AC_DEFUN AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]]) AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]]) AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]]) + AC_DEFINE([SYSCALL_ARG2_REG], [[uregs[2]]]) + AC_DEFINE([SYSCALL_ARG3_REG], [[uregs[3]]]) AC_DEFINE([STACK_POINTER], [[uregs[13]]]) AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) @@ -307,6 +348,8 @@ AC_DEFUN AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) + AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) exec_CHECK_LINUX_CLONE3 LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" exec_loader=loader-armeabi.s], @@ -324,6 +367,8 @@ AC_DEFUN AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0 AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0 AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1 + AC_DEFINE([SYSCALL_ARG2_REG], [[gregs[4]]]) # a2 + AC_DEFINE([SYSCALL_ARG3_REG], [[gregs[5]]]) # a3 AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) @@ -331,6 +376,8 @@ AC_DEFUN AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) + AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) AC_CHECK_DECL([_MIPS_SIM], [exec_CHECK_MIPS_NABI], [AC_MSG_ERROR([_MIPS_SIM could not be determined]), [[ @@ -347,6 +394,8 @@ AC_DEFUN AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0 AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0 AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1 + AC_DEFINE([SYSCALL_ARG2_REG], [[gregs[4]]]) # a2 + AC_DEFINE([SYSCALL_ARG3_REG], [[gregs[5]]]) # a3 AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) AC_DEFINE([USER_WORD], [uintptr_t]) @@ -355,6 +404,8 @@ AC_DEFUN AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) + AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) AC_CACHE_CHECK([whether as understands `daddi'], [exec_cv_as_daddi], [exec_cv_as_daddi=no diff --git a/exec/exec.c b/exec/exec.c index 7f2cc75338b..17051428658 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -31,14 +31,6 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif /* MIN */ - -#ifndef MAX -#define MAX(a, b) ((a) < (b) ? (b) : (a)) -#endif /* MAX */ - #include "exec.h" #if defined __mips__ && !defined MIPS_NABI @@ -938,6 +930,10 @@ format_pid (char *in, unsigned int pid) with #!; in that case, find the program to open and use that instead. + If REENTRANT is not defined, NAME is actually a buffer of size + PATH_MAX + 80. In that case, copy over the file name actually + opened. + Next, read the executable header, and add the necessary memory mappings for each file. Finally, return the action data and its size in *SIZE. @@ -948,7 +944,7 @@ format_pid (char *in, unsigned int pid) Value is NULL upon failure, with errno set accordingly. */ char * -exec_0 (const char *name, struct exec_tracee *tracee, +exec_0 (char *name, struct exec_tracee *tracee, size_t *size, USER_REGS_STRUCT *regs) { int fd, rc, i; @@ -961,7 +957,8 @@ exec_0 (const char *name, struct exec_tracee *tracee, #if defined __mips__ && !defined MIPS_NABI int fpu_mode; #endif /* defined __mips__ && !defined MIPS_NABI */ - char buffer[PATH_MAX + 80], *rewrite; + char buffer[80], buffer1[PATH_MAX + 80], *rewrite; + ssize_t link_size; size_t remaining; /* If name is not absolute, then make it relative to TRACEE's @@ -971,18 +968,43 @@ exec_0 (const char *name, struct exec_tracee *tracee, { /* Clear `buffer'. */ memset (buffer, 0, sizeof buffer); + memset (buffer1, 0, sizeof buffer); /* Copy over /proc, the PID, and /cwd/. */ rewrite = stpcpy (buffer, "/proc/"); rewrite = format_pid (rewrite, tracee->pid); - rewrite = stpcpy (rewrite, "/cwd/"); + stpcpy (rewrite, "/cwd"); + + /* Resolve this symbolic link. */ + + link_size = readlink (buffer, buffer1, + PATH_MAX + 1); + + if (link_size < 0) + return NULL; + + /* Check that the name is a reasonable size. */ + + if (link_size > PATH_MAX) + { + /* The name is too long. */ + errno = ENAMETOOLONG; + return NULL; + } + + /* Add a directory separator if necessary. */ + + if (!link_size || buffer1[link_size - 1] != '/') + buffer1[link_size] = '/', link_size++; - /* Make sure there is enough free space. */ - remaining = buffer + sizeof buffer - rewrite - 1; + rewrite = buffer1 + link_size; + remaining = buffer1 + sizeof buffer1 - rewrite - 1; rewrite = stpncpy (rewrite, name, remaining); - /* Replace name with buffer. */ - name = buffer; + /* Replace name with buffer1. */ +#ifndef REENTRANT + strcpy (name, buffer1); +#endif /* REENTRANT */ } fd = open (name, O_RDONLY); diff --git a/exec/exec.h b/exec/exec.h index 0f6a8a893b6..625ad0bb219 100644 --- a/exec/exec.h +++ b/exec/exec.h @@ -154,6 +154,11 @@ #define _EXEC_H_ /* Whether or not the tracee is currently waiting for a system call to complete. */ bool waiting_for_syscall; + +#ifndef REENTRANT + /* Name of the executable being run. */ + char *exec_file; +#endif /* !REENTRANT */ }; @@ -184,7 +189,7 @@ #define _EXEC_H_ /* Defined in exec.c. */ -extern char *exec_0 (const char *, struct exec_tracee *, +extern char *exec_0 (char *, struct exec_tracee *, size_t *, USER_REGS_STRUCT *); diff --git a/exec/trace.c b/exec/trace.c index d9e8673ba71..cb0839c9cd9 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -315,6 +315,13 @@ remove_tracee (struct exec_tracee *tracee) /* Link the tracee onto the list of free tracees. */ tracee->next = free_tracees; + +#ifndef REENTRANT + /* Free the exec file, if any. */ + free (tracee->exec_file); + tracee->exec_file = NULL; +#endif /* REENTRANT */ + free_tracees = tracee; return; @@ -431,7 +438,7 @@ syscall_trap_p (siginfo_t *signal) static int handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) { - char buffer[PATH_MAX], *area; + char buffer[PATH_MAX + 80], *area; USER_REGS_STRUCT original; size_t size, loader_size; USER_WORD loader, size1, sp; @@ -517,6 +524,17 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) return 1; } +#ifndef REENTRANT + /* Now that the loader has started, record the value to use for + /proc/self/exe. Don't give up just because strdup fails. + + Note that exec_0 copies the absolute file name into buffer. */ + + if (tracee->exec_file) + free (tracee->exec_file); + tracee->exec_file = strdup (buffer); +#endif /* REENTRANT */ + again: rc = waitpid (tracee->pid, &wstatus, __WALL); if (rc == -1 && errno == EINTR) @@ -622,6 +640,91 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) return 3; } +/* Handle a `readlink' or `readlinkat' system call. + + CALLNO is the system call number, and REGS are the current user + registers of the TRACEE. + + If the first argument of a `readlinkat' system call is AT_FDCWD, + and the file name specified in either a `readlink' or `readlinkat' + system call is `/proc/self/exe', write the name of the executable + being run into the buffer specified in the system call. + + Return the number of bytes written to the tracee's buffer in + *RESULT. + + Value is 0 upon success. Value is 1 upon failure, and 2 if the + system call has been emulated. */ + +static int +handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs, + struct exec_tracee *tracee, USER_WORD *result) +{ +#ifdef REENTRANT + /* readlinkat cannot be handled specially when the library is built + to be reentrant, as the file name information cannot be + recorded. */ + return 0; +#else /* !REENTRANT */ + + char buffer[PATH_MAX + 1]; + USER_WORD address, return_buffer, size; + size_t length; + + /* Read the file name. */ + +#ifdef READLINK_SYSCALL + if (callno == READLINK_SYSCALL) + { + address = regs->SYSCALL_ARG_REG; + return_buffer = regs->SYSCALL_ARG1_REG; + size = regs->SYSCALL_ARG2_REG; + } + else +#endif /* READLINK_SYSCALL */ + { + address = regs->SYSCALL_ARG1_REG; + return_buffer = regs->SYSCALL_ARG2_REG; + size = regs->SYSCALL_ARG3_REG; + } + + read_memory (tracee, buffer, PATH_MAX, address); + + /* Make sure BUFFER is NULL terminated. */ + + if (!memchr (buffer, '\0', PATH_MAX)) + { + errno = ENAMETOOLONG; + return 1; + } + + /* Now check if the caller is looking for /proc/self/exe. + + dirfd can be ignored, as for now only absolute file names are + handled. FIXME. */ + + if (strcmp (buffer, "/proc/self/exe") || !tracee->exec_file) + return 0; + + /* Copy over tracee->exec_file. Truncate it to PATH_MAX, length, or + size, whichever is less. */ + + length = strlen (tracee->exec_file); + length = MIN (size, MIN (PATH_MAX, length)); + strncpy (buffer, tracee->exec_file, length); + + if (user_copy (tracee, (unsigned char *) buffer, + return_buffer, length)) + { + errno = EIO; + return 1; + } + + *result = length; + return 2; +#endif /* REENTRANT */ +} + /* Process the system call at which TRACEE is stopped. If the system call is not known or not exec, send TRACEE on its way. Otherwise, rewrite it to load the loader and perform an appropriate action. */ @@ -635,6 +738,8 @@ process_system_call (struct exec_tracee *tracee) #ifdef __aarch64__ USER_WORD old_w1, old_w2; #endif /* __aarch64__ */ + USER_WORD result; + bool reporting_error; #ifdef __aarch64__ rc = aarch64_get_regs (tracee->pid, ®s); @@ -678,6 +783,24 @@ process_system_call (struct exec_tracee *tracee) break; +#ifdef READLINK_SYSCALL + case READLINK_SYSCALL: +#endif /* READLINK_SYSCALL */ + case READLINKAT_SYSCALL: + + /* Handle this readlinkat system call. */ + rc = handle_readlinkat (callno, ®s, tracee, + &result); + + /* rc means the same as in `handle_exec'. */ + + if (rc == 1) + goto report_syscall_error; + else if (rc == 2) + goto emulate_syscall; + + /* Fallthrough. */ + default: /* Don't wait for the system call to finish; instead, the system will DTRT upon the next call to PTRACE_SYSCALL after the @@ -694,8 +817,16 @@ process_system_call (struct exec_tracee *tracee) return; report_syscall_error: - /* Reporting an error works by setting the system call number to -1, - letting it continue, and then substituting errno for ENOSYS. + reporting_error = true; + goto common; + + emulate_syscall: + reporting_error = false; + common: + + /* Reporting an error or emulating a system call works by setting + the system call number to -1, letting it continue, and then + substituting errno for ENOSYS in the case of an error. Make sure that the stack pointer is restored to its original position upon exit, or bad things can happen. */ @@ -755,7 +886,7 @@ process_system_call (struct exec_tracee *tracee) /* The process has been killed in response to a signal. In this case, simply unlink the tracee and return. */ remove_tracee (tracee); - else + else if (reporting_error) { #ifdef __mips__ /* MIPS systems place errno in v0 and set a3 to 1. */ @@ -775,6 +906,32 @@ process_system_call (struct exec_tracee *tracee) ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s); #endif /* __aarch64__ */ + /* Now wait for the next system call to happen. */ + ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL); + } + else + { + /* No error is being reported. Return the result in the + appropriate registers. */ + +#ifdef __mips__ + /* MIPS systems place errno in v0 and set a3 to 1. */ + regs.gregs[2] = result; + regs.gregs[7] = 0; +#else /* !__mips__ */ + regs.SYSCALL_RET_REG = result; +#endif /* __mips__ */ + + /* Report errno. */ +#ifdef __aarch64__ + /* Restore x1 and x2. x0 is clobbered by errno. */ + regs.regs[1] = old_w1; + regs.regs[2] = old_w2; + aarch64_set_regs (tracee->pid, ®s, false); +#else /* !__aarch64__ */ + ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s); +#endif /* __aarch64__ */ + /* Now wait for the next system call to happen. */ ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL); } @@ -869,6 +1026,9 @@ after_fork (pid_t pid) tracee->pid = pid; tracee->next = tracing_processes; tracee->waiting_for_syscall = false; +#ifndef REENTRANT + tracee->exec_file = NULL; +#endif /* REENTRANT */ tracing_processes = tracee; return 0; } commit f4512cca0b996e5343ebe57511f45a29f64c4a8e Author: Po Lu Date: Tue May 2 16:44:46 2023 +0800 Fix ps name in Android subprocesses * exec/Makefile.in (.SUFFIXES): Include ., then `srcdir'. * exec/loader-aarch64.s (_start): * exec/loader-armeabi.s (_start): * exec/loader-mips64el.s (__start): * exec/loader-mipsel.s (__start): * exec/loader-x86.s (_start): * exec/loader-x86_64.s (_start): Get basename of opened exec file and make it the command name. Fix envp skipping on x86 and various leaks. diff --git a/exec/Makefile.in b/exec/Makefile.in index 365dc42e0b7..ae6bdf00415 100644 --- a/exec/Makefile.in +++ b/exec/Makefile.in @@ -81,7 +81,7 @@ Makefile: .SUFFIXES: .c .s .c.o: - $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -I$(srcdir) -I. $< -o $@ + $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -I. -I$(srcdir) $< -o $@ .s.o: $(M4) $< > $(notdir $<).s $(AS) $(ASFLAGS) $(notdir $<).s -o $@ diff --git a/exec/loader-aarch64.s b/exec/loader-aarch64.s index 1b99d238b92..da8ec1f4977 100644 --- a/exec/loader-aarch64.s +++ b/exec/loader-aarch64.s @@ -92,14 +92,27 @@ _start: svc #0 // syscall cmp x0, #-1 // rc < 0? ble .perror + mov x19, x1 // x19 == x1 .nextc: ldrb w2, [x1], #1 // b = *x1++ + cmp w2, #47 // dir separator? + bne .nextc1 // not dir separator + mov x19, x1 // x19 = char past separator +.nextc1: cbnz w2, .nextc // b? add x1, x1, #7 // round up x1 and x20, x1, #-8 // mask for round, set x20 tst x11, #16 // primary fd? bne .secondary // secondary fd mov x29, x0 // primary fd + mov x8, #167 // SYS_prctl + mov x0, #15 // PR_SET_NAME + mov x1, x19 // basename + mov x2, #0 // arg2 + mov x3, #0 // arg3 + mov x4, #0 // arg4 + mov x5, #0 // arg5 + svc #0 // syscall b .next_action // next action .secondary: mov x28, x0 // secondary fd diff --git a/exec/loader-armeabi.s b/exec/loader-armeabi.s index 182ff11ec7a..32b2a5268d6 100644 --- a/exec/loader-armeabi.s +++ b/exec/loader-armeabi.s @@ -104,15 +104,27 @@ _start: cmp r0, #-1 @ r0 <= -1? ble .perror add r8, r8, #4 @ r8 = start of string + mov r1, r8 @ r1 = r8 .nextc: - ldrb r1, [r8], #1 @ b = *r0++ - cmp r1, #0 @ b? + ldrb r2, [r8], #1 @ b = *r0++ + cmp r2, #47 @ dir separator? + bne .nextc1 @ not dir separator + mov r1, r8 @ r1 = char past separator +.nextc1: + cmp r2, #0 @ b? bne .nextc @ next character add r8, r8, #3 @ round up r8 and r8, r8, #-4 @ mask for round, set r8 tst r11, #16 @ primary fd? bne .secondary @ secondary fd mov r10, r0 @ primary fd + mov r7, #172 @ SYS_prctl + mov r0, #15 @ PR_SET_NAME, r1 = name + mov r2, #0 @ arg2 + mov r3, #0 @ arg3 + mov r4, #0 @ arg4 + mov r5, #0 @ arg5 + swi #0 @ syscall b .next_action @ next action .secondary: mov r14, r0 @ secondary fd diff --git a/exec/loader-mips64el.s b/exec/loader-mips64el.s index 73dc8c63fe8..00a2765a9b6 100644 --- a/exec/loader-mips64el.s +++ b/exec/loader-mips64el.s @@ -123,10 +123,16 @@ dnl syscall # syscall bne $a3, $zero, .perror # perror nop # delay slot DADDI2( $s0, 8) # start of string + move $t3, $s0 # t3 = s0 .nextc: lb $t0, ($s0) # load byte DADDI2( $s0, 1) # s0++ - bne $t0, $zero, .nextc # next character? + li $t1, 47 # directory separator `/' + bne $t0, $t1, .nextc1 # is separator char? + nop # delay slot + move $t3, $s0 # t3 = char past separator +.nextc1: + bnez $t0, .nextc # next character? nop # delay slot DADDI2( $s0, 7) # adjust for round li $t2, -8 # t2 = -8 @@ -136,8 +142,19 @@ dnl syscall # syscall beqz $t0, .primary # primary fd? nop # delay slot DADDI2( $t1, 8) # address of secondary fd + sd $v0, ($t1) # store fd + j .next_action # next action + nop # delay slot .primary: sd $v0, ($t1) # store fd + li $v0, 5153 # SYS_prctl + li $a0, 15 # PR_SET_NAME + move $a1, $t3 # char past separator + move $a2, $zero # a2 + move $a3, $zero # a3 + move $a4, $zero # a4 + move $a5, $zero # a5 + syscall # syscall j .next_action # next action nop # delay slot .perror: diff --git a/exec/loader-mipsel.s b/exec/loader-mipsel.s index 2ad9d97dfed..8537a0d2fe2 100644 --- a/exec/loader-mipsel.s +++ b/exec/loader-mipsel.s @@ -24,10 +24,10 @@ include(`config-mips.m4') .section .text .global __start __start: -dnl li $v0, SYSCALL_nanosleep # SYS_nanosleep -dnl la $a0, .timespec # rqtp -dnl li $a1, 0 # rmtp -dnl syscall # syscall + li $v0, SYSCALL_nanosleep # SYS_nanosleep + la $a0, .timespec # rqtp + li $a1, 0 # rmtp + syscall # syscall lw $s6, ($sp) # original stack pointer addi $s0, $sp, 8 # start of load area addi $sp, -8 # primary fd, secondary fd @@ -121,10 +121,16 @@ RESTORE() # delay slot, restore sp syscall # syscall bne $a3, $zero, .perror # perror addi $s0, $s0, 4 # start of string, delay slot + move $t3, $s0 # t3 = char past separator .nextc: lb $t0, ($s0) # load byte addi $s0, $s0, 1 # s0++ - bne $t0, $zero, .nextc # next character? + li $t1, 47 # directory separator `/' + bne $t0, $t1, .nextc1 # is separator char? + nop # delay slot + move $t3, $s0 # t3 = char past separator +.nextc1: + bnez $t0, .nextc # next character? nop # delay slot addi $s0, $s0, 3 # adjust for round li $t2, -4 # t2 = -4 @@ -133,8 +139,17 @@ RESTORE() # delay slot, restore sp beqz $t0, .primary # primary fd? move $t0, $sp # address of primary fd, delay slot addi $t0, $t0, 4 # address of secondary fd + j .next_action # next action .primary: - sw $v0, ($t0) # store fd + sw $v0, ($t0) # store fd, delay slot + li $v0, SYSCALL_prctl # SYS_prctl + li $a0, 15 # PR_SET_NAME + move $a1, $t3 # name + move $a2, $zero # arg1 + move $a3, $zero # arg2 +SYSCALL(`$a2',`$a2',`$a2',`$a2') # syscall args + syscall # syscall +RESTORE() # restore sp j .next_action # next action nop # delay slot .perror: diff --git a/exec/loader-x86.s b/exec/loader-x86.s index ee69b26d78b..6329e7f33b1 100644 --- a/exec/loader-x86.s +++ b/exec/loader-x86.s @@ -21,10 +21,10 @@ CC along with GNU Emacs. If not, see . .section .text .global _start _start: - #movl $162, %eax CC SYS_nanosleep - #leal timespec, %ebx - #xorl %ecx, %ecx - #int $0x80 +dnl movl $162, %eax CC SYS_nanosleep +dnl leal timespec, %ebx +dnl xorl %ecx, %ecx +dnl int $0x80 leal 8(%esp), %ebp CC ebp = start of load area subl $8, %esp CC (%esp) = primary fd, 4(%esp) = secondary fd movl $-1, 4(%esp) @@ -102,10 +102,16 @@ _start: jle .perror movl %ebp, %esi CC (esi) = original action number popl %ebp CC ebp = start of string + movl %ebp, %ecx CC char past separator decl %ebp .nextc: incl %ebp - cmpb $0, (%ebp) CC *ebp == 0? + movb (%ebp), %dl CC dl = *ebp + cmpb $47, %dl CC dl == '\?'? + jne .nextc1 + leal 1(%ebp), %ecx CC ecx = char past separator +.nextc1: + cmpb $0, %dl CC dl == 0? jne .nextc addl $4, %ebp CC adjust past ebp prior to rounding andl $-4, %ebp CC round ebp up to the next long @@ -114,7 +120,16 @@ _start: movl %eax, 4(%esp) CC secondary fd = eax jmp .next_action .primary: - movl %eax, (%esp) CC primary fd = eax + pushl %ebp + xorl %esi, %esi CC arg3 + movl %eax, 4(%esp) CC primary fd = eax + xorl %edx, %edx CC arg2 + movl $15, %ebx CC PR_SET_NAME, arg1 = ecx + xorl %edi, %edi CC arg4 + movl $172, %eax CC SYS_prctl + xorl %ebp, %ebp CC arg5 + int $0x80 CC syscall + popl %ebp jmp .next_action .perror: movl %eax, %ebx @@ -127,7 +142,7 @@ _start: leal 8(%ecx, %esi, 4), %ecx CC ecx = start of environ .skip_environ: movl (%ecx), %esi CC envp[N] - subl $4, %ecx + addl $4, %ecx testl %esi, %esi CC envp[n] ? jnz .skip_environ CC otherwise, esi is now at the start of auxv .one_auxv: @@ -168,12 +183,12 @@ _start: jmp .one_auxv .cleanup: movl $6, %eax CC SYS_close - cmpl $1, -4(%esp) CC see if interpreter fd is set - jne .cleanup_1 - movl -4(%esp), %ebx + cmpl $-1, 4(%esp) CC see if interpreter fd is set + je .cleanup_1 + movl 4(%esp), %ebx int $0x80 -.cleanup_1: movl $6, %eax CC SYS_close +.cleanup_1: movl (%esp), %ebx int $0x80 .enter: diff --git a/exec/loader-x86_64.s b/exec/loader-x86_64.s index 07227d38396..acba609b202 100644 --- a/exec/loader-x86_64.s +++ b/exec/loader-x86_64.s @@ -21,10 +21,10 @@ CC along with GNU Emacs. If not, see . .section .text .global _start _start: - #movq $35, %rax CC SYS_nanosleep - #leaq timespec(%rip), %rdi - #xorq %rsi, %rsi - #syscall +dnl movq $35, %rax CC SYS_nanosleep +dnl leaq timespec(%rip), %rdi +dnl xorq %rsi, %rsi +dnl syscall popq %r13 CC original SP popq %r15 CC size of load area. movq $-1, %r12 CC r12 is the interpreter fd @@ -87,9 +87,16 @@ _start: jle .perror movq %rdi, %rsp CC rsp = start of string subq $1, %rsp + movq %rsp, %r14 CC r14 = start of string .nextc: addq $1, %rsp - cmpb $0, (%rsp) CC *rsp == 0? + movb (%rsp), %dil CC rdi = *rsp + cmpb $47, %dil CC *rsp == '/'? + jne .nextc1 + movq %rsp, %r14 CC r14 = rsp + addq $1, %r14 CC r14 = char past separator +.nextc1: + cmpb $0, %dil CC *rsp == 0? jne .nextc addq $8, %rsp CC adjust past rsp prior to rounding andq $-8, %rsp CC round rsp up to the next quad @@ -99,6 +106,14 @@ _start: jmp .next_action .primary: movq %rax, %rbx CC if not, move fd to rbx + movq $157, %rax CC SYS_prctl + movq $15, %rdi CC PR_SET_NAME + movq %r14, %rsi CC arg1 + xorq %rdx, %rdx CC arg2 + xorq %r10, %r10 CC arg3 + xorq %r8, %r8 CC arg4 + xorq %r9, %r9 CC arg5 + syscall jmp .next_action .perror: movq %rax, %r12 CC error code @@ -159,11 +174,11 @@ _start: .cleanup: movq $3, %rax CC SYS_close cmpq $-1, %r12 CC see if interpreter fd is set - jne .cleanup_1 + je .cleanup_1 movq %r12, %rdi syscall -.cleanup_1: movq $3, %rax CC SYS_close +.cleanup_1: movq %rbx, %rdi syscall .enter: commit 5a58a6bc477f290ee0b8a6111e92df56ff538719 Author: Po Lu Date: Tue May 2 08:16:00 2023 +0800 Port Android port to older Android systems * exec/config.h.in: Autoheader. * exec/configure.ac: Check for declarations of stpcpy and stpncpy. * exec/exec.c (stpcpy, stpncpy): Use replacements if declarations are not present; this happens when a new Android NDK is building for an old version of Android. diff --git a/exec/config.h.in b/exec/config.h.in index c276ff3f1f7..d8c225b631d 100644 --- a/exec/config.h.in +++ b/exec/config.h.in @@ -38,6 +38,14 @@ along with GNU Emacs. If not, see . */ /* Define to number of the `exec' system call. */ #undef EXEC_SYSCALL +/* Define to 1 if you have the declaration of `stpcpy', and to 0 if you don't. + */ +#undef HAVE_DECL_STPCPY + +/* Define to 1 if you have the declaration of `stpncpy', and to 0 if you + don't. */ +#undef HAVE_DECL_STPNCPY + /* Define to 1 if you have the `getpagesize' function. */ #undef HAVE_GETPAGESIZE diff --git a/exec/configure.ac b/exec/configure.ac index 9b4bcebe34e..f3eef60173c 100644 --- a/exec/configure.ac +++ b/exec/configure.ac @@ -55,6 +55,7 @@ AC_HEADER_STDBOOL AC_CHECK_FUNCS([getpagesize stpcpy stpncpy]) +AC_CHECK_DECLS([stpcpy, stpncpy]) AH_BOTTOM([ #ifdef HAVE_STDBOOL_H diff --git a/exec/exec.c b/exec/exec.c index df8c9430236..7f2cc75338b 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -50,7 +50,7 @@ #define MAX(a, b) ((a) < (b) ? (b) : (a)) /* Define replacements for required string functions. */ -#ifndef HAVE_STPCPY +#if !defined HAVE_STPCPY || !defined HAVE_DECL_STPCPY /* Copy SRC to DEST, returning the address of the terminating '\0' in DEST. */ @@ -72,14 +72,14 @@ rpl_stpcpy (char *dest, const char *src) } #define stpcpy rpl_stpcpy -#endif /* !HAVE_STPCPY */ +#endif /* !defined HAVE_STPCPY || !defined HAVE_DECL_STPCPY */ -#ifndef HAVE_STPNCPY +#if !defined HAVE_STPNCPY || !defined HAVE_DECL_STPNCPY /* Copy no more than N bytes of SRC to DST, returning a pointer past the last non-NUL byte written into DST. */ -char * +static char * rpl_stpncpy (char *dest, const char *src, size_t n) { char c, *s; @@ -140,7 +140,7 @@ rpl_stpncpy (char *dest, const char *src, size_t n) } #define stpncpy rpl_stpncpy -#endif /* !HAVE_STPNCPY */ +#endif /* !defined HAVE_STPNCPY || !defined HAVE_DECL_STPNCPY */ commit 86b7ed619c7d7da035915b8c75b427ed66801123 Merge: f92bdbc6775 7d246c359cf Author: Po Lu Date: Tue May 2 08:09:17 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit f92bdbc67754c77afcae95cb1ab237e2c5053ab2 Author: Po Lu Date: Mon May 1 21:42:42 2023 +0800 Update Android port * exec/config.h.in: Update config.h.in. * exec/configure.ac: Check for stpcpy and stpncpy. * exec/exec.c (rpl_stpcpy, rpl_stpncpy): Define replacements when they are not present on the system. (process_program_header): Fill comment. diff --git a/exec/config.h.in b/exec/config.h.in index d602d89a38e..c276ff3f1f7 100644 --- a/exec/config.h.in +++ b/exec/config.h.in @@ -38,6 +38,9 @@ along with GNU Emacs. If not, see . */ /* Define to number of the `exec' system call. */ #undef EXEC_SYSCALL +/* Define to 1 if you have the `getpagesize' function. */ +#undef HAVE_GETPAGESIZE + /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H @@ -53,6 +56,12 @@ along with GNU Emacs. If not, see . */ /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H +/* Define to 1 if you have the `stpcpy' function. */ +#undef HAVE_STPCPY + +/* Define to 1 if you have the `stpncpy' function. */ +#undef HAVE_STPNCPY + /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H diff --git a/exec/configure.ac b/exec/configure.ac index 9763edc99f3..9b4bcebe34e 100644 --- a/exec/configure.ac +++ b/exec/configure.ac @@ -54,7 +54,7 @@ AC_TYPE_PID_T AC_HEADER_STDBOOL -AC_CHECK_FUNC([getpagesize]) +AC_CHECK_FUNCS([getpagesize stpcpy stpncpy]) AH_BOTTOM([ #ifdef HAVE_STDBOOL_H diff --git a/exec/exec.c b/exec/exec.c index c7a73f221f5..df8c9430236 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -21,7 +21,6 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include -#include #include #include #include @@ -48,6 +47,103 @@ #define MAX(a, b) ((a) < (b) ? (b) : (a)) + +/* Define replacements for required string functions. */ + +#ifndef HAVE_STPCPY + +/* Copy SRC to DEST, returning the address of the terminating '\0' in + DEST. */ + +static char * +rpl_stpcpy (char *dest, const char *src) +{ + register char *d; + register const char *s; + + d = dest; + s = src; + + do + *d++ = *s; + while (*s++ != '\0'); + + return d - 1; +} + +#define stpcpy rpl_stpcpy +#endif /* !HAVE_STPCPY */ + +#ifndef HAVE_STPNCPY + +/* Copy no more than N bytes of SRC to DST, returning a pointer past + the last non-NUL byte written into DST. */ + +char * +rpl_stpncpy (char *dest, const char *src, size_t n) +{ + char c, *s; + size_t n4; + + s = dest; + + if (n >= 4) + { + n4 = n >> 2; + + for (;;) + { + c = *src++; + *dest++ = c; + if (c == '\0') + break; + c = *src++; + *dest++ = c; + if (c == '\0') + break; + c = *src++; + *dest++ = c; + if (c == '\0') + break; + c = *src++; + *dest++ = c; + if (c == '\0') + break; + if (--n4 == 0) + goto last_chars; + } + n -= dest - s; + goto zero_fill; + } + + last_chars: + n &= 3; + if (n == 0) + return dest; + + for (;;) + { + c = *src++; + --n; + *dest++ = c; + if (c == '\0') + break; + if (n == 0) + return dest; + } + + zero_fill: + while (n-- > 0) + dest[n] = '\0'; + + return dest - 1; +} + +#define stpncpy rpl_stpncpy +#endif /* !HAVE_STPNCPY */ + + + /* Executable reading functions. These functions extract information from an executable that is about to be loaded. @@ -624,9 +720,8 @@ process_program_header (const char *name, int fd, break; case 3: /* PT_INTERP */ - /* This describes another executable that must be loaded. - Open the interpreter and process each of its headers - as well. */ + /* This describes another executable that must be loaded. Open + the interpreter and process each of its headers as well. */ rc = process_interpreter (fd, header, entry); break; commit 8a909927995738cf1103198f8086a42bf28a1d50 Author: Po Lu Date: Mon May 1 21:28:40 2023 +0800 ; * src/term.c (syms_of_term): Pretend Android uses TERMINFO. diff --git a/src/term.c b/src/term.c index 2fa06d71f03..3b5619ff93e 100644 --- a/src/term.c +++ b/src/term.c @@ -4660,7 +4660,7 @@ syms_of_term (void) DEFVAR_BOOL ("system-uses-terminfo", system_uses_terminfo, doc: /* Non-nil means the system uses terminfo rather than termcap. This variable can be used by terminal emulator packages. */); -#ifdef TERMINFO +#if defined TERMINFO || (defined HAVE_ANDROID && !defined ANDROID_STUBIFY) system_uses_terminfo = 1; #else system_uses_terminfo = 0; commit b9de6e35b79cbc10909a856df6b1caa770bd4ac4 Author: Po Lu Date: Mon May 1 21:23:12 2023 +0800 Fix cwd relative process execution on Android * exec/exec.c (format_pid): New function. (exec_0): Make cwd relative file names relative to /proc/pid/cwd. * exec/trace.c (handle_exec): Handle EINTR. (process_system_call): Report failure without clobbering x0. diff --git a/exec/exec.c b/exec/exec.c index 662c8bf69d2..c7a73f221f5 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -26,6 +26,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include #include +#include #include #include @@ -808,6 +809,35 @@ insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, +/* Format PID, an unsigned process identifier, in base 10. Place the + result in *IN, and return a pointer to the byte after the + result. REM should be NULL. */ + +static char * +format_pid (char *in, unsigned int pid) +{ + unsigned int digits[32], *fill; + + fill = digits; + + for (; pid != 0; pid = pid / 10) + *fill++ = pid % 10; + + /* Insert 0 if the number would otherwise be empty. */ + + if (fill == digits) + *fill++ = 0; + + while (fill != digits) + { + --fill; + *in++ = '0' + *fill; + } + + *in = '\0'; + return in; +} + /* Return a sequence of actions required to load the executable under the file NAME for the given TRACEE. First, see if the file starts with #!; in that case, find the program to open and use that @@ -836,6 +866,29 @@ exec_0 (const char *name, struct exec_tracee *tracee, #if defined __mips__ && !defined MIPS_NABI int fpu_mode; #endif /* defined __mips__ && !defined MIPS_NABI */ + char buffer[PATH_MAX + 80], *rewrite; + size_t remaining; + + /* If name is not absolute, then make it relative to TRACEE's + cwd. Use stpcpy, as sprintf is not reentrant. */ + + if (name[0] && name[0] != '/') + { + /* Clear `buffer'. */ + memset (buffer, 0, sizeof buffer); + + /* Copy over /proc, the PID, and /cwd/. */ + rewrite = stpcpy (buffer, "/proc/"); + rewrite = format_pid (rewrite, tracee->pid); + rewrite = stpcpy (rewrite, "/cwd/"); + + /* Make sure there is enough free space. */ + remaining = buffer + sizeof buffer - rewrite - 1; + rewrite = stpncpy (rewrite, name, remaining); + + /* Replace name with buffer. */ + name = buffer; + } fd = open (name, O_RDONLY); if (fd < 0) diff --git a/exec/trace.c b/exec/trace.c index df5deacd9bb..d9e8673ba71 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -457,10 +457,17 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) memcpy (&original, regs, sizeof *regs); /* Figure out what the loader needs to do. */ + again1: area = exec_0 (buffer, tracee, &size, regs); if (!area) - return 1; + { + /* Handle SIGINTR errors caused by IO. */ + if (errno == EINTR) + goto again1; + + return 1; + } /* Rewrite the first argument to point to the loader. */ @@ -516,10 +523,7 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) goto again; if (rc < 0) - { - errno = EIO; - return 1; - } + return 1; if (!WIFSTOPPED (wstatus)) /* The process has been killed in response to a signal. @@ -608,13 +612,14 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) #endif /* STACK_GROWS_DOWNWARDS */ - exec_failure: - /* Continue. */ if (ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0)) return 3; return 0; + + exec_failure: + return 3; } /* Process the system call at which TRACEE is stopped. If the system @@ -625,10 +630,10 @@ handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) process_system_call (struct exec_tracee *tracee) { USER_REGS_STRUCT regs; - int rc, wstatus; + int rc, wstatus, save_errno; USER_WORD callno, sp; #ifdef __aarch64__ - USER_WORD old_w0, old_w1, old_w2; + USER_WORD old_w1, old_w2; #endif /* __aarch64__ */ #ifdef __aarch64__ @@ -695,6 +700,9 @@ process_system_call (struct exec_tracee *tracee) Make sure that the stack pointer is restored to its original position upon exit, or bad things can happen. */ + /* First, save errno; system calls below will clobber it. */ + save_errno = errno; + #ifndef __aarch64__ regs.SYSCALL_NUM_REG = -1; #else /* __aarch64__ */ @@ -702,7 +710,6 @@ process_system_call (struct exec_tracee *tracee) can't find any unused system call, so use fcntl instead, with invalid arguments. */ regs.SYSCALL_NUM_REG = 72; - old_w0 = regs.regs[0]; old_w1 = regs.regs[1]; old_w2 = regs.regs[2]; regs.regs[0] = -1; @@ -739,6 +746,11 @@ process_system_call (struct exec_tracee *tracee) if (rc == -1 && errno == EINTR) goto again1; + /* Return if waitpid fails. */ + + if (rc == -1) + return; + if (!WIFSTOPPED (wstatus)) /* The process has been killed in response to a signal. In this case, simply unlink the tracee and return. */ @@ -747,16 +759,15 @@ process_system_call (struct exec_tracee *tracee) { #ifdef __mips__ /* MIPS systems place errno in v0 and set a3 to 1. */ - regs.gregs[2] = errno; + regs.gregs[2] = save_errno; regs.gregs[7] = 1; #else /* !__mips__ */ - regs.SYSCALL_RET_REG = -errno; + regs.SYSCALL_RET_REG = -save_errno; #endif /* __mips__ */ /* Report errno. */ #ifdef __aarch64__ - /* Restore x0, x1 and x2. */ - regs.regs[0] = old_w0; + /* Restore x1 and x2. x0 is clobbered by errno. */ regs.regs[1] = old_w1; regs.regs[2] = old_w2; aarch64_set_regs (tracee->pid, ®s, false); commit da6f0d9c6fd4b2a097f02a6e8f1b2aa33a6bf307 Author: Po Lu Date: Mon May 1 14:25:40 2023 +0800 ; * README: Describe `exec' directory. diff --git a/README b/README index c8a3d485c28..694b3aa4c94 100644 --- a/README +++ b/README @@ -98,6 +98,8 @@ There are several subdirectories: 'java' holds the Java code for the Emacs port to Android. 'cross' holds Makefiles and an additional copy of gnulib used to build Emacs for Android devices. +'exec' holds the source code to several helper executables used to run + user-installed programs on Android. Building Emacs on non-Posix platforms requires tools that aren't part of the standard distribution of the OS. The platform-specific README commit 843d8797db80aa13f50c9bab4e3c5c4fa0b80edc Author: Po Lu Date: Mon May 1 14:16:07 2023 +0800 Fix use dialog box regression on Android * lisp/subr.el (use-dialog-box-p): Always prefer dialog boxes. diff --git a/lisp/subr.el b/lisp/subr.el index b1e460b88eb..a695d52046e 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3601,7 +3601,8 @@ from--tty-menu-p (defun use-dialog-box-p () "Return non-nil if the current command should prompt the user via a dialog box." (and last-input-event ; not during startup - (or (consp last-nonmenu-event) ; invoked by a mouse event + (or (featurep 'android) ; prefer dialog boxes on Android + (consp last-nonmenu-event) ; invoked by a mouse event from--tty-menu-p) ; invoked via TTY menu use-dialog-box)) commit 6c3369abe5de22a86645793fb6be00b234d4cc87 Author: Po Lu Date: Mon May 1 13:51:12 2023 +0800 Make it easier to quit on Android * src/android.c (android_write_event): (JNICALL): Raise SIGIO on key press and window action events. diff --git a/src/android.c b/src/android.c index 0244113285b..129ad6b5767 100644 --- a/src/android.c +++ b/src/android.c @@ -669,9 +669,23 @@ android_write_event (union android_event *event) pthread_cond_broadcast (&event_queue.read_var); pthread_mutex_unlock (&event_queue.mutex); - /* Now set pending_signals to true. This allows C-g to be handled - immediately even without SIGIO. */ + /* Now set pending_signals to true, and raise SIGIO to interrupt any + ongoing reads if the event is important. */ pending_signals = true; + + switch (event->type) + { + /* Key press and window action events are considered important, + as they either end up quitting or asking for responses to the + IME. */ + case ANDROID_KEY_PRESS: + case ANDROID_WINDOW_ACTION: + raise (SIGIO); + break; + + default: + break; + } } int @@ -2480,7 +2494,10 @@ NATIVE_NAME (quit) (JNIEnv *env, jobject object) { JNI_STACK_ALIGNMENT_PROLOGUE; + /* Raise sigio to interrupt anything that could be reading + input. */ Vquit_flag = Qt; + raise (SIGIO); } JNIEXPORT jlong JNICALL commit 6a30a74cb2ac2cba69aa01da12caf1eac7134f43 Author: Po Lu Date: Mon May 1 13:12:44 2023 +0800 Fix syscall error reporting on aarch64 * exec/trace.c (process_system_call): Save and restore x0, x1 and x2 regs after replacing them with an invalid file descriptor. diff --git a/exec/trace.c b/exec/trace.c index cef699e8526..df5deacd9bb 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -627,6 +627,9 @@ process_system_call (struct exec_tracee *tracee) USER_REGS_STRUCT regs; int rc, wstatus; USER_WORD callno, sp; +#ifdef __aarch64__ + USER_WORD old_w0, old_w1, old_w2; +#endif /* __aarch64__ */ #ifdef __aarch64__ rc = aarch64_get_regs (tracee->pid, ®s); @@ -692,7 +695,20 @@ process_system_call (struct exec_tracee *tracee) Make sure that the stack pointer is restored to its original position upon exit, or bad things can happen. */ +#ifndef __aarch64__ regs.SYSCALL_NUM_REG = -1; +#else /* __aarch64__ */ + /* ARM also requires the system call number to be valid. However, I + can't find any unused system call, so use fcntl instead, with + invalid arguments. */ + regs.SYSCALL_NUM_REG = 72; + old_w0 = regs.regs[0]; + old_w1 = regs.regs[1]; + old_w2 = regs.regs[2]; + regs.regs[0] = -1; + regs.regs[1] = -1; + regs.regs[2] = -1; +#endif /* !__aarch64__ */ regs.STACK_POINTER = sp; #ifdef __aarch64__ @@ -714,7 +730,6 @@ process_system_call (struct exec_tracee *tracee) return; #endif /* __aarch64__ */ - /* Do this invalid system call. */ if (ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL)) return; @@ -740,6 +755,10 @@ process_system_call (struct exec_tracee *tracee) /* Report errno. */ #ifdef __aarch64__ + /* Restore x0, x1 and x2. */ + regs.regs[0] = old_w0; + regs.regs[1] = old_w1; + regs.regs[2] = old_w2; aarch64_set_regs (tracee->pid, ®s, false); #else /* !__aarch64__ */ ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s); commit ddc16de86964d445309dd38175a85221c14f05ab Author: Po Lu Date: Mon May 1 11:28:22 2023 +0800 Update Android port * Makefile.in (extraclean): Clean in exec as well. * configure.ac: Fix detection of absolute srcdir. Also, pass CFLAGS. * exec/Makefile.in: (.c.o): Add -I. so config.h can be found.:(.s.o): Don't create m4 temporary in srcdir. * exec/config-mips.m4.in (DADDI2, DADDI3): New macros. Define to substitute if as cannot assemble daddi. * exec/configure.ac (user_h): Look for user.h in asm/ as well. Use new user.h. Also look in ptrace.h on arm systems. Check if as supports daddi on mips64. * exec/exec.c (check_interpreter): Fix char signedness bug. * exec/loader-mips64el.s (__start): Use DADDI2 and DADDI3 for two- and 3-operand daddi. * exec/mipsel-user.h: Don't include sgidefs.h. * java/INSTALL: Document that m4 is now required. * src/android.c (android_rewrite_spawn_argv): Add missing NULL. diff --git a/Makefile.in b/Makefile.in index 7c18a9fa453..488f4c4ef45 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1101,6 +1101,8 @@ extraclean: -[ "${srcdir}" = "." ] || \ find ${srcdir} '(' -name '*~' -o -name '#*' ')' ${FIND_DELETE} -find . '(' -name '*~' -o -name '#*' ')' ${FIND_DELETE} + -rm -f ${srcdir}/exec/config-tmp-* ${srcdir}/exec/aclocal.m4 \ + ${srcdir}/src/config.in ${srcdir}/exec/configure # The src subdir knows how to do the right thing # even when the build directory and source dir are different. diff --git a/configure.ac b/configure.ac index ef16841e066..750220b5129 100644 --- a/configure.ac +++ b/configure.ac @@ -179,7 +179,6 @@ # At the same time, configure libexec with the build directory # set to `exec'. AS_MKDIR_P([exec]) - AC_MSG_NOTICE([configuring in `exec']) # Enter exec and configure it, using the C compiler as both the # assembler and the linker. Determine the absolute name of the @@ -187,18 +186,21 @@ # N.B. that the linker is actually cc, so pass -nostdlib, lest # the crt be linked in. Likewise for as. - AS_CASE([$ac_srcdir], [.], [emacs_srcdir=`pwd`], - [[[\\/]* | ?:[\\/]*]], [emacs_srcdir=$ac_srcdir], - [*], [emacs_srcdir=`pwd`/$ac_srcdir]) + AS_CASE([$srcdir], [.], [emacs_srcdir=`pwd`], + [[[\\/]* | ?:[\\/]*]], [emacs_srcdir=$srcdir], + [*], [emacs_srcdir=`pwd`/$srcdir]) + + AC_MSG_NOTICE([configuring in `exec']) OLDCWD=`pwd` cd exec - $CONFIG_SHELL $emacs_srcdir/exec/configure \ - --host=$host CC=$CC LD=$CC AS=$CC \ - AR=$AR ASFLAGS=-c + $CONFIG_SHELL $emacs_srcdir/exec/configure \ + --host=$host "CC=$CC" "LD=$CC" "AS=$CC" \ + "AR=$AR" "CFLAGS=$CFLAGS" + emacs_val=$? cd $OLDCWD - AS_IF([test "$?" != "0"], + AS_IF([test "$emacs_val" != "0"], [AC_MSG_ERROR([failed to configure in `exec'])]) ]) diff --git a/exec/Makefile.in b/exec/Makefile.in index 5bd61b2e831..365dc42e0b7 100644 --- a/exec/Makefile.in +++ b/exec/Makefile.in @@ -81,10 +81,10 @@ Makefile: .SUFFIXES: .c .s .c.o: - $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -I$(srcdir) $< -o $@ + $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -I$(srcdir) -I. $< -o $@ .s.o: - $(M4) $< > $<.s - $(AS) $(ASFLAGS) $<.s -o $@ + $(M4) $< > $(notdir $<).s + $(AS) $(ASFLAGS) $(notdir $<).s -o $@ # Set up dependencies for config-mips.m4. diff --git a/exec/config-mips.m4.in b/exec/config-mips.m4.in index 886d19b8e8f..72632765bd0 100644 --- a/exec/config-mips.m4.in +++ b/exec/config-mips.m4.in @@ -34,3 +34,9 @@ define(`SYSCALL', `ifelse(`@MIPS_N32@',`yes',` move $a4, $1 sw $4, 28($sp)')') define(`RESTORE', `ifelse(`@MIPS_N32@',`yes',` nop',` addi $sp, 32')') + +dnl For mips64. Some assemblers don't want to assemble `daddi'. +define(`DADDI2', `ifelse(`@DADDI_BROKEN@',`yes',` li $at, $2 +dadd $1, $1, $at',` daddi $1, $2')') +define(`DADDI3', `ifelse(`@DADDI_BROKEN@',`yes',` li $at, $3 +dadd $1, $2, $at',` daddi $1, $2, $3')') diff --git a/exec/configure.ac b/exec/configure.ac index 0a334c6e4ff..9763edc99f3 100644 --- a/exec/configure.ac +++ b/exec/configure.ac @@ -96,6 +96,13 @@ AC_CANONICAL_HOST +# Check whether or not sys/user exists. If it doesn't, try +# asm/user.h, and croak if that doesn't exist either. +AS_CASE([$host], [*mips*], [], [*], + [AC_CHECK_HEADER([sys/user.h], [user_h=""], + [AC_CHECK_HEADER([asm/user.h], [user_h=""], + [AC_MSG_ERROR([Can not find working user.h])])])]) + # Look for required tools. AC_ARG_VAR([M4], [`m4' preprocessor command.]) @@ -187,11 +194,12 @@ AC_DEFUN exec_loader= is_mips= OBJS="exec.o trace.o" +DADDI_BROKEN=no AS_CASE([$host], [x86_64-*linux*], [AC_CHECK_MEMBER([struct user_regs_struct.rdi], [AC_DEFINE([SYSCALL_HEADER], []) - AC_DEFINE([USER_HEADER], []) + AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h]) AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct]) AC_DEFINE([SYSCALL_NUM_REG], [orig_rax]) AC_DEFINE([SYSCALL_RET_REG], [rax]) @@ -213,11 +221,11 @@ AC_DEFUN exec_loader=loader-x86_64.s], [AC_MSG_ERROR([Missing `rdi' in user_regs_struct])], [[ -#include +#include $user_h ]])], [i[[34567]]86-*linux*], [AC_CHECK_MEMBER([struct user_regs_struct.edi], [AC_DEFINE([SYSCALL_HEADER], []) - AC_DEFINE([USER_HEADER], []) + AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h]) AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct]) AC_DEFINE([SYSCALL_NUM_REG], [orig_eax]) AC_DEFINE([SYSCALL_RET_REG], [eax]) @@ -237,11 +245,11 @@ AC_DEFUN exec_loader=loader-x86.s], [AC_MSG_ERROR([Missing `edi' in user_regs_struct])], [[ -#include +#include $user_h ]])], [aarch64-*linux*], [AC_CHECK_MEMBER([struct user_regs_struct.sp], [AC_DEFINE([SYSCALL_HEADER], []) - AC_DEFINE([USER_HEADER], []) + AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h]) AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct]) AC_DEFINE([SYSCALL_NUM_REG], [[regs[8]]]) AC_DEFINE([SYSCALL_RET_REG], [[regs[0]]]) @@ -263,11 +271,11 @@ AC_DEFUN exec_loader=loader-aarch64.s], [AC_MSG_ERROR([Missing `sp' in user_regs_struct])], [[ -#include +#include $user_h ]])], [arm*linux*eabi* | armv7*linux*], [AC_CHECK_MEMBER([struct user_regs.uregs], [AC_DEFINE([SYSCALL_HEADER], []) - AC_DEFINE([USER_HEADER], []) + AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h]) AC_DEFINE([USER_REGS_STRUCT], [struct user_regs]) AC_DEFINE([SYSCALL_NUM_REG], [[uregs[7]]]) AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]]) @@ -283,9 +291,30 @@ AC_DEFUN exec_CHECK_LINUX_CLONE3 LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" exec_loader=loader-armeabi.s], - [AC_MSG_ERROR([Missing `uregs' in user_regs_struct])], + [AC_CHECK_MEMBER([struct pt_regs.uregs], + [AC_DEFINE([SYSCALL_HEADER], []) + AC_DEFINE_UNQUOTED([USER_HEADER], []) + AC_DEFINE([USER_REGS_STRUCT], [struct pt_regs]) + AC_DEFINE([SYSCALL_NUM_REG], [[uregs[7]]]) + AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]]) + AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]]) + AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]]) + AC_DEFINE([STACK_POINTER], [[uregs[13]]]) + AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) + AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) + AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) + AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) + AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + exec_CHECK_LINUX_CLONE3 + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" + exec_loader=loader-armeabi.s], + [AC_MSG_ERROR([Missing `uregs' in user_regs_struct or pt_regs])], + [[ +#include + ]])], [[ -#include +#include $user_h ]])], [mipsel*linux*], [AC_DEFINE([SYSCALL_HEADER], []) AC_DEFINE([USER_HEADER], ["mipsel-user.h"]) @@ -325,6 +354,27 @@ AC_DEFUN AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + AC_CACHE_CHECK([whether as understands `daddi'], + [exec_cv_as_daddi], + [exec_cv_as_daddi=no + cat <<_ACEOF >conftest.s + .section text + .global __start +__start: + li $t0, 0 + li $t1, 0 + daddi $t0, $t1, 1 + daddi $t0, $t1, -1 + daddi $t0, -1 + daddi $t0, 1 + +_ACEOF + $AS $ASFLAGS conftest.s -o conftest.$OBJEXT \ + >&AS_MESSAGE_LOG_FD 2>&1 \ + && exec_cv_as_daddi=yes + rm -f conftest.s conftest.$OBJEXT]) + AS_IF([test "x$exec_cv_as_daddi" != "xyes"], + [DADDI_BROKEN=yes]) exec_CHECK_LINUX_CLONE3 exec_CHECK_MIPS_NABI LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x3e00000000" @@ -332,6 +382,8 @@ AC_DEFUN exec_loader=loader-mips64el.s], [*], [AC_MSG_ERROR([Please port libexec to $host])]) +AC_SUBST([DADDI_BROKEN]) + MIPS_N32=$exec_cv_mips_nabi AC_ARG_VAR([LOADERFLAGS], [Flags used to link the loader.]) diff --git a/exec/exec.c b/exec/exec.c index e890179a9ab..662c8bf69d2 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -95,7 +95,7 @@ check_interpreter (const char *name, int fd, const char **extra) /* Strip leading whitespace. */ start = buffer; - while (*start && *start < 128 && isspace (*start)) + while (*start && ((unsigned char) *start) < 128 && isspace (*start)) ++start; /* Look for a newline character. */ diff --git a/exec/loader-mips64el.s b/exec/loader-mips64el.s index ccebdfe72f6..73dc8c63fe8 100644 --- a/exec/loader-mips64el.s +++ b/exec/loader-mips64el.s @@ -15,7 +15,10 @@ # You should have received a copy of the GNU General Public License # along with GNU Emacs. If not, see . +include(`config-mips.m4') + .set noreorder # delay slots managed by hand + .set noat # no assembler macros .section .text .global __start __start: @@ -24,8 +27,8 @@ dnl dla $a0, .timespec # rqtp dnl li $a1, 0 # rmtp dnl syscall # syscall ld $s2, ($sp) # original stack pointer - daddi $s0, $sp, 16 # start of load area - daddi $sp, -16 # primary fd, secondary fd + DADDI3( $s0, $sp, 16) # start of load area + DADDI2( $sp, -16) # primary fd, secondary fd li $t0, -1 # secondary fd sd $t0, 8($sp) # initialize secondary fd .next_action: @@ -33,7 +36,7 @@ dnl syscall # syscall andi $t0, $s1, 15 # t0 = action number & 15 beqz $t0, .open_file # open file? nop # delay slot - daddi $t0, -3 # t0 -= 3 + DADDI2( $t0, -3) # t0 -= 3 beqz $t0, .rest_of_exec # jump to code nop # delay slot li $t1, 1 @@ -76,30 +79,30 @@ dnl syscall # syscall bne $t2, $zero, .fillb # fill bytes nop # delay slot sd $zero, ($t1) # zero doubleword - daddi $t1, 8 # next doubleword + DADDI2( $t1, 8) # next doubleword sd $zero, ($t1) # zero doubleword - daddi $t1, 8 # next doubleword + DADDI2( $t1, 8) # next doubleword sd $zero, ($t1) # zero doubleword - daddi $t1, 8 # next doubleword + DADDI2( $t1, 8) # next doubleword sd $zero, ($t1) # zero doubleword - daddi $t1, 8 # next doubleword + DADDI2( $t1, 8) # next doubleword sd $zero, ($t1) # zero doubleword - daddi $t1, 8 # next doubleword + DADDI2( $t1, 8) # next doubleword sd $zero, ($t1) # zero doubleword - daddi $t1, 8 # next doubleword + DADDI2( $t1, 8) # next doubleword sd $zero, ($t1) # zero doubleword - daddi $t1, 8 # next doubleword + DADDI2( $t1, 8) # next doubleword sd $zero, ($t1) # zero doubleword - daddi $t1, 8 # next doubleword + DADDI2( $t1, 8) # next doubleword j .filld # fill either doubleword or byte nop # delay slot .fillb: beq $t0, $t1, .continue # already finished? nop # delay slot sb $zero, ($t1) # clear byte - daddi $t1, $t1, 1 # t1++ + DADDI2( $t1, 1) # t1++ .continue: - daddi $s0, $s0, 56 # s0 = next action + DADDI2( $s0, 56) # s0 = next action j .next_action # next action nop # delay slot .do_mmap_anon: @@ -113,26 +116,26 @@ dnl syscall # syscall nop # branch delay slot .open_file: li $v0, 5002 # SYS_open - daddi $a0, $s0, 8 # start of name + DADDI3( $a0, $s0, 8) # start of name move $a1, $zero # flags = O_RDONLY move $a2, $zero # mode = 0 syscall # syscall bne $a3, $zero, .perror # perror nop # delay slot - daddi $s0, $s0, 8 # start of string + DADDI2( $s0, 8) # start of string .nextc: lb $t0, ($s0) # load byte - daddi $s0, $s0, 1 # s0++ + DADDI2( $s0, 1) # s0++ bne $t0, $zero, .nextc # next character? nop # delay slot - daddi $s0, $s0, 7 # adjust for round + DADDI2( $s0, 7) # adjust for round li $t2, -8 # t2 = -8 and $s0, $s0, $t2 # mask for round andi $t0, $s1, 16 # t1 = s1 & 16 move $t1, $sp # address of primary fd beqz $t0, .primary # primary fd? nop # delay slot - daddi $t1, $t1, 8 # address of secondary fd + DADDI2( $t1, 8) # address of secondary fd .primary: sd $v0, ($t1) # store fd j .next_action # next action @@ -145,11 +148,11 @@ dnl syscall # syscall move $s1, $s2 # original SP ld $t0, ($s1) # argc dsll $t0, $t0, 3 # argc *= 3 - daddi $t0, $t0, 16 # argc += 16 + DADDI2( $t0, 16) # argc += 16 dadd $s1, $s1, $t0 # s1 = start of envp .skipenv: ld $t0, ($s1) # t0 = *s1 - daddi $s1, $s1, 8 # s1++ + DADDI2( $s1, 8) # s1++ bne $t0, $zero, .skipenv # skip again nop # delay slot dla $t3, .auxvtab # address of auxv table @@ -170,7 +173,7 @@ dnl syscall # syscall ld $t2, ($t2) # t2 = *t2 sd $t2, 8($s1) # set auxv value .next: - daddi $s1, $s1, 16 # next auxv + DADDI2( $s1, 16) # next auxv j .one_auxv # next auxv nop # delay slot .finish: diff --git a/exec/mipsel-user.h b/exec/mipsel-user.h index 2b77a970d8e..dc3f98eb4e7 100644 --- a/exec/mipsel-user.h +++ b/exec/mipsel-user.h @@ -22,7 +22,6 @@ Copyright (C) 2023 Free Software Foundation, Inc. #ifndef _MIPSEL_USER_H_ #define _MIPSEL_USER_H_ -#include #include #ifndef ELF_NGREG diff --git a/java/INSTALL b/java/INSTALL index 4bab7d5a2da..63b99272004 100644 --- a/java/INSTALL +++ b/java/INSTALL @@ -22,10 +22,11 @@ your freedom's sake, you should use the Android SDK provided by the Debian project. In addition to the Android SDK and Android NDK, Emacs also requires -the Java compiler from OpenJDK 1.7.0 to be installed on your system. -Building on GNU systems is all that is officially supported. We are -told that Mac OS works too, and other Unix systems will likely work -as well, but MS Windows and Cygwin will not. +the Java compiler from OpenJDK 1.7.0 to be installed on your system, +along with a working `m4' macro processor. Building on GNU systems is +all that is officially supported. We are told that Mac OS works too, +and other Unix systems will likely work as well, but MS Windows and +Cygwin will not. Once all of those tools are obtained, you may invoke the `configure' script like so: diff --git a/src/android.c b/src/android.c index ce8f277e120..0244113285b 100644 --- a/src/android.c +++ b/src/android.c @@ -6584,8 +6584,8 @@ android_rewrite_spawn_argv (const char ***argv) new_args[0] = exec1_name; new_args[1] = loader_name; - /* And insert the rest. */ - for (i = 0; i < nargs; ++i) + /* And insert the rest, including the trailing NULL. */ + for (i = 0; i < nargs + 1; ++i) new_args[i + 2] = (*argv)[i]; /* Replace argv. */ commit a8f9a4d2d9bc982217b4be783b236778f9d6dd32 Merge: 5550816f596 aa56253407e Author: Po Lu Date: Mon May 1 09:48:25 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 5550816f5962943abd81fbf68901dad575f18c06 Author: Po Lu Date: Mon May 1 09:31:58 2023 +0800 Work around system restrictions regarding exec * doc/emacs/android.texi (Android Environment): Document `android-use-exec-loader'. * exec/exec1.c (main): Set program group of child process. * src/android.c (android_rewrite_spawn_argv): New function. * src/android.h: Update prototypes. * src/androidfns.c (syms_of_androidfns): New variable `android_use_exec_loader'. * src/callproc.c (emacs_spawn): Rewrite the argument vector to use exec1 if necessary. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 08897d3f97e..e1c644d6043 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -252,10 +252,7 @@ Android Environment which is the app data directory (@pxref{Android File System}.) Each application is also prohibited from accessing system -directories, and the app data directories of other applications. In -recent versions of Android, the system also prohibits, for security -reasons, even Emacs itself from running executables inside the app -data directory. +directories, and the app data directories of other applications. Emacs comes with several binaries. While being executable files, they are packaged as libraries in the library directory, because @@ -277,6 +274,17 @@ Android Environment code, and is not sanctioned by the Android compatibility definition documents, so your mileage may vary. +@cindex call-process, Android +@vindex android-use-exec-loader + Android 10 and later versions of the system also prohibit Emacs +itself from running executables inside the app data directory. On +these systems, Emacs normally applies a workaround; however, this +workaround requires running all sub-processes in another subprocess, +and applying process tracing to all executables, which may prove to be +problematic for various different reasons. In that case, the +workaround can be disabled by changing the variable +@code{android-use-exec-loader} to @code{nil}. + @section Running Emacs in the background @cindex emacs killed, android @cindex emacs in the background, android diff --git a/exec/exec1.c b/exec/exec1.c index 835bf8e72b9..d77ca8adf54 100644 --- a/exec/exec1.c +++ b/exec/exec1.c @@ -20,6 +20,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include #include +#include #include #include "exec.h" @@ -41,10 +42,15 @@ main (int argc, char **argv) extern char **environ; int wstatus; + pid1 = getpid (); pid = fork (); if (!pid) { + /* Set the process group used to the parent. */ + if (setpgid (0, pid1)) + perror ("setpgid"); + tracing_execve (argv[2], argv + 2, environ); /* An error occured. Exit with failure. */ diff --git a/src/android.c b/src/android.c index 3798758ff16..ce8f277e120 100644 --- a/src/android.c +++ b/src/android.c @@ -6514,6 +6514,89 @@ android_free_cursor (android_cursor cursor) android_destroy_handle (cursor); } + + +/* Process execution. + + Newer Android systems use SELinux to restrict user programs from + executing programs installed in the application data directory for + security reasons. Emacs uses a `loader' binary installed in the + application data directory to manually load executables and replace + the `execve' system call. */ + +enum + { + /* Maximum number of arguments available. */ + MAXARGS = 1024, + }; + +/* Rewrite the command line given in *ARGV to utilize the `exec1' + bootstrap binary if necessary. + + Value is 0 upon success, else 1. Set errno upon failure. + + ARGV holds a pointer to a NULL-terminated array of arguments given + to `emacs_spawn'. */ + +int +android_rewrite_spawn_argv (const char ***argv) +{ + static const char *new_args[MAXARGS]; + static char exec1_name[PATH_MAX], loader_name[PATH_MAX]; + size_t i, nargs; + + /* This isn't required on Android 9 or earlier. */ + + if (android_api_level < 29 || !android_use_exec_loader) + return 0; + + /* Get argv[0]; this should never be NULL. + Then, verify that it exists and is executable. */ + + eassert (**argv); + if (access (**argv, R_OK | X_OK)) + return 1; + + /* Count the number of arguments in *argv. */ + + nargs = 0; + while ((*argv)[nargs]) + ++nargs; + + /* nargs now holds the number of arguments in argv. If it's larger + than MAXARGS, return failure. */ + + if (nargs + 2 > MAXARGS) + { + errno = E2BIG; + return 1; + } + + /* Fill in the name of `libexec1.so'. */ + snprintf (exec1_name, PATH_MAX, "%s/libexec1.so", + android_lib_dir); + + /* And libloader.so. */ + snprintf (loader_name, PATH_MAX, "%s/libloader.so", + android_lib_dir); + + /* Now fill in the first two arguments. */ + new_args[0] = exec1_name; + new_args[1] = loader_name; + + /* And insert the rest. */ + for (i = 0; i < nargs; ++i) + new_args[i + 2] = (*argv)[i]; + + /* Replace argv. */ + *argv = new_args; + + /* Return success. */ + return 0; +} + + + #else /* ANDROID_STUBIFY */ /* X emulation functions for Android. */ diff --git a/src/android.h b/src/android.h index 24666aaf989..62d420d4cce 100644 --- a/src/android.h +++ b/src/android.h @@ -190,6 +190,11 @@ Copyright (C) 2023 Free Software Foundation, Inc. extern unsigned int event_serial; + + +/* Process related functions. */ +extern int android_rewrite_spawn_argv (const char ***); + #endif /* JNI functions should not be built when Emacs is stubbed out for the diff --git a/src/androidfns.c b/src/androidfns.c index 3367ebdf755..3bd34edd5b9 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -3112,6 +3112,20 @@ syms_of_androidfns (void) using the volume down button. */); android_pass_multimedia_buttons_to_system = false; + DEFVAR_BOOL ("android-use-exec-loader", android_use_exec_loader, + doc: /* Whether or not to bypass system restrictions on program execution. + +Android 10 and later prevent programs from executing files installed +in writable directories, such as the application data directory. + +When non-nil, Emacs will bypass this restriction by running such +executables under system call tracing, and replacing the `execve' +system call with a version which ignores the system's security +restrictions. + +This option has no effect on Android 9 and earlier. */); + android_use_exec_loader = true; + /* Functions defined. */ defsubr (&Sx_create_frame); defsubr (&Sxw_color_defined_p); diff --git a/src/callproc.c b/src/callproc.c index a1811a3bb23..015b52bc9bc 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -92,6 +92,10 @@ #define _P_NOWAIT 1 /* from process.h */ #include "pgtkterm.h" #endif +#ifdef HAVE_ANDROID +#include "android.h" +#endif /* HAVE_ANDROID */ + /* Pattern used by call-process-region to make temp files. */ static Lisp_Object Vtemp_file_name_pattern; @@ -1437,6 +1441,18 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err, const char *pty_name, bool pty_in, bool pty_out, const sigset_t *oldset) { +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + /* Android 10 and later don't allow directly executing programs + installed in the application data directory. Emacs provides a + loader binary which replaces the `execve' system call for it and + all its children. On these systems, rewrite the command line to + call that loader binary instead. */ + + if (android_rewrite_spawn_argv ((const char ***) &argv)) + return 1; +#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ + + #if USABLE_POSIX_SPAWN /* Prefer the simpler `posix_spawn' if available. `posix_spawn' doesn't yet support setting up pseudoterminals, so we fall back commit 9a7c645dd4ef38b72787e932d444a61ed4e04c63 Author: Po Lu Date: Mon May 1 08:15:48 2023 +0800 Remove exec/configure * exec/configure: Remove file. * .gitignore: Add exec/configure. diff --git a/.gitignore b/.gitignore index 2c5559412bc..c95bccccfbf 100644 --- a/.gitignore +++ b/.gitignore @@ -381,4 +381,5 @@ exec/deps/* exec/autom4te.cache exec/config.h exec/config-mips.m4 +exec/configure exec/*.s.s diff --git a/exec/configure b/exec/configure deleted file mode 100755 index 5074fa2570a..00000000000 --- a/exec/configure +++ /dev/null @@ -1,6940 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.71 for libexec 30.0.50. -# -# Report bugs to . -# -# -# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, -# Inc. -# -# -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -as_nop=: -if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 -then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else $as_nop - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - - -# Reset variables that may have inherited troublesome values from -# the environment. - -# IFS needs to be set, to space, tab, and newline, in precisely that order. -# (If _AS_PATH_WALK were called with IFS unset, it would have the -# side effect of setting IFS to empty, thus disabling word splitting.) -# Quoting is to prevent editors from complaining about space-tab. -as_nl=' -' -export as_nl -IFS=" "" $as_nl" - -PS1='$ ' -PS2='> ' -PS4='+ ' - -# Ensure predictable behavior from utilities with locale-dependent output. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# We cannot yet rely on "unset" to work, but we need these variables -# to be unset--not just set to an empty or harmless value--now, to -# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct -# also avoids known problems related to "unset" and subshell syntax -# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). -for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH -do eval test \${$as_var+y} \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done - -# Ensure that fds 0, 1, and 2 are open. -if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi -if (exec 3>&2) ; then :; else exec 2>/dev/null; fi - -# The user is always right. -if ${PATH_SEPARATOR+false} :; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - test -r "$as_dir$0" && as_myself=$as_dir$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - - -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} -if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="as_nop=: -if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 -then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else \$as_nop - case \`(set -o) 2>/dev/null\` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi -" - as_required="as_fn_return () { (exit \$1); } -as_fn_success () { as_fn_return 0; } -as_fn_failure () { as_fn_return 1; } -as_fn_ret_success () { return 0; } -as_fn_ret_failure () { return 1; } - -exitcode=0 -as_fn_success || { exitcode=1; echo as_fn_success failed.; } -as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } -as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } -as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ) -then : - -else \$as_nop - exitcode=1; echo positional parameters were not saved. -fi -test x\$exitcode = x0 || exit 1 -blah=\$(echo \$(echo blah)) -test x\"\$blah\" = xblah || exit 1 -test -x / || exit 1" - as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO - as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO - eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" - if (eval "$as_required") 2>/dev/null -then : - as_have_required=yes -else $as_nop - as_have_required=no -fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null -then : - -else $as_nop - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - as_found=: - case $as_dir in #( - /*) - for as_base in sh bash ksh sh5; do - # Try only shells that exist, to save several forks. - as_shell=$as_dir$as_base - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null -then : - CONFIG_SHELL=$as_shell as_have_required=yes - if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null -then : - break 2 -fi -fi - done;; - esac - as_found=false -done -IFS=$as_save_IFS -if $as_found -then : - -else $as_nop - if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null -then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi -fi - - - if test "x$CONFIG_SHELL" != x -then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 -fi - - if test x$as_have_required = xno -then : - printf "%s\n" "$0: This script requires a shell more modern than all" - printf "%s\n" "$0: the shells that I found on your system." - if test ${ZSH_VERSION+y} ; then - printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" - printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." - else - printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and -$0: bug-gnu-emacs@gnu.org about your system, including any -$0: error possibly output before this message. Then install -$0: a modern shell, or manually run the script under such a -$0: shell if you do have one." - fi - exit 1 -fi -fi -fi -SHELL=${CONFIG_SHELL-/bin/sh} -export SHELL -# Unset more variables known to interfere with behavior of common tools. -CLICOLOR_FORCE= GREP_OPTIONS= -unset CLICOLOR_FORCE GREP_OPTIONS - -## --------------------- ## -## M4sh Shell Functions. ## -## --------------------- ## -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit -# as_fn_nop -# --------- -# Do nothing but, unlike ":", preserve the value of $?. -as_fn_nop () -{ - return $? -} -as_nop=as_fn_nop - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null -then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else $as_nop - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null -then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else $as_nop - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - -# as_fn_nop -# --------- -# Do nothing but, unlike ":", preserve the value of $?. -as_fn_nop () -{ - return $? -} -as_nop=as_fn_nop - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - printf "%s\n" "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - - - as_lineno_1=$LINENO as_lineno_1a=$LINENO - as_lineno_2=$LINENO as_lineno_2a=$LINENO - eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && - test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { - # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - - -# Determine whether it's possible to make 'echo' print without a newline. -# These variables are no longer used directly by Autoconf, but are AC_SUBSTed -# for compatibility with existing Makefiles. -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -# For backward compatibility with old third-party macros, we provide -# the shell variables $as_echo and $as_echo_n. New code should use -# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. -as_echo='printf %s\n' -as_echo_n='printf %s' - - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -test -n "$DJDIR" || exec 7<&0 &1 - -# Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_clean_files= -ac_config_libobj_dir=. -LIBOBJS= -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= - -# Identity of this package. -PACKAGE_NAME='libexec' -PACKAGE_TARNAME='libexec' -PACKAGE_VERSION='30.0.50' -PACKAGE_STRING='libexec 30.0.50' -PACKAGE_BUGREPORT='bug-gnu-emacs@gnu.org' -PACKAGE_URL='https://www.gnu.org/software/emacs/' - -# Factoring default headers for most tests. -ac_includes_default="\ -#include -#ifdef HAVE_STDIO_H -# include -#endif -#ifdef HAVE_STDLIB_H -# include -#endif -#ifdef HAVE_STRING_H -# include -#endif -#ifdef HAVE_INTTYPES_H -# include -#endif -#ifdef HAVE_STDINT_H -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif" - -ac_header_c_list= -ac_subst_vars='LTLIBOBJS -LIBOBJS -OBJS -MIPS_N32 -exec_loader -AUTO_DEPEND -FIND_DELETE -ASFLAGS -ARFLAGS -LOADERFLAGS -AR -LD -AS -M4 -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build -INSTALL_DATA -INSTALL_SCRIPT -INSTALL_PROGRAM -CPP -OBJEXT -EXEEXT -ac_ct_CC -CPPFLAGS -LDFLAGS -CFLAGS -CC -target_alias -host_alias -build_alias -LIBS -ECHO_T -ECHO_N -ECHO_C -DEFS -mandir -localedir -libdir -psdir -pdfdir -dvidir -htmldir -infodir -docdir -oldincludedir -includedir -runstatedir -localstatedir -sharedstatedir -sysconfdir -datadir -datarootdir -libexecdir -sbindir -bindir -program_transform_name -prefix -exec_prefix -PACKAGE_URL -PACKAGE_BUGREPORT -PACKAGE_STRING -PACKAGE_VERSION -PACKAGE_TARNAME -PACKAGE_NAME -PATH_SEPARATOR -SHELL' -ac_subst_files='' -ac_user_opts=' -enable_option_checking -' - ac_precious_vars='build_alias -host_alias -target_alias -CC -CFLAGS -LDFLAGS -LIBS -CPPFLAGS -CPP -M4 -AS -LD -LOADERFLAGS -ARFLAGS -ASFLAGS' - - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -ac_unrecognized_opts= -ac_unrecognized_sep= -# The variables have the same names as the options, with -# dashes changed to underlines. -cache_file=/dev/null -exec_prefix=NONE -no_create= -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -verbose= -x_includes=NONE -x_libraries=NONE - -# Installation directory options. -# These are left unexpanded so users can "make install exec_prefix=/foo" -# and all the variables that are supposed to be based on exec_prefix -# by default will actually change. -# Use braces instead of parens because sh, perl, etc. also accept them. -# (The list follows the same order as the GNU Coding Standards.) -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datarootdir='${prefix}/share' -datadir='${datarootdir}' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -runstatedir='${localstatedir}/run' -includedir='${prefix}/include' -oldincludedir='/usr/include' -docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' -infodir='${datarootdir}/info' -htmldir='${docdir}' -dvidir='${docdir}' -pdfdir='${docdir}' -psdir='${docdir}' -libdir='${exec_prefix}/lib' -localedir='${datarootdir}/locale' -mandir='${datarootdir}/man' - -ac_prev= -ac_dashdash= -for ac_option -do - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval $ac_prev=\$ac_option - ac_prev= - continue - fi - - case $ac_option in - *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; - esac - - case $ac_dashdash$ac_option in - --) - ac_dashdash=yes ;; - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir=$ac_optarg ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build_alias ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build_alias=$ac_optarg ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file=$ac_optarg ;; - - --config-cache | -C) - cache_file=config.cache ;; - - -datadir | --datadir | --datadi | --datad) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=*) - datadir=$ac_optarg ;; - - -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ - | --dataroo | --dataro | --datar) - ac_prev=datarootdir ;; - -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ - | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) - datarootdir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: \`$ac_useropt'" - ac_useropt_orig=$ac_useropt - ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=no ;; - - -docdir | --docdir | --docdi | --doc | --do) - ac_prev=docdir ;; - -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) - docdir=$ac_optarg ;; - - -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) - ac_prev=dvidir ;; - -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) - dvidir=$ac_optarg ;; - - -enable-* | --enable-*) - ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: \`$ac_useropt'" - ac_useropt_orig=$ac_useropt - ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=\$ac_optarg ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix=$ac_optarg ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he | -h) - ac_init_help=long ;; - -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) - ac_init_help=recursive ;; - -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) - ac_init_help=short ;; - - -host | --host | --hos | --ho) - ac_prev=host_alias ;; - -host=* | --host=* | --hos=* | --ho=*) - host_alias=$ac_optarg ;; - - -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) - ac_prev=htmldir ;; - -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ - | --ht=*) - htmldir=$ac_optarg ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir=$ac_optarg ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir=$ac_optarg ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir=$ac_optarg ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir=$ac_optarg ;; - - -localedir | --localedir | --localedi | --localed | --locale) - ac_prev=localedir ;; - -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) - localedir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst | --locals) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) - localstatedir=$ac_optarg ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir=$ac_optarg ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c | -n) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir=$ac_optarg ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix=$ac_optarg ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix=$ac_optarg ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix=$ac_optarg ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name=$ac_optarg ;; - - -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) - ac_prev=pdfdir ;; - -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) - pdfdir=$ac_optarg ;; - - -psdir | --psdir | --psdi | --psd | --ps) - ac_prev=psdir ;; - -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) - psdir=$ac_optarg ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -runstatedir | --runstatedir | --runstatedi | --runstated \ - | --runstate | --runstat | --runsta | --runst | --runs \ - | --run | --ru | --r) - ac_prev=runstatedir ;; - -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ - | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ - | --run=* | --ru=* | --r=*) - runstatedir=$ac_optarg ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir=$ac_optarg ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir=$ac_optarg ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site=$ac_optarg ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir=$ac_optarg ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir=$ac_optarg ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target_alias ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target_alias=$ac_optarg ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers | -V) - ac_init_version=: ;; - - -with-* | --with-*) - ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: \`$ac_useropt'" - ac_useropt_orig=$ac_useropt - ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=\$ac_optarg ;; - - -without-* | --without-*) - ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: \`$ac_useropt'" - ac_useropt_orig=$ac_useropt - ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=no ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes=$ac_optarg ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries=$ac_optarg ;; - - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - case $ac_envvar in #( - '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; - esac - eval $ac_envvar=\$ac_optarg - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 - : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" - ;; - - esac -done - -if test -n "$ac_prev"; then - ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" -fi - -if test -n "$ac_unrecognized_opts"; then - case $enable_option_checking in - no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; - esac -fi - -# Check all directory arguments for consistency. -for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ - datadir sysconfdir sharedstatedir localstatedir includedir \ - oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir runstatedir -do - eval ac_val=\$$ac_var - # Remove trailing slashes. - case $ac_val in - */ ) - ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` - eval $ac_var=\$ac_val;; - esac - # Be sure to have absolute directory names. - case $ac_val in - [\\/$]* | ?:[\\/]* ) continue;; - NONE | '' ) case $ac_var in *prefix ) continue;; esac;; - esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" -done - -# There might be people who depend on the old broken behavior: `$host' -# used to hold the argument of --host etc. -# FIXME: To remove some day. -build=$build_alias -host=$host_alias -target=$target_alias - -# FIXME: To remove some day. -if test "x$host_alias" != x; then - if test "x$build_alias" = x; then - cross_compiling=maybe - elif test "x$build_alias" != "x$host_alias"; then - cross_compiling=yes - fi -fi - -ac_tool_prefix= -test -n "$host_alias" && ac_tool_prefix=$host_alias- - -test "$silent" = yes && exec 6>/dev/null - - -ac_pwd=`pwd` && test -n "$ac_pwd" && -ac_ls_di=`ls -di .` && -ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" -test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" - - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then the parent directory. - ac_confdir=`$as_dirname -- "$as_myself" || -$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_myself" : 'X\(//\)[^/]' \| \ - X"$as_myself" : 'X\(//\)$' \| \ - X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X"$as_myself" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - srcdir=$ac_confdir - if test ! -r "$srcdir/$ac_unique_file"; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r "$srcdir/$ac_unique_file"; then - test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" -fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" -ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" - pwd)` -# When building in place, set srcdir=. -if test "$ac_abs_confdir" = "$ac_pwd"; then - srcdir=. -fi -# Remove unnecessary trailing slashes from srcdir. -# Double slashes in file names in object file debugging info -# mess up M-x gdb in Emacs. -case $srcdir in -*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; -esac -for ac_var in $ac_precious_vars; do - eval ac_env_${ac_var}_set=\${${ac_var}+set} - eval ac_env_${ac_var}_value=\$${ac_var} - eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} - eval ac_cv_env_${ac_var}_value=\$${ac_var} -done - -# -# Report the --help message. -# -if test "$ac_init_help" = "long"; then - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat <<_ACEOF -\`configure' configures libexec 30.0.50 to adapt to many kinds of systems. - -Usage: $0 [OPTION]... [VAR=VALUE]... - -To assign environment variables (e.g., CC, CFLAGS...), specify them as -VAR=VALUE. See below for descriptions of some of the useful variables. - -Defaults for the options are specified in brackets. - -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] - -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/libexec] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] -_ACEOF - - cat <<\_ACEOF - -System types: - --build=BUILD configure for building on BUILD [guessed] - --host=HOST cross-compile to build programs to run on HOST [BUILD] -_ACEOF -fi - -if test -n "$ac_init_help"; then - case $ac_init_help in - short | recursive ) echo "Configuration of libexec 30.0.50:";; - esac - cat <<\_ACEOF - -Some influential environment variables: - CC C compiler command - CFLAGS C compiler flags - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if - you have headers in a nonstandard directory - CPP C preprocessor - M4 `m4' preprocessor command. - AS `as' assembler command. - LD `ld' linker command. - LOADERFLAGS Flags used to link the loader. - ARFLAGS Flags for the archiver. - ASFLAGS Flags for the assembler. - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to . -libexec home page: . -_ACEOF -ac_status=$? -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || - { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || - continue - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - cd "$ac_dir" || { ac_status=$?; continue; } - # Check for configure.gnu first; this name is used for a wrapper for - # Metaconfig's "Configure" on case-insensitive file systems. - if test -f "$ac_srcdir/configure.gnu"; then - echo && - $SHELL "$ac_srcdir/configure.gnu" --help=recursive - elif test -f "$ac_srcdir/configure"; then - echo && - $SHELL "$ac_srcdir/configure" --help=recursive - else - printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi || ac_status=$? - cd "$ac_pwd" || { ac_status=$?; break; } - done -fi - -test -n "$ac_init_help" && exit $ac_status -if $ac_init_version; then - cat <<\_ACEOF -libexec configure 30.0.50 -generated by GNU Autoconf 2.71 - -Copyright (C) 2021 Free Software Foundation, Inc. -This configure script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it. -_ACEOF - exit -fi - -## ------------------------ ## -## Autoconf initialization. ## -## ------------------------ ## - -# ac_fn_c_try_compile LINENO -# -------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest.beam - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext -then : - ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_compile - -# ac_fn_c_try_cpp LINENO -# ---------------------- -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - } -then : - ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_cpp - -# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists and can be compiled using the include files in -# INCLUDES, setting the cache variable VAR accordingly. -ac_fn_c_check_header_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -printf %s "checking for $2... " >&6; } -if eval test \${$3+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - eval "$3=yes" -else $as_nop - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -eval ac_res=\$$3 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -printf "%s\n" "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_compile - -# ac_fn_c_find_uintX_t LINENO BITS VAR -# ------------------------------------ -# Finds an unsigned integer type with width BITS, setting cache variable VAR -# accordingly. -ac_fn_c_find_uintX_t () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 -printf %s "checking for uint$2_t... " >&6; } -if eval test \${$3+y} -then : - printf %s "(cached) " >&6 -else $as_nop - eval "$3=no" - # Order is important - never check a type that is potentially smaller - # than half of the expected target width. - for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ - 'unsigned long long int' 'unsigned short int' 'unsigned char'; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -int -main (void) -{ -static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - case $ac_type in #( - uint$2_t) : - eval "$3=yes" ;; #( - *) : - eval "$3=\$ac_type" ;; -esac -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - if eval test \"x\$"$3"\" = x"no" -then : - -else $as_nop - break -fi - done -fi -eval ac_res=\$$3 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -printf "%s\n" "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_find_uintX_t - -# ac_fn_c_check_type LINENO TYPE VAR INCLUDES -# ------------------------------------------- -# Tests whether TYPE exists after having included INCLUDES, setting cache -# variable VAR accordingly. -ac_fn_c_check_type () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -printf %s "checking for $2... " >&6; } -if eval test \${$3+y} -then : - printf %s "(cached) " >&6 -else $as_nop - eval "$3=no" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main (void) -{ -if (sizeof ($2)) - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main (void) -{ -if (sizeof (($2))) - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - -else $as_nop - eval "$3=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -eval ac_res=\$$3 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -printf "%s\n" "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_type - -# ac_fn_c_try_link LINENO -# ----------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - } -then : - ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_link - -# ac_fn_c_check_func LINENO FUNC VAR -# ---------------------------------- -# Tests whether FUNC exists, setting the cache variable VAR accordingly -ac_fn_c_check_func () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -printf %s "checking for $2... " >&6; } -if eval test \${$3+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Define $2 to an innocuous variant, in case declares $2. - For example, HP-UX 11i declares gettimeofday. */ -#define $2 innocuous_$2 - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. */ - -#include -#undef $2 - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $2 (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_$2 || defined __stub___$2 -choke me -#endif - -int -main (void) -{ -return $2 (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO" -then : - eval "$3=yes" -else $as_nop - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam \ - conftest$ac_exeext conftest.$ac_ext -fi -eval ac_res=\$$3 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -printf "%s\n" "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_func - -# ac_fn_c_try_run LINENO -# ---------------------- -# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that -# executables *can* be run. -ac_fn_c_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; } -then : - ac_retval=0 -else $as_nop - printf "%s\n" "$as_me: program exited with status $ac_status" >&5 - printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_run - -# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES -# ---------------------------------------------------- -# Tries to find if the field MEMBER exists in type AGGR, after including -# INCLUDES, setting cache variable VAR accordingly. -ac_fn_c_check_member () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 -printf %s "checking for $2.$3... " >&6; } -if eval test \${$4+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main (void) -{ -static $2 ac_aggr; -if (ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - eval "$4=yes" -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$5 -int -main (void) -{ -static $2 ac_aggr; -if (sizeof ac_aggr.$3) -return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - eval "$4=yes" -else $as_nop - eval "$4=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -eval ac_res=\$$4 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -printf "%s\n" "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_member - -# ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR -# ------------------------------------------------------------------ -# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR -# accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR. -ac_fn_check_decl () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - as_decl_name=`echo $2|sed 's/ *(.*//'` - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 -printf %s "checking whether $as_decl_name is declared... " >&6; } -if eval test \${$3+y} -then : - printf %s "(cached) " >&6 -else $as_nop - as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` - eval ac_save_FLAGS=\$$6 - as_fn_append $6 " $5" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main (void) -{ -#ifndef $as_decl_name -#ifdef __cplusplus - (void) $as_decl_use; -#else - (void) $as_decl_name; -#endif -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - eval "$3=yes" -else $as_nop - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - eval $6=\$ac_save_FLAGS - -fi -eval ac_res=\$$3 - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -printf "%s\n" "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_check_decl -ac_configure_args_raw= -for ac_arg -do - case $ac_arg in - *\'*) - ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - as_fn_append ac_configure_args_raw " '$ac_arg'" -done - -case $ac_configure_args_raw in - *$as_nl*) - ac_safe_unquote= ;; - *) - ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. - ac_unsafe_a="$ac_unsafe_z#~" - ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" - ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; -esac - -cat >config.log <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. - -It was created by libexec $as_me 30.0.50, which was -generated by GNU Autoconf 2.71. Invocation command line was - - $ $0$ac_configure_args_raw - -_ACEOF -exec 5>>config.log -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## - -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` - -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` - -_ASUNAME - -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - printf "%s\n" "PATH: $as_dir" - done -IFS=$as_save_IFS - -} >&5 - -cat >&5 <<_ACEOF - - -## ----------- ## -## Core tests. ## -## ----------- ## - -_ACEOF - - -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *\'*) - ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; - 2) - as_fn_append ac_configure_args1 " '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - as_fn_append ac_configure_args " '$ac_arg'" - ;; - esac - done -done -{ ac_configure_args0=; unset ac_configure_args0;} -{ ac_configure_args1=; unset ac_configure_args1;} - -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Use '\'' to represent an apostrophe within the trap. -# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. -trap 'exit_status=$? - # Sanitize IFS. - IFS=" "" $as_nl" - # Save into config.log some information that might help in debugging. - { - echo - - printf "%s\n" "## ---------------- ## -## Cache variables. ## -## ---------------- ##" - echo - # The following way of writing the cache mishandles newlines in values, -( - for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - (set) 2>&1 | - case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - sed -n \ - "s/'\''/'\''\\\\'\'''\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" - ;; #( - *) - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) - echo - - printf "%s\n" "## ----------------- ## -## Output variables. ## -## ----------------- ##" - echo - for ac_var in $ac_subst_vars - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - printf "%s\n" "$ac_var='\''$ac_val'\''" - done | sort - echo - - if test -n "$ac_subst_files"; then - printf "%s\n" "## ------------------- ## -## File substitutions. ## -## ------------------- ##" - echo - for ac_var in $ac_subst_files - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - printf "%s\n" "$ac_var='\''$ac_val'\''" - done | sort - echo - fi - - if test -s confdefs.h; then - printf "%s\n" "## ----------- ## -## confdefs.h. ## -## ----------- ##" - echo - cat confdefs.h - echo - fi - test "$ac_signal" != 0 && - printf "%s\n" "$as_me: caught signal $ac_signal" - printf "%s\n" "$as_me: exit $exit_status" - } >&5 - rm -f core *.core core.conftest.* && - rm -f -r conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status -' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -f -r conftest* confdefs.h - -printf "%s\n" "/* confdefs.h */" > confdefs.h - -# Predefined preprocessor variables. - -printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h - -printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h - -printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h - -printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h - -printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h - -printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h - - -# Let the site file select an alternate cache file if it wants to. -# Prefer an explicitly selected file to automatically selected ones. -if test -n "$CONFIG_SITE"; then - ac_site_files="$CONFIG_SITE" -elif test "x$prefix" != xNONE; then - ac_site_files="$prefix/share/config.site $prefix/etc/config.site" -else - ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" -fi - -for ac_site_file in $ac_site_files -do - case $ac_site_file in #( - */*) : - ;; #( - *) : - ac_site_file=./$ac_site_file ;; -esac - if test -f "$ac_site_file" && test -r "$ac_site_file"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special files - # actually), so we avoid doing that. DJGPP emulates it as a regular file. - if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -printf "%s\n" "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . "$cache_file";; - *) . "./$cache_file";; - esac - fi -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -printf "%s\n" "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -# Test code for whether the C compiler supports C89 (global declarations) -ac_c_conftest_c89_globals=' -/* Does the compiler advertise C89 conformance? - Do not test the value of __STDC__, because some compilers set it to 0 - while being otherwise adequately conformant. */ -#if !defined __STDC__ -# error "Compiler does not advertise C89 conformance" -#endif - -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ -struct buf { int x; }; -struct buf * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not \xHH hex character constants. - These do not provoke an error unfortunately, instead are silently treated - as an "x". The following induces an error, until -std is added to get - proper ANSI mode. Curiously \x00 != x always comes out true, for an - array size at least. It is necessary to write \x00 == 0 to get something - that is true only with -std. */ -int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) '\''x'\'' -int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), - int, int);' - -# Test code for whether the C compiler supports C89 (body of main). -ac_c_conftest_c89_main=' -ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); -' - -# Test code for whether the C compiler supports C99 (global declarations) -ac_c_conftest_c99_globals=' -// Does the compiler advertise C99 conformance? -#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L -# error "Compiler does not advertise C99 conformance" -#endif - -#include -extern int puts (const char *); -extern int printf (const char *, ...); -extern int dprintf (int, const char *, ...); -extern void *malloc (size_t); - -// Check varargs macros. These examples are taken from C99 6.10.3.5. -// dprintf is used instead of fprintf to avoid needing to declare -// FILE and stderr. -#define debug(...) dprintf (2, __VA_ARGS__) -#define showlist(...) puts (#__VA_ARGS__) -#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) -static void -test_varargs_macros (void) -{ - int x = 1234; - int y = 5678; - debug ("Flag"); - debug ("X = %d\n", x); - showlist (The first, second, and third items.); - report (x>y, "x is %d but y is %d", x, y); -} - -// Check long long types. -#define BIG64 18446744073709551615ull -#define BIG32 4294967295ul -#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) -#if !BIG_OK - #error "your preprocessor is broken" -#endif -#if BIG_OK -#else - #error "your preprocessor is broken" -#endif -static long long int bignum = -9223372036854775807LL; -static unsigned long long int ubignum = BIG64; - -struct incomplete_array -{ - int datasize; - double data[]; -}; - -struct named_init { - int number; - const wchar_t *name; - double average; -}; - -typedef const char *ccp; - -static inline int -test_restrict (ccp restrict text) -{ - // See if C++-style comments work. - // Iterate through items via the restricted pointer. - // Also check for declarations in for loops. - for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) - continue; - return 0; -} - -// Check varargs and va_copy. -static bool -test_varargs (const char *format, ...) -{ - va_list args; - va_start (args, format); - va_list args_copy; - va_copy (args_copy, args); - - const char *str = ""; - int number = 0; - float fnumber = 0; - - while (*format) - { - switch (*format++) - { - case '\''s'\'': // string - str = va_arg (args_copy, const char *); - break; - case '\''d'\'': // int - number = va_arg (args_copy, int); - break; - case '\''f'\'': // float - fnumber = va_arg (args_copy, double); - break; - default: - break; - } - } - va_end (args_copy); - va_end (args); - - return *str && number && fnumber; -} -' - -# Test code for whether the C compiler supports C99 (body of main). -ac_c_conftest_c99_main=' - // Check bool. - _Bool success = false; - success |= (argc != 0); - - // Check restrict. - if (test_restrict ("String literal") == 0) - success = true; - char *restrict newvar = "Another string"; - - // Check varargs. - success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); - test_varargs_macros (); - - // Check flexible array members. - struct incomplete_array *ia = - malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); - ia->datasize = 10; - for (int i = 0; i < ia->datasize; ++i) - ia->data[i] = i * 1.234; - - // Check named initializers. - struct named_init ni = { - .number = 34, - .name = L"Test wide string", - .average = 543.34343, - }; - - ni.number = 58; - - int dynamic_array[ni.number]; - dynamic_array[0] = argv[0][0]; - dynamic_array[ni.number - 1] = 543; - - // work around unused variable warnings - ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' - || dynamic_array[ni.number - 1] != 543); -' - -# Test code for whether the C compiler supports C11 (global declarations) -ac_c_conftest_c11_globals=' -// Does the compiler advertise C11 conformance? -#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L -# error "Compiler does not advertise C11 conformance" -#endif - -// Check _Alignas. -char _Alignas (double) aligned_as_double; -char _Alignas (0) no_special_alignment; -extern char aligned_as_int; -char _Alignas (0) _Alignas (int) aligned_as_int; - -// Check _Alignof. -enum -{ - int_alignment = _Alignof (int), - int_array_alignment = _Alignof (int[100]), - char_alignment = _Alignof (char) -}; -_Static_assert (0 < -_Alignof (int), "_Alignof is signed"); - -// Check _Noreturn. -int _Noreturn does_not_return (void) { for (;;) continue; } - -// Check _Static_assert. -struct test_static_assert -{ - int x; - _Static_assert (sizeof (int) <= sizeof (long int), - "_Static_assert does not work in struct"); - long int y; -}; - -// Check UTF-8 literals. -#define u8 syntax error! -char const utf8_literal[] = u8"happens to be ASCII" "another string"; - -// Check duplicate typedefs. -typedef long *long_ptr; -typedef long int *long_ptr; -typedef long_ptr long_ptr; - -// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. -struct anonymous -{ - union { - struct { int i; int j; }; - struct { int k; long int l; } w; - }; - int m; -} v1; -' - -# Test code for whether the C compiler supports C11 (body of main). -ac_c_conftest_c11_main=' - _Static_assert ((offsetof (struct anonymous, i) - == offsetof (struct anonymous, w.k)), - "Anonymous union alignment botch"); - v1.i = 2; - v1.w.k = 5; - ok |= v1.i != 5; -' - -# Test code for whether the C compiler supports C11 (complete). -ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} -${ac_c_conftest_c99_globals} -${ac_c_conftest_c11_globals} - -int -main (int argc, char **argv) -{ - int ok = 0; - ${ac_c_conftest_c89_main} - ${ac_c_conftest_c99_main} - ${ac_c_conftest_c11_main} - return ok; -} -" - -# Test code for whether the C compiler supports C99 (complete). -ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} -${ac_c_conftest_c99_globals} - -int -main (int argc, char **argv) -{ - int ok = 0; - ${ac_c_conftest_c89_main} - ${ac_c_conftest_c99_main} - return ok; -} -" - -# Test code for whether the C compiler supports C89 (complete). -ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} - -int -main (int argc, char **argv) -{ - int ok = 0; - ${ac_c_conftest_c89_main} - return ok; -} -" - -as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" -as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" -as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" -as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" -as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" -as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" -as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" -as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" -as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" - -# Auxiliary files required by this configure script. -ac_aux_files="config.guess config.sub install-sh" - -# Locations in which to look for auxiliary files. -ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." - -# Search for a directory containing all of the required auxiliary files, -# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. -# If we don't find one directory that contains all the files we need, -# we report the set of missing files from the *first* directory in -# $ac_aux_dir_candidates and give up. -ac_missing_aux_files="" -ac_first_candidate=: -printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in $ac_aux_dir_candidates -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - as_found=: - - printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 - ac_aux_dir_found=yes - ac_install_sh= - for ac_aux in $ac_aux_files - do - # As a special case, if "install-sh" is required, that requirement - # can be satisfied by any of "install-sh", "install.sh", or "shtool", - # and $ac_install_sh is set appropriately for whichever one is found. - if test x"$ac_aux" = x"install-sh" - then - if test -f "${as_dir}install-sh"; then - printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 - ac_install_sh="${as_dir}install-sh -c" - elif test -f "${as_dir}install.sh"; then - printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 - ac_install_sh="${as_dir}install.sh -c" - elif test -f "${as_dir}shtool"; then - printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 - ac_install_sh="${as_dir}shtool install -c" - else - ac_aux_dir_found=no - if $ac_first_candidate; then - ac_missing_aux_files="${ac_missing_aux_files} install-sh" - else - break - fi - fi - else - if test -f "${as_dir}${ac_aux}"; then - printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 - else - ac_aux_dir_found=no - if $ac_first_candidate; then - ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" - else - break - fi - fi - fi - done - if test "$ac_aux_dir_found" = yes; then - ac_aux_dir="$as_dir" - break - fi - ac_first_candidate=false - - as_found=false -done -IFS=$as_save_IFS -if $as_found -then : - -else $as_nop - as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 -fi - - -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -if test -f "${ac_aux_dir}config.guess"; then - ac_config_guess="$SHELL ${ac_aux_dir}config.guess" -fi -if test -f "${ac_aux_dir}config.sub"; then - ac_config_sub="$SHELL ${ac_aux_dir}config.sub" -fi -if test -f "$ac_aux_dir/configure"; then - ac_configure="$SHELL ${ac_aux_dir}configure" -fi - -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in $ac_precious_vars; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val=\$ac_cv_env_${ac_var}_value - eval ac_new_val=\$ac_env_${ac_var}_value - case $ac_old_set,$ac_new_set in - set,) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - # differences in whitespace do not lead to failure. - ac_old_val_w=`echo x $ac_old_val` - ac_new_val_w=`echo x $ac_new_val` - if test "$ac_old_val_w" != "$ac_new_val_w"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - ac_cache_corrupted=: - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} - eval $ac_var=\$ac_old_val - fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) as_fn_append ac_configure_args " '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' - and start over" "$LINENO" 5 -fi -## -------------------- ## -## Main body of script. ## -## -------------------- ## - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - - - - - - - - - - - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -printf "%s\n" "$CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -printf "%s\n" "$ac_ct_CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -printf "%s\n" "$CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -printf "%s\n" "$CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -printf "%s\n" "$CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -printf "%s\n" "$ac_ct_CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. -set dummy ${ac_tool_prefix}clang; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}clang" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -printf "%s\n" "$CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "clang", so it can be a program name with args. -set dummy clang; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_CC+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="clang" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -printf "%s\n" "$ac_ct_CC" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -fi - - -test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion -version; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -printf %s "checking whether the C compiler works... " >&6; } -ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` - -# The possible output files: -ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" - -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { { ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else $as_nop - ac_file='' -fi -if test -z "$ac_file" -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -printf %s "checking for C compiler default output file name... " >&6; } -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -printf "%s\n" "$ac_file" >&6; } -ac_exeext=$ac_cv_exeext - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -printf %s "checking for suffix of executables... " >&6; } -if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else $as_nop - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest conftest$ac_cv_exeext -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -printf "%s\n" "$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main (void) -{ -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -ac_clean_files="$ac_clean_files conftest.out" -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -printf %s "checking whether we are cross compiling... " >&6; } -if test "$cross_compiling" != yes; then - { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if { ac_try='./conftest$ac_cv_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } - fi - fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -printf "%s\n" "$cross_compiling" >&6; } - -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out -ac_clean_files=$ac_clean_files_save -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -printf %s "checking for suffix of object files... " >&6; } -if test ${ac_cv_objext+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -printf "%s\n" "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -then : - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else $as_nop - printf "%s\n" "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -printf "%s\n" "$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 -printf %s "checking whether the compiler supports GNU C... " >&6; } -if test ${ac_cv_c_compiler_gnu+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - ac_compiler_gnu=yes -else $as_nop - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+y} -ac_save_CFLAGS=$CFLAGS -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -printf %s "checking whether $CC accepts -g... " >&6; } -if test ${ac_cv_prog_cc_g+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_prog_cc_g=yes -else $as_nop - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - -else $as_nop - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -printf "%s\n" "$ac_cv_prog_cc_g" >&6; } -if test $ac_test_CFLAGS; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -ac_prog_cc_stdc=no -if test x$ac_prog_cc_stdc = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 -printf %s "checking for $CC option to enable C11 features... " >&6; } -if test ${ac_cv_prog_cc_c11+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c11=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_c_conftest_c11_program -_ACEOF -for ac_arg in '' -std=gnu11 -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_prog_cc_c11=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam - test "x$ac_cv_prog_cc_c11" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC -fi - -if test "x$ac_cv_prog_cc_c11" = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c11" = x -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 -printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } - CC="$CC $ac_cv_prog_cc_c11" -fi - ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 - ac_prog_cc_stdc=c11 -fi -fi -if test x$ac_prog_cc_stdc = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 -printf %s "checking for $CC option to enable C99 features... " >&6; } -if test ${ac_cv_prog_cc_c99+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c99=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_c_conftest_c99_program -_ACEOF -for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_prog_cc_c99=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam - test "x$ac_cv_prog_cc_c99" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC -fi - -if test "x$ac_cv_prog_cc_c99" = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c99" = x -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 -printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } - CC="$CC $ac_cv_prog_cc_c99" -fi - ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 - ac_prog_cc_stdc=c99 -fi -fi -if test x$ac_prog_cc_stdc = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 -printf %s "checking for $CC option to enable C89 features... " >&6; } -if test ${ac_cv_prog_cc_c89+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_c_conftest_c89_program -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC -fi - -if test "x$ac_cv_prog_cc_c89" = xno -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -printf "%s\n" "unsupported" >&6; } -else $as_nop - if test "x$ac_cv_prog_cc_c89" = x -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -printf "%s\n" "none needed" >&6; } -else $as_nop - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } - CC="$CC $ac_cv_prog_cc_c89" -fi - ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 - ac_prog_cc_stdc=c89 -fi -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 -printf %s "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if test ${ac_cv_prog_CPP+y} -then : - printf %s "(cached) " >&6 -else $as_nop - # Double quotes because $CC needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO" -then : - -else $as_nop - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO" -then : - # Broken: success on invalid input. -continue -else $as_nop - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok -then : - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -printf "%s\n" "$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO" -then : - -else $as_nop - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO" -then : - # Broken: success on invalid input. -continue -else $as_nop - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok -then : - -else $as_nop - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - - - # Find a good install program. We prefer a C program (faster), -# so one script is as good as another. But avoid the broken or -# incompatible versions: -# SysV /etc/install, /usr/sbin/install -# SunOS /usr/etc/install -# IRIX /sbin/install -# AIX /bin/install -# AmigaOS /C/install, which installs bootblocks on floppy discs -# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag -# AFS /usr/afsws/bin/install, which mishandles nonexistent args -# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" -# OS/2's system install, which has a completely different semantic -# ./install, which can be erroneously created by make from ./install.sh. -# Reject install programs that cannot install multiple files. -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 -printf %s "checking for a BSD-compatible install... " >&6; } -if test -z "$INSTALL"; then -if test ${ac_cv_path_install+y} -then : - printf %s "(cached) " >&6 -else $as_nop - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - # Account for fact that we put trailing slashes in our PATH walk. -case $as_dir in #(( - ./ | /[cC]/* | \ - /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ - /usr/ucb/* ) ;; - *) - # OSF1 and SCO ODT 3.0 have their own names for install. - # Don't use installbsd from OSF since it installs stuff as root - # by default. - for ac_prog in ginstall scoinst install; do - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then - if test $ac_prog = install && - grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # AIX install. It has an incompatible calling convention. - : - elif test $ac_prog = install && - grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # program-specific install script used by HP pwplus--don't use. - : - else - rm -rf conftest.one conftest.two conftest.dir - echo one > conftest.one - echo two > conftest.two - mkdir conftest.dir - if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && - test -s conftest.one && test -s conftest.two && - test -s conftest.dir/conftest.one && - test -s conftest.dir/conftest.two - then - ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" - break 3 - fi - fi - fi - done - done - ;; -esac - - done -IFS=$as_save_IFS - -rm -rf conftest.one conftest.two conftest.dir - -fi - if test ${ac_cv_path_install+y}; then - INSTALL=$ac_cv_path_install - else - # As a last resort, use the slow shell script. Don't cache a - # value for INSTALL within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - INSTALL=$ac_install_sh - fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 -printf "%s\n" "$INSTALL" >&6; } - -# Use test -z because SunOS4 sh mishandles braces in ${var-val}. -# It thinks the first close brace ends the variable substitution. -test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' - -test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' - -test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' - - - -ac_header= ac_cache= -for ac_item in $ac_header_c_list -do - if test $ac_cache; then - ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" - if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then - printf "%s\n" "#define $ac_item 1" >> confdefs.h - fi - ac_header= ac_cache= - elif test $ac_header; then - ac_cache=$ac_item - else - ac_header=$ac_item - fi -done - - - - - - - - -if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes -then : - -printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h - -fi -ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" -case $ac_cv_c_uint8_t in #( - no|yes) ;; #( - *) - -printf "%s\n" "#define _UINT8_T 1" >>confdefs.h - - -printf "%s\n" "#define uint8_t $ac_cv_c_uint8_t" >>confdefs.h -;; - esac - -ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" -case $ac_cv_c_uint16_t in #( - no|yes) ;; #( - *) - - -printf "%s\n" "#define uint16_t $ac_cv_c_uint16_t" >>confdefs.h -;; - esac - -ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" -case $ac_cv_c_uint32_t in #( - no|yes) ;; #( - *) - -printf "%s\n" "#define _UINT32_T 1" >>confdefs.h - - -printf "%s\n" "#define uint32_t $ac_cv_c_uint32_t" >>confdefs.h -;; - esac - -ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" -case $ac_cv_c_uint64_t in #( - no|yes) ;; #( - *) - -printf "%s\n" "#define _UINT64_T 1" >>confdefs.h - - -printf "%s\n" "#define uint64_t $ac_cv_c_uint64_t" >>confdefs.h -;; - esac - - - ac_fn_c_check_type "$LINENO" "uintptr_t" "ac_cv_type_uintptr_t" "$ac_includes_default" -if test "x$ac_cv_type_uintptr_t" = xyes -then : - -printf "%s\n" "#define HAVE_UINTPTR_T 1" >>confdefs.h - -else $as_nop - for ac_type in 'unsigned int' 'unsigned long int' \ - 'unsigned long long int'; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -int -main (void) -{ -static int test_array [1 - 2 * !(sizeof (void *) <= sizeof ($ac_type))]; -test_array [0] = 0; -return test_array [0]; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - -printf "%s\n" "#define uintptr_t $ac_type" >>confdefs.h - - ac_type= -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - test -z "$ac_type" && break - done -fi - - - - ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default -" -if test "x$ac_cv_type_pid_t" = xyes -then : - -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - #if defined _WIN64 && !defined __CYGWIN__ - LLP64 - #endif - -int -main (void) -{ - - ; - return 0; -} - -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - ac_pid_type='int' -else $as_nop - ac_pid_type='__int64' -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - -printf "%s\n" "#define pid_t $ac_pid_type" >>confdefs.h - - -fi - - - -ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" -if test "x$ac_cv_type__Bool" = xyes -then : - -printf "%s\n" "#define HAVE__BOOL 1" >>confdefs.h - - -fi - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5 -printf %s "checking for stdbool.h that conforms to C99... " >&6; } -if test ${ac_cv_header_stdbool_h+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - - #ifndef __bool_true_false_are_defined - #error "__bool_true_false_are_defined is not defined" - #endif - char a[__bool_true_false_are_defined == 1 ? 1 : -1]; - - /* Regardless of whether this is C++ or "_Bool" is a - valid type name, "true" and "false" should be usable - in #if expressions and integer constant expressions, - and "bool" should be a valid type name. */ - - #if !true - #error "'true' is not true" - #endif - #if true != 1 - #error "'true' is not equal to 1" - #endif - char b[true == 1 ? 1 : -1]; - char c[true]; - - #if false - #error "'false' is not false" - #endif - #if false != 0 - #error "'false' is not equal to 0" - #endif - char d[false == 0 ? 1 : -1]; - - enum { e = false, f = true, g = false * true, h = true * 256 }; - - char i[(bool) 0.5 == true ? 1 : -1]; - char j[(bool) 0.0 == false ? 1 : -1]; - char k[sizeof (bool) > 0 ? 1 : -1]; - - struct sb { bool s: 1; bool t; } s; - char l[sizeof s.t > 0 ? 1 : -1]; - - /* The following fails for - HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ - bool m[h]; - char n[sizeof m == h * sizeof m[0] ? 1 : -1]; - char o[-1 - (bool) 0 < 0 ? 1 : -1]; - /* Catch a bug in an HP-UX C compiler. See - https://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html - https://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html - */ - bool p = true; - bool *pp = &p; - - /* C 1999 specifies that bool, true, and false are to be - macros, but C++ 2011 and later overrule this. */ - #if __cplusplus < 201103 - #ifndef bool - #error "bool is not defined" - #endif - #ifndef false - #error "false is not defined" - #endif - #ifndef true - #error "true is not defined" - #endif - #endif - - /* If _Bool is available, repeat with it all the tests - above that used bool. */ - #ifdef HAVE__BOOL - struct sB { _Bool s: 1; _Bool t; } t; - - char q[(_Bool) 0.5 == true ? 1 : -1]; - char r[(_Bool) 0.0 == false ? 1 : -1]; - char u[sizeof (_Bool) > 0 ? 1 : -1]; - char v[sizeof t.t > 0 ? 1 : -1]; - - _Bool w[h]; - char x[sizeof m == h * sizeof m[0] ? 1 : -1]; - char y[-1 - (_Bool) 0 < 0 ? 1 : -1]; - _Bool z = true; - _Bool *pz = &p; - #endif - -int -main (void) -{ - - bool ps = &s; - *pp |= p; - *pp |= ! p; - - #ifdef HAVE__BOOL - _Bool pt = &t; - *pz |= z; - *pz |= ! z; - #endif - - /* Refer to every declared value, so they cannot be - discarded as unused. */ - return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !j + !k - + !l + !m + !n + !o + !p + !pp + !ps - #ifdef HAVE__BOOL - + !q + !r + !u + !v + !w + !x + !y + !z + !pt - #endif - ); - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_header_stdbool_h=yes -else $as_nop - ac_cv_header_stdbool_h=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5 -printf "%s\n" "$ac_cv_header_stdbool_h" >&6; } - -if test $ac_cv_header_stdbool_h = yes; then - -printf "%s\n" "#define HAVE_STDBOOL_H 1" >>confdefs.h - -fi - -ac_fn_c_check_func "$LINENO" "getpagesize" "ac_cv_func_getpagesize" -if test "x$ac_cv_func_getpagesize" = xyes -then : - -fi - - - - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 -printf %s "checking whether byte ordering is bigendian... " >&6; } -if test ${ac_cv_c_bigendian+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_cv_c_bigendian=unknown - # See if we're dealing with a universal compiler. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifndef __APPLE_CC__ - not a universal capable compiler - #endif - typedef int dummy; - -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - - # Check for potential -arch flags. It is not universal unless - # there are at least two -arch flags with different values. - ac_arch= - ac_prev= - for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do - if test -n "$ac_prev"; then - case $ac_word in - i?86 | x86_64 | ppc | ppc64) - if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then - ac_arch=$ac_word - else - ac_cv_c_bigendian=universal - break - fi - ;; - esac - ac_prev= - elif test "x$ac_word" = "x-arch"; then - ac_prev=arch - fi - done -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - if test $ac_cv_c_bigendian = unknown; then - # See if sys/param.h defines the BYTE_ORDER macro. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - #include - -int -main (void) -{ -#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ - && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ - && LITTLE_ENDIAN) - bogus endian macros - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - # It does; now see whether it defined to BIG_ENDIAN or not. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - #include - -int -main (void) -{ -#if BYTE_ORDER != BIG_ENDIAN - not big endian - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_c_bigendian=yes -else $as_nop - ac_cv_c_bigendian=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - fi - if test $ac_cv_c_bigendian = unknown; then - # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -int -main (void) -{ -#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) - bogus endian macros - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - # It does; now see whether it defined to _BIG_ENDIAN or not. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -int -main (void) -{ -#ifndef _BIG_ENDIAN - not big endian - #endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - ac_cv_c_bigendian=yes -else $as_nop - ac_cv_c_bigendian=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - fi - if test $ac_cv_c_bigendian = unknown; then - # Compile a test program. - if test "$cross_compiling" = yes -then : - # Try to guess by grepping values from an object file. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -unsigned short int ascii_mm[] = - { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; - unsigned short int ascii_ii[] = - { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; - int use_ascii (int i) { - return ascii_mm[i] + ascii_ii[i]; - } - unsigned short int ebcdic_ii[] = - { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; - unsigned short int ebcdic_mm[] = - { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; - int use_ebcdic (int i) { - return ebcdic_mm[i] + ebcdic_ii[i]; - } - extern int foo; - -int -main (void) -{ -return use_ascii (foo) == use_ebcdic (foo); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then - ac_cv_c_bigendian=yes - fi - if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then - if test "$ac_cv_c_bigendian" = unknown; then - ac_cv_c_bigendian=no - else - # finding both strings is unlikely to happen, but who knows? - ac_cv_c_bigendian=unknown - fi - fi -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$ac_includes_default -int -main (void) -{ - - /* Are we little or big endian? From Harbison&Steele. */ - union - { - long int l; - char c[sizeof (long int)]; - } u; - u.l = 1; - return u.c[sizeof (long int) - 1] == 1; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO" -then : - ac_cv_c_bigendian=no -else $as_nop - ac_cv_c_bigendian=yes -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - - fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 -printf "%s\n" "$ac_cv_c_bigendian" >&6; } - case $ac_cv_c_bigendian in #( - yes) - printf "%s\n" "#define WORDS_BIGENDIAN 1" >>confdefs.h -;; #( - no) - ;; #( - universal) - -printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h - - ;; #( - *) - as_fn_error $? "unknown endianness - presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; - esac - - - - - - - - - - - - - - - - - - - - - - - # Make sure we can run config.sub. -$SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -printf %s "checking build system type... " >&6; } -if test ${ac_cv_build+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` -test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -printf "%s\n" "$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -printf %s "checking host system type... " >&6; } -if test ${ac_cv_host+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test "x$host_alias" = x; then - ac_cv_host=$ac_cv_build -else - ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || - as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 -fi - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -printf "%s\n" "$ac_cv_host" >&6; } -case $ac_cv_host in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; -esac -host=$ac_cv_host -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_host -shift -host_cpu=$1 -host_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -host_os=$* -IFS=$ac_save_IFS -case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac - - - -# Look for required tools. - - - - - -# Check for a working m4. -for ac_prog in gm4 m4 -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_M4+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$M4"; then - ac_cv_prog_M4="$M4" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_M4="$ac_prog" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -M4=$ac_cv_prog_M4 -if test -n "$M4"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $M4" >&5 -printf "%s\n" "$M4" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - - test -n "$M4" && break -done -test -n "$M4" || M4="as_fn_error $? "Cannot find m4" "$LINENO" 5" - - -# Check for a working assembler. -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args. -set dummy ${ac_tool_prefix}as; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_AS+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$AS"; then - ac_cv_prog_AS="$AS" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_AS="${ac_tool_prefix}as" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -AS=$ac_cv_prog_AS -if test -n "$AS"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AS" >&5 -printf "%s\n" "$AS" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_AS"; then - ac_ct_AS=$AS - # Extract the first word of "as", so it can be a program name with args. -set dummy as; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_AS+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_AS"; then - ac_cv_prog_ac_ct_AS="$ac_ct_AS" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_AS="as" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_AS=$ac_cv_prog_ac_ct_AS -if test -n "$ac_ct_AS"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AS" >&5 -printf "%s\n" "$ac_ct_AS" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_AS" = x; then - AS="as_fn_error $? "Cannot find a working assembler" "$LINENO" 5" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - AS=$ac_ct_AS - fi -else - AS="$ac_cv_prog_AS" -fi - - -# And ar. -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. -set dummy ${ac_tool_prefix}ar; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_AR+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$AR"; then - ac_cv_prog_AR="$AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_AR="${ac_tool_prefix}ar" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -AR=$ac_cv_prog_AR -if test -n "$AR"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 -printf "%s\n" "$AR" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_AR"; then - ac_ct_AR=$AR - # Extract the first word of "ar", so it can be a program name with args. -set dummy ar; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_AR+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_AR"; then - ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_AR="ar" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_AR=$ac_cv_prog_ac_ct_AR -if test -n "$ac_ct_AR"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 -printf "%s\n" "$ac_ct_AR" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_AR" = x; then - AR="as_fn_error $? "Cannot find a working ar" "$LINENO" 5" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - AR=$ac_ct_AR - fi -else - AR="$ac_cv_prog_AR" -fi - - -# And ld. -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ld", so it can be a program name with args. -set dummy ${ac_tool_prefix}ld; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_LD+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$LD"; then - ac_cv_prog_LD="$LD" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_LD="${ac_tool_prefix}ld" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -LD=$ac_cv_prog_LD -if test -n "$LD"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 -printf "%s\n" "$LD" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_LD"; then - ac_ct_LD=$LD - # Extract the first word of "ld", so it can be a program name with args. -set dummy ld; ac_word=$2 -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -printf %s "checking for $ac_word... " >&6; } -if test ${ac_cv_prog_ac_ct_LD+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if test -n "$ac_ct_LD"; then - ac_cv_prog_ac_ct_LD="$ac_ct_LD" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_LD="ld" - printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_LD=$ac_cv_prog_ac_ct_LD -if test -n "$ac_ct_LD"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LD" >&5 -printf "%s\n" "$ac_ct_LD" >&6; } -else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - - if test "x$ac_ct_LD" = x; then - LD="as_fn_error $? "Cannot find a working linker" "$LINENO" 5" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - LD=$ac_ct_LD - fi -else - LD="$ac_cv_prog_LD" -fi - - -# Now check if ld is a C compiler. -LDPREFIX= -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ld is a C compiler" >&5 -printf %s "checking whether ld is a C compiler... " >&6; } -if test ${exec_cv_ld_is_cc+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat <<_ACEOF > conftest.c - -int -main (void) -{ - - ; - return 0; -} -_ACEOF - exec_cv_ld_is_cc=yes - $LD -c conftest.c -o conftest.$OBJEXT >&5 2>&1 \ - || exec_cv_ld_is_cc=no - rm -f conftest.c conftest.$OBJEXT -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_ld_is_cc" >&5 -printf "%s\n" "$exec_cv_ld_is_cc" >&6; } - -# And if as is a C compiler. -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether as is a C compiler" >&5 -printf %s "checking whether as is a C compiler... " >&6; } -if test ${exec_cv_as_is_cc+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat <<_ACEOF > conftest.c - -int -main (void) -{ - - ; - return 0; -} -_ACEOF - exec_cv_as_is_cc=yes - $AS -c conftest.c -o conftest.$OBJEXT >&5 2>&1 \ - || exec_cv_as_is_cc=no - rm -f conftest.c conftest.$OBJEXT -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_as_is_cc" >&5 -printf "%s\n" "$exec_cv_as_is_cc" >&6; } - -# If ld is a C compiler, pass `-nostdlib', `-nostartfiles', and -# `-static'. Also, set LDPREFIX to -Wl, -if test "x$exec_cv_ld_is_cc" = "xyes" -then : - LOADERFLAGS="$LOADERFLAGS -nostdlib -nostartfiles -static" - LDPREFIX=-Wl, -fi - -# If as is a C compiler, add `-c' to ASFLAGS. -if test "x$exec_cv_as_is_cc" = "xyes" -then : - ASFLAGS="$ASFLAGS -c" -fi - - - - - -# Determine the system type and define appropriate macros. -exec_loader= -is_mips= -OBJS="exec.o trace.o" - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 -printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } -if test ${ac_cv_c_undeclared_builtin_options+y} -then : - printf %s "(cached) " >&6 -else $as_nop - ac_save_CFLAGS=$CFLAGS - ac_cv_c_undeclared_builtin_options='cannot detect' - for ac_arg in '' -fno-builtin; do - CFLAGS="$ac_save_CFLAGS $ac_arg" - # This test program should *not* compile successfully. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ -(void) strchr; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - -else $as_nop - # This test program should compile successfully. - # No library function is consistently available on - # freestanding implementations, so test against a dummy - # declaration. Include always-available headers on the - # off chance that they somehow elicit warnings. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include -extern void ac_decl (int, char *); - -int -main (void) -{ -(void) ac_decl (0, (char *) 0); - (void) ac_decl; - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - if test x"$ac_arg" = x -then : - ac_cv_c_undeclared_builtin_options='none needed' -else $as_nop - ac_cv_c_undeclared_builtin_options=$ac_arg -fi - break -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - done - CFLAGS=$ac_save_CFLAGS - -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 -printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } - case $ac_cv_c_undeclared_builtin_options in #( - 'cannot detect') : - { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot make $CC report undeclared builtins -See \`config.log' for more details" "$LINENO" 5; } ;; #( - 'none needed') : - ac_c_undeclared_builtin_options='' ;; #( - *) : - ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;; -esac - -case $host in #( - x86_64-*linux*) : - ac_fn_c_check_member "$LINENO" "struct user_regs_struct" "rdi" "ac_cv_member_struct_user_regs_struct_rdi" " -#include - -" -if test "x$ac_cv_member_struct_user_regs_struct_rdi" = xyes -then : - printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h - - printf "%s\n" "#define USER_HEADER " >>confdefs.h - - printf "%s\n" "#define USER_REGS_STRUCT struct user_regs_struct" >>confdefs.h - - printf "%s\n" "#define SYSCALL_NUM_REG orig_rax" >>confdefs.h - - printf "%s\n" "#define SYSCALL_RET_REG rax" >>confdefs.h - - printf "%s\n" "#define SYSCALL_ARG_REG rdi" >>confdefs.h - - printf "%s\n" "#define SYSCALL_ARG1_REG rsi" >>confdefs.h - - printf "%s\n" "#define STACK_POINTER rsp" >>confdefs.h - - printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h - - printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h - - printf "%s\n" "#define EXEC_64 1" >>confdefs.h - - printf "%s\n" "#define ABI_RED_ZONE 128" >>confdefs.h - - printf "%s\n" "#define EXECUTABLE_BASE 0x555555554000" >>confdefs.h - - printf "%s\n" "#define INTERPRETER_BASE 0x600000000000" >>confdefs.h - - printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h - - printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h - - -ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " -#include - -" "$ac_c_undeclared_builtin_options" "CFLAGS" -if test "x$ac_cv_have_decl___NR_clone3" = xyes -then : - printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h - -fi - - # Make sure the loader doesn't conflict with other position - # dependent code. - LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x200000000000" - exec_loader=loader-x86_64.s -else $as_nop - as_fn_error $? "Missing \`rdi' in user_regs_struct" "$LINENO" 5 -fi - ;; #( - i[34567]86-*linux*) : - ac_fn_c_check_member "$LINENO" "struct user_regs_struct" "edi" "ac_cv_member_struct_user_regs_struct_edi" " -#include - -" -if test "x$ac_cv_member_struct_user_regs_struct_edi" = xyes -then : - printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h - - printf "%s\n" "#define USER_HEADER " >>confdefs.h - - printf "%s\n" "#define USER_REGS_STRUCT struct user_regs_struct" >>confdefs.h - - printf "%s\n" "#define SYSCALL_NUM_REG orig_eax" >>confdefs.h - - printf "%s\n" "#define SYSCALL_RET_REG eax" >>confdefs.h - - printf "%s\n" "#define SYSCALL_ARG_REG ebx" >>confdefs.h - - printf "%s\n" "#define SYSCALL_ARG1_REG ecx" >>confdefs.h - - printf "%s\n" "#define STACK_POINTER esp" >>confdefs.h - - printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h - - printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h - - printf "%s\n" "#define EXECUTABLE_BASE 0x0f000000" >>confdefs.h - - printf "%s\n" "#define INTERPRETER_BASE 0xaf000000" >>confdefs.h - - printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h - - printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h - - -ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " -#include - -" "$ac_c_undeclared_builtin_options" "CFLAGS" -if test "x$ac_cv_have_decl___NR_clone3" = xyes -then : - printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h - -fi - - # Make sure the loader doesn't conflict with other position - # dependent code. - LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0xa0000000" - exec_loader=loader-x86.s -else $as_nop - as_fn_error $? "Missing \`edi' in user_regs_struct" "$LINENO" 5 -fi - ;; #( - aarch64-*linux*) : - ac_fn_c_check_member "$LINENO" "struct user_regs_struct" "sp" "ac_cv_member_struct_user_regs_struct_sp" " -#include - -" -if test "x$ac_cv_member_struct_user_regs_struct_sp" = xyes -then : - printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h - - printf "%s\n" "#define USER_HEADER " >>confdefs.h - - printf "%s\n" "#define USER_REGS_STRUCT struct user_regs_struct" >>confdefs.h - - printf "%s\n" "#define SYSCALL_NUM_REG regs[8]" >>confdefs.h - - printf "%s\n" "#define SYSCALL_RET_REG regs[0]" >>confdefs.h - - printf "%s\n" "#define SYSCALL_ARG_REG regs[0]" >>confdefs.h - - printf "%s\n" "#define SYSCALL_ARG1_REG regs[1]" >>confdefs.h - - printf "%s\n" "#define STACK_POINTER sp" >>confdefs.h - - printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h - - printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h - - printf "%s\n" "#define EXEC_64 1" >>confdefs.h - - printf "%s\n" "#define EXECUTABLE_BASE 0x3000000000" >>confdefs.h - - printf "%s\n" "#define INTERPRETER_BASE 0x3f00000000" >>confdefs.h - - printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h - - printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h - - -ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " -#include - -" "$ac_c_undeclared_builtin_options" "CFLAGS" -if test "x$ac_cv_have_decl___NR_clone3" = xyes -then : - printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h - -fi - - # Make sure the loader doesn't conflict with other position - # dependent code. ARM places rather significant restrictions on - # virtual addresses for a 64 bit architecture. - LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x2000000000" - exec_loader=loader-aarch64.s -else $as_nop - as_fn_error $? "Missing \`sp' in user_regs_struct" "$LINENO" 5 -fi - ;; #( - arm*linux*eabi* | armv7*linux*) : - ac_fn_c_check_member "$LINENO" "struct user_regs" "uregs" "ac_cv_member_struct_user_regs_uregs" " -#include - -" -if test "x$ac_cv_member_struct_user_regs_uregs" = xyes -then : - printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h - - printf "%s\n" "#define USER_HEADER " >>confdefs.h - - printf "%s\n" "#define USER_REGS_STRUCT struct user_regs" >>confdefs.h - - printf "%s\n" "#define SYSCALL_NUM_REG uregs[7]" >>confdefs.h - - printf "%s\n" "#define SYSCALL_RET_REG uregs[0]" >>confdefs.h - - printf "%s\n" "#define SYSCALL_ARG_REG uregs[0]" >>confdefs.h - - printf "%s\n" "#define SYSCALL_ARG1_REG uregs[1]" >>confdefs.h - - printf "%s\n" "#define STACK_POINTER uregs[13]" >>confdefs.h - - printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h - - printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h - - printf "%s\n" "#define EXECUTABLE_BASE 0x0f000000" >>confdefs.h - - printf "%s\n" "#define INTERPRETER_BASE 0x1f000000" >>confdefs.h - - printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h - - printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h - - -ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " -#include - -" "$ac_c_undeclared_builtin_options" "CFLAGS" -if test "x$ac_cv_have_decl___NR_clone3" = xyes -then : - printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h - -fi - - LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" - exec_loader=loader-armeabi.s -else $as_nop - as_fn_error $? "Missing \`uregs' in user_regs_struct" "$LINENO" 5 -fi - ;; #( - mipsel*linux*) : - printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h - - printf "%s\n" "#define USER_HEADER \"mipsel-user.h\"" >>confdefs.h - - printf "%s\n" "#define USER_REGS_STRUCT struct mipsel_regs" >>confdefs.h - - printf "%s\n" "#define SYSCALL_NUM_REG gregs[2]" >>confdefs.h - # v0 - printf "%s\n" "#define SYSCALL_RET_REG gregs[4]" >>confdefs.h - # a0 - printf "%s\n" "#define SYSCALL_ARG_REG gregs[4]" >>confdefs.h - # a0 - printf "%s\n" "#define SYSCALL_ARG1_REG gregs[5]" >>confdefs.h - # a1 - printf "%s\n" "#define STACK_POINTER gregs[29]" >>confdefs.h - # sp - printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h - - printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h - - printf "%s\n" "#define EXECUTABLE_BASE 0x0f000000" >>confdefs.h - - printf "%s\n" "#define INTERPRETER_BASE 0x1f000000" >>confdefs.h - - printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h - - printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h - - ac_fn_check_decl "$LINENO" "_MIPS_SIM" "ac_cv_have_decl__MIPS_SIM" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS" -if test "x$ac_cv_have_decl__MIPS_SIM" = xyes -then : - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether MIPS NABI calling convention is used" >&5 -printf %s "checking whether MIPS NABI calling convention is used... " >&6; } -if test ${exec_cv_mips_nabi+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include - -int -main (void) -{ - -#ifndef __mips64__ -#if _MIPS_SIM == _ABIO32 -OABI in use. -#endif /* _MIPS_SIM == _ABIO32 */ -#endif /* !__mips64__ */ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - exec_cv_mips_nabi=yes -else $as_nop - exec_cv_mips_nabi=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_mips_nabi" >&5 -printf "%s\n" "$exec_cv_mips_nabi" >&6; } - -if test "x$exec_cv_mips_nabi" != "xno" -then : - -printf "%s\n" "#define MIPS_NABI 1" >>confdefs.h - -else $as_nop - OBJS="$OBJS mipsfpu.o" -fi - -else $as_nop - as_fn_error $? "_MIPS_SIM could not be determined" "$LINENO" 5, - [ -#include -] -fi - -ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " -#include - -" "$ac_c_undeclared_builtin_options" "CFLAGS" -if test "x$ac_cv_have_decl___NR_clone3" = xyes -then : - printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h - -fi - - LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" - is_mips=yes - exec_loader=loader-mipsel.s ;; #( - mips64el*linux*) : - printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h - - printf "%s\n" "#define USER_HEADER \"mipsel-user.h\"" >>confdefs.h - - printf "%s\n" "#define USER_REGS_STRUCT struct mipsel_regs" >>confdefs.h - - printf "%s\n" "#define SYSCALL_NUM_REG gregs[2]" >>confdefs.h - # v0 - printf "%s\n" "#define SYSCALL_RET_REG gregs[4]" >>confdefs.h - # a0 - printf "%s\n" "#define SYSCALL_ARG_REG gregs[4]" >>confdefs.h - # a0 - printf "%s\n" "#define SYSCALL_ARG1_REG gregs[5]" >>confdefs.h - # a1 - printf "%s\n" "#define STACK_POINTER gregs[29]" >>confdefs.h - # sp - printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h - - printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h - - printf "%s\n" "#define EXEC_64 1" >>confdefs.h - - printf "%s\n" "#define EXECUTABLE_BASE 0x400000" >>confdefs.h - - printf "%s\n" "#define INTERPRETER_BASE 0x3f00000000" >>confdefs.h - - printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h - - printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h - - -ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " -#include - -" "$ac_c_undeclared_builtin_options" "CFLAGS" -if test "x$ac_cv_have_decl___NR_clone3" = xyes -then : - printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h - -fi - - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether MIPS NABI calling convention is used" >&5 -printf %s "checking whether MIPS NABI calling convention is used... " >&6; } -if test ${exec_cv_mips_nabi+y} -then : - printf %s "(cached) " >&6 -else $as_nop - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -#include - -int -main (void) -{ - -#ifndef __mips64__ -#if _MIPS_SIM == _ABIO32 -OABI in use. -#endif /* _MIPS_SIM == _ABIO32 */ -#endif /* !__mips64__ */ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - exec_cv_mips_nabi=yes -else $as_nop - exec_cv_mips_nabi=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_mips_nabi" >&5 -printf "%s\n" "$exec_cv_mips_nabi" >&6; } - -if test "x$exec_cv_mips_nabi" != "xno" -then : - -printf "%s\n" "#define MIPS_NABI 1" >>confdefs.h - -else $as_nop - OBJS="$OBJS mipsfpu.o" -fi - - LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x3e00000000" - is_mips=yes - exec_loader=loader-mips64el.s ;; #( - *) : - as_fn_error $? "Please port libexec to $host" "$LINENO" 5 ;; #( - *) : - ;; -esac - -MIPS_N32=$exec_cv_mips_nabi - - - - - -# Make the assembler optimize for code size. Don't do this on MIPS, -# as the assembler code manages branch delays manually. - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether as understands -O" >&5 -printf %s "checking whether as understands -O... " >&6; } -if test ${exec_cv_as_O+y} -then : - printf %s "(cached) " >&6 -else $as_nop - exec_cv_as_O=no - cat <<_ACEOF >conftest.s - .section text - .global _start -_start: - -_ACEOF - $AS $ASFLAGS -O conftest.s -o conftest.$OBJEXT \ - >&5 2>&1 \ - && exec_cv_as_O=yes - rm -f conftest.s conftest.$OBJEXT -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_as_O" >&5 -printf "%s\n" "$exec_cv_as_O" >&6; } - -if test "$exec_cv_as_O" = "yes" \ - && test "$is_mips" != "yes" -then : - ASFLAGS="$ASFLAGS -O" -fi - -# Make the assembler generate debug information. - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether as understands -g" >&5 -printf %s "checking whether as understands -g... " >&6; } -if test ${exec_cv_as_g+y} -then : - printf %s "(cached) " >&6 -else $as_nop - exec_cv_as_g=no - cat <<_ACEOF >conftest.s - .section text - .global _start -_start: - -_ACEOF - $AS $ASFLAGS -g conftest.s -o conftest.$OBJEXT \ - >&5 2>&1 \ - && exec_cv_as_g=yes - rm -f conftest.s conftest.$OBJEXT -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_as_g" >&5 -printf "%s\n" "$exec_cv_as_g" >&6; } -if test "$exec_cv_as_g" = "yes" -then : - ASFLAGS="$ASFLAGS -g" -fi - -# Check for the ability to automatically generate dependencies for C -# source files. -AUTO_DEPEND=no -if test "x$GCC" = xyes -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether gcc understands -MMD -MF" >&5 -printf %s "checking whether gcc understands -MMD -MF... " >&6; } -if test ${exec_cv_autodepend+y} -then : - printf %s "(cached) " >&6 -else $as_nop - SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -MMD -MF deps.d -MP" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main (void) -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO" -then : - exec_cv_autodepend=yes -else $as_nop - exec_cv_autodepend=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext - CFLAGS="$SAVE_CFLAGS" - test -f deps.d || emacs_cv_autodepend=no - rm -rf deps.d -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_autodepend" >&5 -printf "%s\n" "$exec_cv_autodepend" >&6; } - if test "x$exec_cv_autodepend" = xyes -then : - AUTO_DEPEND=yes - as_dir=deps; as_fn_mkdir_p -fi -fi - -# Now check for some other stuff. - -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for 'find' args to delete a file" >&5 -printf %s "checking for 'find' args to delete a file... " >&6; } -if test ${exec_cv_find_delete+y} -then : - printf %s "(cached) " >&6 -else $as_nop - if touch conftest.tmp && find conftest.tmp -delete 2>/dev/null && - test ! -f conftest.tmp -then : - exec_cv_find_delete="-delete" -else $as_nop - exec_cv_find_delete="-exec rm -f {} ';'" -fi -fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_find_delete" >&5 -printf "%s\n" "$exec_cv_find_delete" >&6; } -FIND_DELETE=$exec_cv_find_delete - - -ac_config_headers="$ac_config_headers config.h" - -ac_config_files="$ac_config_files Makefile config-mips.m4" - - - - - - - - - - -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. - -_ACEOF - -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, we kill variables containing newlines. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -( - for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - - (set) 2>&1 | - case $as_nl`(ac_space=' '; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes: double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \. - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; #( - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) | - sed ' - /^ac_cv_env_/b end - t clear - :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ - t end - s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - :end' >>confcache -if diff "$cache_file" confcache >/dev/null 2>&1; then :; else - if test -w "$cache_file"; then - if test "x$cache_file" != "x/dev/null"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -printf "%s\n" "$as_me: updating cache $cache_file" >&6;} - if test ! -f "$cache_file" || test -h "$cache_file"; then - cat confcache >"$cache_file" - else - case $cache_file in #( - */* | ?:*) - mv -f confcache "$cache_file"$$ && - mv -f "$cache_file"$$ "$cache_file" ;; #( - *) - mv -f confcache "$cache_file" ;; - esac - fi - fi - else - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} - fi -fi -rm -f confcache - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -DEFS=-DHAVE_CONFIG_H - -ac_libobjs= -ac_ltlibobjs= -U= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` - # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR - # will be set to the directory where LIBOBJS objects are built. - as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" - as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - - - -: "${CONFIG_STATUS=./config.status}" -ac_write_fail=0 -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} -as_write_fail=0 -cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. - -debug=false -ac_cs_recheck=false -ac_cs_silent=false - -SHELL=\${CONFIG_SHELL-$SHELL} -export SHELL -_ASEOF -cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -as_nop=: -if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 -then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else $as_nop - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - - -# Reset variables that may have inherited troublesome values from -# the environment. - -# IFS needs to be set, to space, tab, and newline, in precisely that order. -# (If _AS_PATH_WALK were called with IFS unset, it would have the -# side effect of setting IFS to empty, thus disabling word splitting.) -# Quoting is to prevent editors from complaining about space-tab. -as_nl=' -' -export as_nl -IFS=" "" $as_nl" - -PS1='$ ' -PS2='> ' -PS4='+ ' - -# Ensure predictable behavior from utilities with locale-dependent output. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# We cannot yet rely on "unset" to work, but we need these variables -# to be unset--not just set to an empty or harmless value--now, to -# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct -# also avoids known problems related to "unset" and subshell syntax -# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). -for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH -do eval test \${$as_var+y} \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done - -# Ensure that fds 0, 1, and 2 are open. -if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi -if (exec 3>&2) ; then :; else exec 2>/dev/null; fi - -# The user is always right. -if ${PATH_SEPARATOR+false} :; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - case $as_dir in #((( - '') as_dir=./ ;; - */) ;; - *) as_dir=$as_dir/ ;; - esac - test -r "$as_dir$0" && as_myself=$as_dir$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - printf "%s\n" "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null -then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else $as_nop - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null -then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else $as_nop - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - - -# Determine whether it's possible to make 'echo' print without a newline. -# These variables are no longer used directly by Autoconf, but are AC_SUBSTed -# for compatibility with existing Makefiles. -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -# For backward compatibility with old third-party macros, we provide -# the shell variables $as_echo and $as_echo_n. New code should use -# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. -as_echo='printf %s\n' -as_echo_n='printf %s' - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -exec 6>&1 -## ----------------------------------- ## -## Main body of $CONFIG_STATUS script. ## -## ----------------------------------- ## -_ASEOF -test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# Save the log message, to keep $0 and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. -ac_log=" -This file was extended by libexec $as_me 30.0.50, which was -generated by GNU Autoconf 2.71. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -on `(hostname || uname -n) 2>/dev/null | sed 1q` -" - -_ACEOF - -case $ac_config_files in *" -"*) set x $ac_config_files; shift; ac_config_files=$*;; -esac - -case $ac_config_headers in *" -"*) set x $ac_config_headers; shift; ac_config_headers=$*;; -esac - - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# Files that config.status was made for. -config_files="$ac_config_files" -config_headers="$ac_config_headers" - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions -from templates according to the current configuration. Unless the files -and actions are specified as TAGs, all are instantiated by default. - -Usage: $0 [OPTION]... [TAG]... - - -h, --help print this help, then exit - -V, --version print version number and configuration settings, then exit - --config print configuration, then exit - -q, --quiet, --silent - do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - --header=FILE[:TEMPLATE] - instantiate the configuration header FILE - -Configuration files: -$config_files - -Configuration headers: -$config_headers - -Report bugs to . -libexec home page: ." - -_ACEOF -ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` -ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config='$ac_cs_config_escaped' -ac_cs_version="\\ -libexec config.status 30.0.50 -configured by $0, generated by GNU Autoconf 2.71, - with options \\"\$ac_cs_config\\" - -Copyright (C) 2021 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." - -ac_pwd='$ac_pwd' -srcdir='$srcdir' -INSTALL='$INSTALL' -test -n "\$AWK" || AWK=awk -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# The default lists apply if the user does not specify any file. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=?*) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` - ac_shift=: - ;; - --*=) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; - *) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - esac - - case $ac_option in - # Handling of the options. - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - printf "%s\n" "$ac_cs_version"; exit ;; - --config | --confi | --conf | --con | --co | --c ) - printf "%s\n" "$ac_cs_config"; exit ;; - --debug | --debu | --deb | --de | --d | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; - esac - as_fn_append CONFIG_FILES " '$ac_optarg'" - ac_need_defaults=false;; - --header | --heade | --head | --hea ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - as_fn_append CONFIG_HEADERS " '$ac_optarg'" - ac_need_defaults=false;; - --he | --h) - # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' -Try \`$0 --help' for more information.";; - --help | --hel | -h ) - printf "%s\n" "$ac_cs_usage"; exit ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; - - *) as_fn_append ac_config_targets " $1" - ac_need_defaults=false ;; - - esac - shift -done - -ac_configure_extra_args= - -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion - shift - \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 - CONFIG_SHELL='$SHELL' - export CONFIG_SHELL - exec "\$@" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX - printf "%s\n" "$ac_log" -} >&5 - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - -# Handling of arguments. -for ac_config_target in $ac_config_targets -do - case $ac_config_target in - "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; - "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; - "config-mips.m4") CONFIG_FILES="$CONFIG_FILES config-mips.m4" ;; - - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; - esac -done - - -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files - test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers -fi - -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason against having it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Hook for its removal unless debugging. -# Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. -$debug || -{ - tmp= ac_tmp= - trap 'exit_status=$? - : "${ac_tmp:=$tmp}" - { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status -' 0 - trap 'as_fn_exit 1' 1 2 13 15 -} -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -d "$tmp" -} || -{ - tmp=./conf$$-$RANDOM - (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 -ac_tmp=$tmp - -# Set up the scripts for CONFIG_FILES section. -# No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. -if test -n "$CONFIG_FILES"; then - - -ac_cr=`echo X | tr X '\015'` -# On cygwin, bash can eat \r inside `` if the user requested igncr. -# But we know of no other shell where ac_cr would be empty at this -# point, so we can use a bashism as a fallback. -if test "x$ac_cr" = x; then - eval ac_cr=\$\'\\r\' -fi -ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` -if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' -else - ac_cs_awk_cr=$ac_cr -fi - -echo 'BEGIN {' >"$ac_tmp/subs1.awk" && -_ACEOF - - -{ - echo "cat >conf$$subs.awk <<_ACEOF" && - echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && - echo "_ACEOF" -} >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - - ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` - if test $ac_delim_n = $ac_delim_num; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done -rm -f conf$$subs.sh - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && -_ACEOF -sed -n ' -h -s/^/S["/; s/!.*/"]=/ -p -g -s/^[^!]*!// -:repl -t repl -s/'"$ac_delim"'$// -t delim -:nl -h -s/\(.\{148\}\)..*/\1/ -t more1 -s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ -p -n -b repl -:more1 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t nl -:delim -h -s/\(.\{148\}\)..*/\1/ -t more2 -s/["\\]/\\&/g; s/^/"/; s/$/"/ -p -b -:more2 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t delim -' >$CONFIG_STATUS || ac_write_fail=1 -rm -f conf$$subs.awk -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACAWK -cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && - for (key in S) S_is_set[key] = 1 - FS = "" - -} -{ - line = $ 0 - nfields = split(line, field, "@") - substed = 0 - len = length(field[1]) - for (i = 2; i < nfields; i++) { - key = field[i] - keylen = length(key) - if (S_is_set[key]) { - value = S[key] - line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) - len += length(value) + length(field[++i]) - substed = 1 - } else - len += 1 + keylen - } - - print line -} - -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then - sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" -else - cat -fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 -_ACEOF - -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// -s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// -s/^[^=]*=[ ]*$// -}' -fi - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -fi # test -n "$CONFIG_FILES" - -# Set up the scripts for CONFIG_HEADERS section. -# No need to generate them if there are no CONFIG_HEADERS. -# This happens for instance with `./config.status Makefile'. -if test -n "$CONFIG_HEADERS"; then -cat >"$ac_tmp/defines.awk" <<\_ACAWK || -BEGIN { -_ACEOF - -# Transform confdefs.h into an awk script `defines.awk', embedded as -# here-document in config.status, that substitutes the proper values into -# config.h.in to produce config.h. - -# Create a delimiter string that does not exist in confdefs.h, to ease -# handling of long lines. -ac_delim='%!_!# ' -for ac_last_try in false false :; do - ac_tt=`sed -n "/$ac_delim/p" confdefs.h` - if test -z "$ac_tt"; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done - -# For the awk script, D is an array of macro values keyed by name, -# likewise P contains macro parameters if any. Preserve backslash -# newline sequences. - -ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* -sed -n ' -s/.\{148\}/&'"$ac_delim"'/g -t rset -:rset -s/^[ ]*#[ ]*define[ ][ ]*/ / -t def -d -:def -s/\\$// -t bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3"/p -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p -d -:bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3\\\\\\n"\\/p -t cont -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p -t cont -d -:cont -n -s/.\{148\}/&'"$ac_delim"'/g -t clear -:clear -s/\\$// -t bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/"/p -d -:bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p -b cont -' >$CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - for (key in D) D_is_set[key] = 1 - FS = "" -} -/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { - line = \$ 0 - split(line, arg, " ") - if (arg[1] == "#") { - defundef = arg[2] - mac1 = arg[3] - } else { - defundef = substr(arg[1], 2) - mac1 = arg[2] - } - split(mac1, mac2, "(") #) - macro = mac2[1] - prefix = substr(line, 1, index(line, defundef) - 1) - if (D_is_set[macro]) { - # Preserve the white space surrounding the "#". - print prefix "define", macro P[macro] D[macro] - next - } else { - # Replace #undef with comments. This is necessary, for example, - # in the case of _POSIX_SOURCE, which is predefined and required - # on some systems where configure will not decide to define it. - if (defundef == "undef") { - print "/*", prefix defundef, macro, "*/" - next - } - } -} -{ print } -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 -fi # test -n "$CONFIG_HEADERS" - - -eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " -shift -for ac_tag -do - case $ac_tag in - :[FHLC]) ac_mode=$ac_tag; continue;; - esac - case $ac_mode$ac_tag in - :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; - :[FH]-) ac_tag=-:-;; - :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; - esac - ac_save_IFS=$IFS - IFS=: - set x $ac_tag - IFS=$ac_save_IFS - shift - ac_file=$1 - shift - - case $ac_mode in - :L) ac_source=$1;; - :[FH]) - ac_file_inputs= - for ac_f - do - case $ac_f in - -) ac_f="$ac_tmp/stdin";; - *) # Look for the file first in the build tree, then in the source tree - # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. - test -f "$ac_f" || - case $ac_f in - [\\/$]*) false;; - *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; - esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; - esac - case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac - as_fn_append ac_file_inputs " '$ac_f'" - done - - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - configure_input='Generated from '` - printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' - `' by configure.' - if test x"$ac_file" != x-; then - configure_input="$ac_file. $configure_input" - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -printf "%s\n" "$as_me: creating $ac_file" >&6;} - fi - # Neutralize special characters interpreted by sed in replacement strings. - case $configure_input in #( - *\&* | *\|* | *\\* ) - ac_sed_conf_input=`printf "%s\n" "$configure_input" | - sed 's/[\\\\&|]/\\\\&/g'`;; #( - *) ac_sed_conf_input=$configure_input;; - esac - - case $ac_tag in - *:-:* | *:-) cat >"$ac_tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; - esac - ;; - esac - - ac_dir=`$as_dirname -- "$ac_file" || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -printf "%s\n" X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - as_dir="$ac_dir"; as_fn_mkdir_p - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - - case $ac_mode in - :F) - # - # CONFIG_FILE - # - - case $INSTALL in - [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; - *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; - esac -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# If the template does not know about datarootdir, expand it. -# FIXME: This hack should be removed a few years after 2.60. -ac_datarootdir_hack=; ac_datarootdir_seen= -ac_sed_dataroot=' -/datarootdir/ { - p - q -} -/@datadir@/p -/@docdir@/p -/@infodir@/p -/@localedir@/p -/@mandir@/p' -case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in -*datarootdir*) ac_datarootdir_seen=yes;; -*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - ac_datarootdir_hack=' - s&@datadir@&$datadir&g - s&@docdir@&$docdir&g - s&@infodir@&$infodir&g - s&@localedir@&$localedir&g - s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; -esac -_ACEOF - -# Neutralize VPATH when `$srcdir' = `.'. -# Shell code in configure.ac might set extrasub. -# FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_sed_extra="$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s|@configure_input@|$ac_sed_conf_input|;t t -s&@top_builddir@&$ac_top_builddir_sub&;t t -s&@top_build_prefix@&$ac_top_build_prefix&;t t -s&@srcdir@&$ac_srcdir&;t t -s&@abs_srcdir@&$ac_abs_srcdir&;t t -s&@top_srcdir@&$ac_top_srcdir&;t t -s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t -s&@builddir@&$ac_builddir&;t t -s&@abs_builddir@&$ac_abs_builddir&;t t -s&@abs_top_builddir@&$ac_abs_top_builddir&;t t -s&@INSTALL@&$ac_INSTALL&;t t -$ac_datarootdir_hack -" -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ - >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - -test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ - "$ac_tmp/out"`; test -z "$ac_out"; } && - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 -printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} - - rm -f "$ac_tmp/stdin" - case $ac_file in - -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; - *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; - esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - ;; - :H) - # - # CONFIG_HEADER - # - if test x"$ac_file" != x-; then - { - printf "%s\n" "/* $configure_input */" >&1 \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" - } >"$ac_tmp/config.h" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 -printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} - else - rm -f "$ac_file" - mv "$ac_tmp/config.h" "$ac_file" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - fi - else - printf "%s\n" "/* $configure_input */" >&1 \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error $? "could not create -" "$LINENO" 5 - fi - ;; - - - esac - -done # for ac_tag - - -as_fn_exit 0 -_ACEOF -ac_clean_files=$ac_clean_files_save - -test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 - - -# configure is writing to config.log, and then calls config.status. -# config.status does its own redirection, appending to config.log. -# Unfortunately, on DOS this fails, as config.log is still kept open -# by configure, so config.status won't be able to write to it; its -# output is simply discarded. So we exec the FD to /dev/null, -# effectively closing config.log, so it can be properly (re)opened and -# appended to by config.status. When coming back to configure, we -# need to make the FD available again. -if test "$no_create" != yes; then - ac_cs_success=: - ac_config_status_args= - test "$silent" = yes && - ac_config_status_args="$ac_config_status_args --quiet" - exec 5>/dev/null - $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false - exec 5>>config.log - # Use ||, not &&, to avoid exiting from the if with $? = 1, which - # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 -fi -if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} -fi - - commit 368f6f3942a1f8b9483763a6ac24b3b3021e92bf Author: Po Lu Date: Sun Apr 30 21:37:19 2023 +0800 Add helper binary `exec1' * .gitignore: New files. * Makefile.in (mostlyclean_dirs): Add libexec, if its Makefile exists. * autogen.sh (do_git): Autoreconf in exec as well. * configure.ac: Configure libexec on Android. * exec/Makefile.in: * exec/README: * exec/config-mips.m4.in: * exec/config.guess: * exec/config.h.in: * exec/config.sub: * exec/configure: * exec/configure.ac: * exec/deps.mk: * exec/exec.c (MIN, struct exec_open_command) (struct exec_map_command, struct exec_jump_command) (write_open_command, write_load_command, process_interpreter_1) (process_interpreter, process_program_header, insert_args) (exec_0): * exec/exec.h (_EXEC_H_, struct elf_header_32) (struct program_header_32, struct dt_entry_32) (struct elf_header_64, struct program_header_64) (struct dt_entry_64, struct exec_tracee): * exec/exec1.c (main): * exec/install-sh (scriptversion): * exec/loader-aarch64.s (_start): * exec/loader-armeabi.s (_start): * exec/loader-mips64el.s (__start): * exec/loader-mipsel.s (__start): * exec/loader-x86.s (_start): * exec/loader-x86_64.s (_start): * exec/mipsel-user.h (_MIPSEL_USER_H_): * exec/mipsfpu.c (MIPS_ABI_FP_ANY, fpu_reqs, valid_abi_p) (fp_mode_for_abi, cpu_supports_fr0_p, determine_fpu_mode): * exec/mipsfpu.h (_MIPSFPU_H_, FP_FR0): * exec/test.c (print_usage, main): * exec/trace.c (MAX_TRACEES, aarch64_set_regs, read_memory) (user_alloca, user_copy, remove_tracee, handle_clone) (syscall_trap_p, handle_exec, process_system_call, tracing_execve) (after_fork, find_tracee, exec_waitpid, exec_init): New files. * java/Makefile.in (CROSS_EXEC_BINS): Add exec1 and loader. ($(CROSS_EXEC_BINS) &): New target. diff --git a/.gitignore b/.gitignore index 8552d118552..2c5559412bc 100644 --- a/.gitignore +++ b/.gitignore @@ -371,3 +371,14 @@ lib-src/seccomp-filter-exec.pfc # GDB history .gdb_history _gdb_history + +# Files ignored in exec/. +exec/config.status +exec/loader +exec/test +exec/exec1 +exec/deps/* +exec/autom4te.cache +exec/config.h +exec/config-mips.m4 +exec/*.s.s diff --git a/Makefile.in b/Makefile.in index af299ddb0c8..7c18a9fa453 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1000,6 +1000,12 @@ $(1)_$(2): mostlyclean_dirs = src oldXMenu lwlib lib lib-src nt doc/emacs doc/misc \ doc/lispref doc/lispintro test +### Add the libexec directory to mostlyclean_dirs if its Makefile has +### been created. +ifneq ($(wildcard exec/Makefile),) +mostlyclean_dirs := $(mostlyclean_dirs) exec +endif + $(foreach dir,$(mostlyclean_dirs),$(eval $(call submake_template,$(dir),mostlyclean))) mostlyclean: $(mostlyclean_dirs:=_mostlyclean) diff --git a/autogen.sh b/autogen.sh index 6127e7b24f4..3cb77afedb3 100755 --- a/autogen.sh +++ b/autogen.sh @@ -256,6 +256,12 @@ do_git= ## Let autoreconf figure out what, if anything, needs doing. ## Use autoreconf's -f option in case autoreconf itself has changed. autoreconf -fi -I m4 || exit + + echo "Running 'autoreconf -fi' in exec ..." + + # Now, run autoreconf inside the exec directory to generate its + # configure script. + autoreconf -fi exec || exit fi diff --git a/configure.ac b/configure.ac index 3c8aef6daea..ef16841e066 100644 --- a/configure.ac +++ b/configure.ac @@ -174,7 +174,33 @@ with_ndk_cxx_shared="$android_ndk_cxx_shared" with_ndk_cxx="$android_ndk_cxx" ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build], - [$ANDROID_CFLAGS])]) + [$ANDROID_CFLAGS]) + + # At the same time, configure libexec with the build directory + # set to `exec'. + AS_MKDIR_P([exec]) + AC_MSG_NOTICE([configuring in `exec']) + + # Enter exec and configure it, using the C compiler as both the + # assembler and the linker. Determine the absolute name of the + # source directory. + # N.B. that the linker is actually cc, so pass -nostdlib, lest + # the crt be linked in. Likewise for as. + + AS_CASE([$ac_srcdir], [.], [emacs_srcdir=`pwd`], + [[[\\/]* | ?:[\\/]*]], [emacs_srcdir=$ac_srcdir], + [*], [emacs_srcdir=`pwd`/$ac_srcdir]) + + OLDCWD=`pwd` + cd exec + $CONFIG_SHELL $emacs_srcdir/exec/configure \ + --host=$host CC=$CC LD=$CC AS=$CC \ + AR=$AR ASFLAGS=-c + cd $OLDCWD + + AS_IF([test "$?" != "0"], + [AC_MSG_ERROR([failed to configure in `exec'])]) +]) case $host in *-mingw*) diff --git a/exec/Makefile.in b/exec/Makefile.in new file mode 100644 index 00000000000..5bd61b2e831 --- /dev/null +++ b/exec/Makefile.in @@ -0,0 +1,139 @@ +### @configure_input@ + +# Copyright (C) 2023 Free Software Foundation, Inc. + +# This file is part of GNU Emacs. + +# GNU Emacs 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 3 of the License, or +# (at your option) any later version. + +# GNU Emacs 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 GNU Emacs. If not, see . + +# Configure build directory information. + + srcdir = @srcdir@ + VPATH = @srcdir@ + builddir = @builddir@ + +# Set up compilation tools. + + CC = @CC@ + AS = @AS@ + LD = @LD@ + M4 = @M4@ + CPP = @CPP@ + ASFLAGS = @ASFLAGS@ + ARFLAGS = @ARFLAGS@ + CFLAGS = @CFLAGS@ + CPPFLAGS = @CPPFLAGS@ + LDFLAGS = @LDFLAGS@ +LOADERFLAGS = @LOADERFLAGS@ +FIND_DELETE = @FIND_DELETE@ + +# Set up object files. + + LOADER = @exec_loader@ + OBJS = @OBJS@ + LOADOBJS = $(patsubst %.s,%.o,$(LOADER)) + +# Set up automatic dependency tracking. + +AUTO_DEPEND = @AUTO_DEPEND@ +DEPDIR = deps +ifeq ($(AUTO_DEPEND),yes) +DEPFLAGS = -MMD -MF $(DEPDIR)/$*.d -MP +-include $(OBJS:%.o=$(DEPDIR)/%.d) +-include $(DEPDIR)/test.d +-include $(DEPDIR)/exec1.d +else +DEPFLAGS = +include $(srcdir)/deps.mk +endif + +# Set up the appropriate targets. + +all: libexec.a loader + +# Set up automatic Makefile regeneration. + +$(srcdir)/configure: $(srcdir)/configure.ac + cd $(srcdir) && autoreconf + +config.status: $(srcdir)/configure + if [ -x config.status ]; then \ + ./config.status --recheck; \ + else \ + $(srcdir)/configure; \ + fi + +Makefile: config.status Makefile.in + MAKE="$(MAKE)" ./config.status + +# Set up rules to build targets. + +.SUFFIXES: .c .s +.c.o: + $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -I$(srcdir) $< -o $@ +.s.o: + $(M4) $< > $<.s + $(AS) $(ASFLAGS) $<.s -o $@ + +# Set up dependencies for config-mips.m4. + +config-mips.m4: config-mips.m4.in + cd $(srcdir) && ./config.status $@ +$(LOADOBJS): config-mips.m4 + +# Set up rules to build libexec.a. + +libexec.a: $(OBJS) + $(AR) cru $(ARFLAGS) $@ $^ + +# And loader. + +loader: $(LOADOBJS) + $(LD) -o $@ $(LOADERFLAGS) $(LOADOBJS) + +# And test. + +test: test.o libexec.a + $(CC) $(LDFLAGS) $< libexec.a -o $@ + +# And exec1. + +exec1: exec1.o libexec.a + $(CC) $(LDFLAGS) $< libexec.a -o $@ + +# Set up targets for cleaning. + +.PHONY: clean distclean maintainer-clean +clean: + rm -f *.o *.a loader test *.s.s +ifeq ($(AUTO_DEPEND),yes) + rm -rf deps/*.d +endif + +distclean: clean + rm -f Makefile config.status config.h config-mips.m4 + +maintainer-clean: distclean + +### This doesn't actually appear in the coding standards, but Karl +### says GCC supports it, and that's where the configuration part of +### the coding standards seem to come from. It's like distclean, but +### it deletes backup and autosave files too. + +extraclean: maintainer-clean + -rm -f config-tmp-* $(srcdir)/aclocal.m4 $(srcdir)/configure \ + $(srcdir)/src/config.in + -[ "$(srcdir)" = "." ] || \ + find $(srcdir) '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE) + -find . '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE) diff --git a/exec/README b/exec/README new file mode 100644 index 00000000000..f7eb21cfc84 --- /dev/null +++ b/exec/README @@ -0,0 +1,3 @@ +This directory holds the source code to a library used to replace the +`execve' and `execveat' system calls, used by the Android port of +Emacs to start executables without intervention from the system. diff --git a/exec/config-mips.m4.in b/exec/config-mips.m4.in new file mode 100644 index 00000000000..886d19b8e8f --- /dev/null +++ b/exec/config-mips.m4.in @@ -0,0 +1,36 @@ +dnl Assembler templates for MIPS computers. +dnl +dnl Copyright (C) 2023 Free Software Foundation, Inc. +dnl +dnl This file is part of GNU Emacs. +dnl +dnl GNU Emacs is free software: you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation, either version 3 of the License, or +dnl (at your option) any later version. +dnl +dnl GNU Emacs is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with GNU Emacs. If not, see . + +define(`SYSCALL_open', `ifelse(`@MIPS_N32@',`yes',`6002',`4005')') +define(`SYSCALL_close', `ifelse(`@MIPS_N32@',`yes',`6003',`4006')') +define(`SYSCALL_mmap', `ifelse(`@MIPS_N32@',`yes',`6009',`4090')') +define(`SYSCALL_nanosleep', `ifelse(`@MIPS_N32@',`yes',`6034',`4166')') +define(`SYSCALL_exit', `ifelse(`@MIPS_N32@',`yes',`6058',`4001')') +define(`SYSCALL_prctl', `ifelse(`@MIPS_N32@',`yes',`6153',`4192')') + +define(`SYSCALL', `ifelse(`@MIPS_N32@',`yes',` move $a4, $1 + move $a5, $2 + move $a6, $3 + move $a7, $4',` addi $sp, -32 + sw $1, 16($sp) + sw $2, 20($sp) + sw $3, 24($sp) + sw $4, 28($sp)')') + +define(`RESTORE', `ifelse(`@MIPS_N32@',`yes',` nop',` addi $sp, 32')') diff --git a/exec/config.guess b/exec/config.guess new file mode 100755 index 00000000000..c7f17e8fb97 --- /dev/null +++ b/exec/config.guess @@ -0,0 +1,1768 @@ +#!/usr/bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2022 Free Software Foundation, Inc. + +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2022-05-25' + +# This file 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 3 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, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess +# +# Please send patches to . + + +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2022 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +# Just in case it came from the environment. +GUESS= + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039,SC3028 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD=$driver + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if test -f /.attbin/uname ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case $UNAME_SYSTEM in +Linux|GNU|GNU/*) + LIBC=unknown + + set_cc_for_build + cat <<-EOF > "$dummy.c" + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #elif defined(__GLIBC__) + LIBC=gnu + #else + #include + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #endif + #endif + EOF + cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + eval "$cc_set_libc" + + # Second heuristic to detect musl libc. + if [ "$LIBC" = unknown ] && + command -v ldd >/dev/null && + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + if [ "$LIBC" = unknown ]; then + LIBC=gnu + fi + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + echo unknown)` + case $UNAME_MACHINE_ARCH in + aarch64eb) machine=aarch64_be-unknown ;; + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; + *) machine=$UNAME_MACHINE_ARCH-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently (or will in the future) and ABI. + case $UNAME_MACHINE_ARCH in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case $UNAME_MACHINE_ARCH in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case $UNAME_VERSION in + Debian*) + release='-gnu' + ;; + *) + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + GUESS=$machine-${os}${release}${abi-} + ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE + ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE + ;; + *:SecBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE + ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE + ;; + *:MidnightBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE + ;; + *:ekkoBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE + ;; + *:SolidBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE + ;; + *:OS108:*:*) + GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE + ;; + macppc:MirBSD:*:*) + GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE + ;; + *:MirBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE + ;; + *:Sortix:*:*) + GUESS=$UNAME_MACHINE-unknown-sortix + ;; + *:Twizzler:*:*) + GUESS=$UNAME_MACHINE-unknown-twizzler + ;; + *:Redox:*:*) + GUESS=$UNAME_MACHINE-unknown-redox + ;; + mips:OSF1:*.*) + GUESS=mips-dec-osf1 + ;; + alpha:OSF1:*:*) + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + trap '' 0 + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case $ALPHA_CPU_TYPE in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + GUESS=$UNAME_MACHINE-dec-osf$OSF_REL + ;; + Amiga*:UNIX_System_V:4.0:*) + GUESS=m68k-unknown-sysv4 + ;; + *:[Aa]miga[Oo][Ss]:*:*) + GUESS=$UNAME_MACHINE-unknown-amigaos + ;; + *:[Mm]orph[Oo][Ss]:*:*) + GUESS=$UNAME_MACHINE-unknown-morphos + ;; + *:OS/390:*:*) + GUESS=i370-ibm-openedition + ;; + *:z/VM:*:*) + GUESS=s390-ibm-zvmoe + ;; + *:OS400:*:*) + GUESS=powerpc-ibm-os400 + ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + GUESS=arm-acorn-riscix$UNAME_RELEASE + ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + GUESS=arm-unknown-riscos + ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + GUESS=hppa1.1-hitachi-hiuxmpp + ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + case `(/bin/universe) 2>/dev/null` in + att) GUESS=pyramid-pyramid-sysv3 ;; + *) GUESS=pyramid-pyramid-bsd ;; + esac + ;; + NILE*:*:*:dcosx) + GUESS=pyramid-pyramid-svr4 + ;; + DRS?6000:unix:4.0:6*) + GUESS=sparc-icl-nx6 + ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) GUESS=sparc-icl-nx7 ;; + esac + ;; + s390x:SunOS:*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL + ;; + sun4H:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-hal-solaris2$SUN_REL + ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris2$SUN_REL + ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + GUESS=i386-pc-auroraux$UNAME_RELEASE + ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + set_cc_for_build + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$SUN_ARCH-pc-solaris2$SUN_REL + ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris3$SUN_REL + ;; + sun4*:SunOS:*:*) + case `/usr/bin/arch -k` in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` + GUESS=sparc-sun-sunos$SUN_REL + ;; + sun3*:SunOS:*:*) + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case `/bin/arch` in + sun3) + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; + sun4) + GUESS=sparc-sun-sunos$UNAME_RELEASE + ;; + esac + ;; + aushp:SunOS:*:*) + GUESS=sparc-auspex-sunos$UNAME_RELEASE + ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + GUESS=m68k-milan-mint$UNAME_RELEASE + ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + GUESS=m68k-hades-mint$UNAME_RELEASE + ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + GUESS=m68k-unknown-mint$UNAME_RELEASE + ;; + m68k:machten:*:*) + GUESS=m68k-apple-machten$UNAME_RELEASE + ;; + powerpc:machten:*:*) + GUESS=powerpc-apple-machten$UNAME_RELEASE + ;; + RISC*:Mach:*:*) + GUESS=mips-dec-mach_bsd4.3 + ;; + RISC*:ULTRIX:*:*) + GUESS=mips-dec-ultrix$UNAME_RELEASE + ;; + VAX*:ULTRIX*:*:*) + GUESS=vax-dec-ultrix$UNAME_RELEASE + ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + GUESS=clipper-intergraph-clix$UNAME_RELEASE + ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && + { echo "$SYSTEM_NAME"; exit; } + GUESS=mips-mips-riscos$UNAME_RELEASE + ;; + Motorola:PowerMAX_OS:*:*) + GUESS=powerpc-motorola-powermax + ;; + Motorola:*:4.3:PL8-*) + GUESS=powerpc-harris-powermax + ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + GUESS=powerpc-harris-powermax + ;; + Night_Hawk:Power_UNIX:*:*) + GUESS=powerpc-harris-powerunix + ;; + m88k:CX/UX:7*:*) + GUESS=m88k-harris-cxux7 + ;; + m88k:*:4*:R4*) + GUESS=m88k-motorola-sysv4 + ;; + m88k:*:3*:R3*) + GUESS=m88k-motorola-sysv3 + ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 + then + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x + then + GUESS=m88k-dg-dgux$UNAME_RELEASE + else + GUESS=m88k-dg-dguxbcs$UNAME_RELEASE + fi + else + GUESS=i586-dg-dgux$UNAME_RELEASE + fi + ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + GUESS=m88k-dolphin-sysv3 + ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + GUESS=m88k-motorola-sysv3 + ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + GUESS=m88k-tektronix-sysv3 + ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + GUESS=m68k-tektronix-bsd + ;; + *:IRIX*:*:*) + IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` + GUESS=mips-sgi-irix$IRIX_REL + ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id + ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + GUESS=i386-ibm-aix + ;; + ia64:AIX:*:*) + if test -x /usr/bin/oslevel ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE + fi + GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV + ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` + then + GUESS=$SYSTEM_NAME + else + GUESS=rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + GUESS=rs6000-ibm-aix3.2.4 + else + GUESS=rs6000-ibm-aix3.2 + fi + ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if test -x /usr/bin/lslpp ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE + fi + GUESS=$IBM_ARCH-ibm-aix$IBM_REV + ;; + *:AIX:*:*) + GUESS=rs6000-ibm-aix + ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + GUESS=romp-ibm-bsd4.4 + ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to + ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + GUESS=rs6000-bull-bosx + ;; + DPX/2?00:B.O.S.:*:*) + GUESS=m68k-bull-sysv3 + ;; + 9000/[34]??:4.3bsd:1.*:*) + GUESS=m68k-hp-bsd + ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + GUESS=m68k-hp-bsd4.4 + ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + case $UNAME_MACHINE in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if test -x /usr/bin/getconf; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case $sc_cpu_version in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case $sc_kernel_bits in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if test "$HP_ARCH" = ""; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if test "$HP_ARCH" = hppa2.0w + then + set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + GUESS=$HP_ARCH-hp-hpux$HPUX_REV + ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + GUESS=ia64-hp-hpux$HPUX_REV + ;; + 3050*:HI-UX:*:*) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + GUESS=unknown-hitachi-hiuxwe2 + ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + GUESS=hppa1.1-hp-bsd + ;; + 9000/8??:4.3bsd:*:*) + GUESS=hppa1.0-hp-bsd + ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + GUESS=hppa1.0-hp-mpeix + ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + GUESS=hppa1.1-hp-osf + ;; + hp8??:OSF1:*:*) + GUESS=hppa1.0-hp-osf + ;; + i*86:OSF1:*:*) + if test -x /usr/sbin/sysversion ; then + GUESS=$UNAME_MACHINE-unknown-osf1mk + else + GUESS=$UNAME_MACHINE-unknown-osf1 + fi + ;; + parisc*:Lites*:*:*) + GUESS=hppa1.1-hp-lites + ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + GUESS=c1-convex-bsd + ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + GUESS=c34-convex-bsd + ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + GUESS=c38-convex-bsd + ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + GUESS=c4-convex-bsd + ;; + CRAY*Y-MP:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=ymp-cray-unicos$CRAY_REL + ;; + CRAY*[A-Z]90:*:*:*) + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=t90-cray-unicos$CRAY_REL + ;; + CRAY*T3E:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=alphaev5-cray-unicosmk$CRAY_REL + ;; + CRAY*SV1:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=sv1-cray-unicos$CRAY_REL + ;; + *:UNICOS/mp:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=craynv-cray-unicosmp$CRAY_REL + ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE + ;; + sparc*:BSD/OS:*:*) + GUESS=sparc-unknown-bsdi$UNAME_RELEASE + ;; + *:BSD/OS:*:*) + GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE + ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi + else + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf + fi + ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case $UNAME_PROCESSOR in + amd64) + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; + esac + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL + ;; + i*:CYGWIN*:*) + GUESS=$UNAME_MACHINE-pc-cygwin + ;; + *:MINGW64*:*) + GUESS=$UNAME_MACHINE-pc-mingw64 + ;; + *:MINGW*:*) + GUESS=$UNAME_MACHINE-pc-mingw32 + ;; + *:MSYS*:*) + GUESS=$UNAME_MACHINE-pc-msys + ;; + i*:PW*:*) + GUESS=$UNAME_MACHINE-pc-pw32 + ;; + *:SerenityOS:*:*) + GUESS=$UNAME_MACHINE-pc-serenity + ;; + *:Interix*:*) + case $UNAME_MACHINE in + x86) + GUESS=i586-pc-interix$UNAME_RELEASE + ;; + authenticamd | genuineintel | EM64T) + GUESS=x86_64-unknown-interix$UNAME_RELEASE + ;; + IA64) + GUESS=ia64-unknown-interix$UNAME_RELEASE + ;; + esac ;; + i*:UWIN*:*) + GUESS=$UNAME_MACHINE-pc-uwin + ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + GUESS=x86_64-pc-cygwin + ;; + prep*:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=powerpcle-unknown-solaris2$SUN_REL + ;; + *:GNU:*:*) + # the GNU system + GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` + GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL + ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC + ;; + *:Minix:*:*) + GUESS=$UNAME_MACHINE-unknown-minix + ;; + aarch64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arm*:Linux:*:*) + set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi + else + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf + fi + fi + ;; + avr32*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + cris:Linux:*:*) + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; + crisv32:Linux:*:*) + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; + e2k:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + frv:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + hexagon:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + i*86:Linux:*:*) + GUESS=$UNAME_MACHINE-pc-linux-$LIBC + ;; + ia64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + k1om:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + m32r*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + m68*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + mips:Linux:*:* | mips64:Linux:*:*) + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" + #undef CPU + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + MIPS_ENDIAN=el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + MIPS_ENDIAN= + #else + MIPS_ENDIAN= + #endif + #endif +EOF + cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` + eval "$cc_set_vars" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + ;; + mips64el:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + openrisc*:Linux:*:*) + GUESS=or1k-unknown-linux-$LIBC + ;; + or32:Linux:*:* | or1k*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + padre:Linux:*:*) + GUESS=sparc-unknown-linux-$LIBC + ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + GUESS=hppa64-unknown-linux-$LIBC + ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; + PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; + *) GUESS=hppa-unknown-linux-$LIBC ;; + esac + ;; + ppc64:Linux:*:*) + GUESS=powerpc64-unknown-linux-$LIBC + ;; + ppc:Linux:*:*) + GUESS=powerpc-unknown-linux-$LIBC + ;; + ppc64le:Linux:*:*) + GUESS=powerpc64le-unknown-linux-$LIBC + ;; + ppcle:Linux:*:*) + GUESS=powerpcle-unknown-linux-$LIBC + ;; + riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + s390:Linux:*:* | s390x:Linux:*:*) + GUESS=$UNAME_MACHINE-ibm-linux-$LIBC + ;; + sh64*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + sh*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + tile*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + vax:Linux:*:*) + GUESS=$UNAME_MACHINE-dec-linux-$LIBC + ;; + x86_64:Linux:*:*) + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __i386__ + ABI=x86 + #else + #ifdef __ILP32__ + ABI=x32 + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + x86) CPU=i686 ;; + x32) LIBCABI=${LIBC}x32 ;; + esac + fi + GUESS=$CPU-pc-linux-$LIBCABI + ;; + xtensa*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + GUESS=i386-sequent-sysv4 + ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION + ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + GUESS=$UNAME_MACHINE-pc-os2-emx + ;; + i*86:XTS-300:*:STOP) + GUESS=$UNAME_MACHINE-unknown-stop + ;; + i*86:atheos:*:*) + GUESS=$UNAME_MACHINE-unknown-atheos + ;; + i*86:syllable:*:*) + GUESS=$UNAME_MACHINE-pc-syllable + ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + GUESS=i386-unknown-lynxos$UNAME_RELEASE + ;; + i*86:*DOS:*:*) + GUESS=$UNAME_MACHINE-pc-msdosdjgpp + ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL + else + GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL + fi + ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL + else + GUESS=$UNAME_MACHINE-pc-sysv32 + fi + ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + GUESS=i586-pc-msdosdjgpp + ;; + Intel:Mach:3*:*) + GUESS=i386-pc-mach3 + ;; + paragon:*:*:*) + GUESS=i860-intel-osf1 + ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 + fi + ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + GUESS=m68010-convergent-sysv + ;; + mc68k:UNIX:SYSTEM5:3.51m) + GUESS=m68k-convergent-sysv + ;; + M680?0:D-NIX:5.3:*) + GUESS=m68k-diab-dnix + ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + GUESS=m68k-unknown-lynxos$UNAME_RELEASE + ;; + mc68030:UNIX_System_V:4.*:*) + GUESS=m68k-atari-sysv4 + ;; + TSUNAMI:LynxOS:2.*:*) + GUESS=sparc-unknown-lynxos$UNAME_RELEASE + ;; + rs6000:LynxOS:2.*:*) + GUESS=rs6000-unknown-lynxos$UNAME_RELEASE + ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + GUESS=powerpc-unknown-lynxos$UNAME_RELEASE + ;; + SM[BE]S:UNIX_SV:*:*) + GUESS=mips-dde-sysv$UNAME_RELEASE + ;; + RM*:ReliantUNIX-*:*:*) + GUESS=mips-sni-sysv4 + ;; + RM*:SINIX-*:*:*) + GUESS=mips-sni-sysv4 + ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + GUESS=$UNAME_MACHINE-sni-sysv4 + else + GUESS=ns32k-sni-sysv + fi + ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + GUESS=i586-unisys-sysv4 + ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + GUESS=hppa1.1-stratus-sysv4 + ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + GUESS=i860-stratus-sysv4 + ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + GUESS=$UNAME_MACHINE-stratus-vos + ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + GUESS=hppa1.1-stratus-vos + ;; + mc68*:A/UX:*:*) + GUESS=m68k-apple-aux$UNAME_RELEASE + ;; + news*:NEWS-OS:6*:*) + GUESS=mips-sony-newsos6 + ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if test -d /usr/nec; then + GUESS=mips-nec-sysv$UNAME_RELEASE + else + GUESS=mips-unknown-sysv$UNAME_RELEASE + fi + ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + GUESS=powerpc-be-beos + ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + GUESS=powerpc-apple-beos + ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + GUESS=i586-pc-beos + ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + GUESS=i586-pc-haiku + ;; + ppc:Haiku:*:*) # Haiku running on Apple PowerPC + GUESS=powerpc-apple-haiku + ;; + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) + GUESS=$UNAME_MACHINE-unknown-haiku + ;; + SX-4:SUPER-UX:*:*) + GUESS=sx4-nec-superux$UNAME_RELEASE + ;; + SX-5:SUPER-UX:*:*) + GUESS=sx5-nec-superux$UNAME_RELEASE + ;; + SX-6:SUPER-UX:*:*) + GUESS=sx6-nec-superux$UNAME_RELEASE + ;; + SX-7:SUPER-UX:*:*) + GUESS=sx7-nec-superux$UNAME_RELEASE + ;; + SX-8:SUPER-UX:*:*) + GUESS=sx8-nec-superux$UNAME_RELEASE + ;; + SX-8R:SUPER-UX:*:*) + GUESS=sx8r-nec-superux$UNAME_RELEASE + ;; + SX-ACE:SUPER-UX:*:*) + GUESS=sxace-nec-superux$UNAME_RELEASE + ;; + Power*:Rhapsody:*:*) + GUESS=powerpc-apple-rhapsody$UNAME_RELEASE + ;; + *:Rhapsody:*:*) + GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE + ;; + arm64:Darwin:*:*) + GUESS=aarch64-apple-darwin$UNAME_RELEASE + ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build + fi + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE + fi + GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE + ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE + ;; + *:QNX:*:4*) + GUESS=i386-pc-qnx + ;; + NEO-*:NONSTOP_KERNEL:*:*) + GUESS=neo-tandem-nsk$UNAME_RELEASE + ;; + NSE-*:NONSTOP_KERNEL:*:*) + GUESS=nse-tandem-nsk$UNAME_RELEASE + ;; + NSR-*:NONSTOP_KERNEL:*:*) + GUESS=nsr-tandem-nsk$UNAME_RELEASE + ;; + NSV-*:NONSTOP_KERNEL:*:*) + GUESS=nsv-tandem-nsk$UNAME_RELEASE + ;; + NSX-*:NONSTOP_KERNEL:*:*) + GUESS=nsx-tandem-nsk$UNAME_RELEASE + ;; + *:NonStop-UX:*:*) + GUESS=mips-compaq-nonstopux + ;; + BS2000:POSIX*:*:*) + GUESS=bs2000-siemens-sysv + ;; + DS/*:UNIX_System_V:*:*) + GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE + ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "${cputype-}" = 386; then + UNAME_MACHINE=i386 + elif test "x${cputype-}" != x; then + UNAME_MACHINE=$cputype + fi + GUESS=$UNAME_MACHINE-unknown-plan9 + ;; + *:TOPS-10:*:*) + GUESS=pdp10-unknown-tops10 + ;; + *:TENEX:*:*) + GUESS=pdp10-unknown-tenex + ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + GUESS=pdp10-dec-tops20 + ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + GUESS=pdp10-xkl-tops20 + ;; + *:TOPS-20:*:*) + GUESS=pdp10-unknown-tops20 + ;; + *:ITS:*:*) + GUESS=pdp10-unknown-its + ;; + SEI:*:*:SEIUX) + GUESS=mips-sei-seiux$UNAME_RELEASE + ;; + *:DragonFly:*:*) + DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL + ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case $UNAME_MACHINE in + A*) GUESS=alpha-dec-vms ;; + I*) GUESS=ia64-dec-vms ;; + V*) GUESS=vax-dec-vms ;; + esac ;; + *:XENIX:*:SysV) + GUESS=i386-pc-xenix + ;; + i*86:skyos:*:*) + SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` + GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL + ;; + i*86:rdos:*:*) + GUESS=$UNAME_MACHINE-pc-rdos + ;; + i*86:Fiwix:*:*) + GUESS=$UNAME_MACHINE-pc-fiwix + ;; + *:AROS:*:*) + GUESS=$UNAME_MACHINE-unknown-aros + ;; + x86_64:VMkernel:*:*) + GUESS=$UNAME_MACHINE-unknown-esx + ;; + amd64:Isilon\ OneFS:*:*) + GUESS=x86_64-unknown-onefs + ;; + *:Unleashed:*:*) + GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE + ;; +esac + +# Do we have a guess based on uname results? +if test "x$GUESS" != x; then + echo "$GUESS" + exit +fi + +# No uname command or uname output not recognized. +set_cc_for_build +cat > "$dummy.c" < +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); +#endif + +#if defined (vax) +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } + +echo "$0: unable to guess system type" >&2 + +case $UNAME_MACHINE:$UNAME_SYSTEM in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 <&2 </dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" +EOF +fi + +exit 1 + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/exec/config.h.in b/exec/config.h.in new file mode 100644 index 00000000000..d602d89a38e --- /dev/null +++ b/exec/config.h.in @@ -0,0 +1,202 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at your +option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + +/* Define to number of reserved bytes past the stack frame. */ +#undef ABI_RED_ZONE + +/* Define if building universal (internal helper macro) */ +#undef AC_APPLE_UNIVERSAL_BUILD + +/* Define to number of the `clone3' system call. */ +#undef CLONE3_SYSCALL + +/* Define to number of the `clone' system call. */ +#undef CLONE_SYSCALL + +/* Virtual address for loading PIC executables */ +#undef EXECUTABLE_BASE + +/* Define to 1 if the system utilizes 64-bit ELF. */ +#undef EXEC_64 + +/* Define to number of the `exec' system call. */ +#undef EXEC_SYSCALL + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if stdbool.h conforms to C99. */ +#undef HAVE_STDBOOL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if the system has the type `uintptr_t'. */ +#undef HAVE_UINTPTR_T + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Virtual address for loading PIC interpreters */ +#undef INTERPRETER_BASE + +/* Define to 1 if MIPS NABI calling convention is being used. */ +#undef MIPS_NABI + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if the stack grows downwards. */ +#undef STACK_GROWS_DOWNWARDS + +/* Define to register holding the stack pointer. */ +#undef STACK_POINTER + +/* Define to 1 if all of the C90 standard headers exist (not just the ones + required in a freestanding environment). This macro is provided for + backward compatibility; new code need not use it. */ +#undef STDC_HEADERS + +/* Define to register holding arg1 to system calls. */ +#undef SYSCALL_ARG1_REG + +/* Define to register holding arg0 to system calls. */ +#undef SYSCALL_ARG_REG + +/* Define to header holding system call numbers. */ +#undef SYSCALL_HEADER + +/* Define to register holding the system call number. */ +#undef SYSCALL_NUM_REG + +/* Define to register holding value of system calls. */ +#undef SYSCALL_RET_REG + +/* Define to header holding USER_REGS_STRUCT. */ +#undef USER_HEADER + +/* Define to structure holding user registers. */ +#undef USER_REGS_STRUCT + +/* Define to word type used by tracees. */ +#undef USER_WORD + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +# undef WORDS_BIGENDIAN +# endif +#endif + +/* Define for Solaris 2.5.1 so the uint32_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT32_T + +/* Define for Solaris 2.5.1 so the uint64_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT64_T + +/* Define for Solaris 2.5.1 so the uint8_t typedef from , + , or is not used. If the typedef were allowed, the + #define below would cause a syntax error. */ +#undef _UINT8_T + +/* Define as a signed integer type capable of holding a process identifier. */ +#undef pid_t + +/* Define to the type of an unsigned integer type of width exactly 16 bits if + such a type exists and the standard includes do not define it. */ +#undef uint16_t + +/* Define to the type of an unsigned integer type of width exactly 32 bits if + such a type exists and the standard includes do not define it. */ +#undef uint32_t + +/* Define to the type of an unsigned integer type of width exactly 64 bits if + such a type exists and the standard includes do not define it. */ +#undef uint64_t + +/* Define to the type of an unsigned integer type of width exactly 8 bits if + such a type exists and the standard includes do not define it. */ +#undef uint8_t + +/* Define to the type of an unsigned integer type wide enough to hold a + pointer, if such a type exists, and if the system does not define it. */ +#undef uintptr_t + + +#ifdef HAVE_STDBOOL_H +# include +#else +# ifndef HAVE__BOOL +# ifdef __cplusplus +typedef bool _Bool; +# else +# define _Bool signed char +# endif +# endif +# define bool _Bool +# define false 0 +# define true 1 +# define __bool_true_false_are_defined 1 +#endif + diff --git a/exec/config.sub b/exec/config.sub new file mode 100755 index 00000000000..b41da55df45 --- /dev/null +++ b/exec/config.sub @@ -0,0 +1,1890 @@ +#!/usr/bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2022 Free Software Foundation, Inc. + +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2022-01-03' + +# This file 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 3 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, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2022 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo "$1" + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Split fields of configuration type +# shellcheck disable=SC2162 +saved_IFS=$IFS +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 + ;; + *-*-*-*) + basic_machine=$field1-$field2 + basic_os=$field3-$field4 + ;; + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + nto-qnx* | linux-* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova*) + basic_machine=$field1 + basic_os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + basic_os=linux-android + ;; + *) + basic_machine=$field1-$field2 + basic_os=$field3 + ;; + esac + ;; + *-*) + # A lone config we happen to match not fitting any pattern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + basic_os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + basic_os=$field2 + ;; + zephyr*) + basic_machine=$field1-unknown + basic_os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + basic_os= + ;; + *) + basic_machine=$field1 + basic_os=$field2 + ;; + esac + ;; + esac + ;; + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + basic_os=bsd + ;; + a29khif) + basic_machine=a29k-amd + basic_os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + basic_os=scout + ;; + alliant) + basic_machine=fx80-alliant + basic_os= + ;; + altos | altos3068) + basic_machine=m68k-altos + basic_os= + ;; + am29k) + basic_machine=a29k-none + basic_os=bsd + ;; + amdahl) + basic_machine=580-amdahl + basic_os=sysv + ;; + amiga) + basic_machine=m68k-unknown + basic_os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + basic_os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + basic_os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + basic_os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + basic_os=bsd + ;; + aros) + basic_machine=i386-pc + basic_os=aros + ;; + aux) + basic_machine=m68k-apple + basic_os=aux + ;; + balance) + basic_machine=ns32k-sequent + basic_os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + basic_os=linux + ;; + cegcc) + basic_machine=arm-unknown + basic_os=cegcc + ;; + convex-c1) + basic_machine=c1-convex + basic_os=bsd + ;; + convex-c2) + basic_machine=c2-convex + basic_os=bsd + ;; + convex-c32) + basic_machine=c32-convex + basic_os=bsd + ;; + convex-c34) + basic_machine=c34-convex + basic_os=bsd + ;; + convex-c38) + basic_machine=c38-convex + basic_os=bsd + ;; + cray) + basic_machine=j90-cray + basic_os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + basic_os= + ;; + da30) + basic_machine=m68k-da30 + basic_os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + basic_os= + ;; + delta88) + basic_machine=m88k-motorola + basic_os=sysv3 + ;; + dicos) + basic_machine=i686-pc + basic_os=dicos + ;; + djgpp) + basic_machine=i586-pc + basic_os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + basic_os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + basic_os=ose + ;; + gmicro) + basic_machine=tron-gmicro + basic_os=sysv + ;; + go32) + basic_machine=i386-pc + basic_os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + basic_os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + basic_os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + basic_os=hms + ;; + harris) + basic_machine=m88k-harris + basic_os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + basic_os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + basic_os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + basic_os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + basic_os=proelf + ;; + i386mach) + basic_machine=i386-mach + basic_os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + basic_os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + basic_os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + basic_os=sysv + ;; + merlin) + basic_machine=ns32k-utek + basic_os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + basic_os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + basic_os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + basic_os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + basic_os=coff + ;; + morphos) + basic_machine=powerpc-unknown + basic_os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + basic_os=moxiebox + ;; + msdos) + basic_machine=i386-pc + basic_os=msdos + ;; + msys) + basic_machine=i686-pc + basic_os=msys + ;; + mvs) + basic_machine=i370-ibm + basic_os=mvs + ;; + nacl) + basic_machine=le32-unknown + basic_os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + basic_os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + basic_os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + basic_os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + basic_os=newsos + ;; + news1000) + basic_machine=m68030-sony + basic_os=newsos + ;; + necv70) + basic_machine=v70-nec + basic_os=sysv + ;; + nh3000) + basic_machine=m68k-harris + basic_os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + basic_os=cxux + ;; + nindy960) + basic_machine=i960-intel + basic_os=nindy + ;; + mon960) + basic_machine=i960-intel + basic_os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + basic_os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + basic_os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + basic_os=ose + ;; + os68k) + basic_machine=m68k-none + basic_os=os68k + ;; + paragon) + basic_machine=i860-intel + basic_os=osf + ;; + parisc) + basic_machine=hppa-unknown + basic_os=linux + ;; + psp) + basic_machine=mipsallegrexel-sony + basic_os=psp + ;; + pw32) + basic_machine=i586-unknown + basic_os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + basic_os=rdos + ;; + rdos32) + basic_machine=i386-pc + basic_os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + basic_os=coff + ;; + sa29200) + basic_machine=a29k-amd + basic_os=udi + ;; + sei) + basic_machine=mips-sei + basic_os=seiux + ;; + sequent) + basic_machine=i386-sequent + basic_os= + ;; + sps7) + basic_machine=m68k-bull + basic_os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + basic_os= + ;; + stratus) + basic_machine=i860-stratus + basic_os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + basic_os= + ;; + sun2os3) + basic_machine=m68000-sun + basic_os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + basic_os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + basic_os= + ;; + sun3os3) + basic_machine=m68k-sun + basic_os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + basic_os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + basic_os= + ;; + sun4os3) + basic_machine=sparc-sun + basic_os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + basic_os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + basic_os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + basic_os= + ;; + sv1) + basic_machine=sv1-cray + basic_os=unicos + ;; + symmetry) + basic_machine=i386-sequent + basic_os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + basic_os=unicos + ;; + t90) + basic_machine=t90-cray + basic_os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + basic_os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + basic_os=tpf + ;; + udi29k) + basic_machine=a29k-amd + basic_os=udi + ;; + ultra3) + basic_machine=a29k-nyu + basic_os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + basic_os=none + ;; + vaxv) + basic_machine=vax-dec + basic_os=sysv + ;; + vms) + basic_machine=vax-dec + basic_os=vms + ;; + vsta) + basic_machine=i386-pc + basic_os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + basic_os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + basic_os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + basic_os=vxworks + ;; + xbox) + basic_machine=i686-pc + basic_os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + basic_os=unicos + ;; + *) + basic_machine=$1 + basic_os= + ;; + esac + ;; +esac + +# Decode 1-component or ad-hoc basic machines +case $basic_machine in + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond + ;; + op50n) + cpu=hppa1.1 + vendor=oki + ;; + op60c) + cpu=hppa1.1 + vendor=oki + ;; + ibm*) + cpu=i370 + vendor=ibm + ;; + orion105) + cpu=clipper + vendor=highlevel + ;; + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple + ;; + pmac | pmac-mpw) + cpu=powerpc + vendor=apple + ;; + + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + cpu=m68000 + vendor=att + ;; + 3b*) + cpu=we32k + vendor=att + ;; + bluegene*) + cpu=powerpc + vendor=ibm + basic_os=cnk + ;; + decsystem10* | dec10*) + cpu=pdp10 + vendor=dec + basic_os=tops10 + ;; + decsystem20* | dec20*) + cpu=pdp10 + vendor=dec + basic_os=tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + cpu=m68k + vendor=motorola + ;; + dpx2*) + cpu=m68k + vendor=bull + basic_os=sysv3 + ;; + encore | umax | mmax) + cpu=ns32k + vendor=encore + ;; + elxsi) + cpu=elxsi + vendor=elxsi + basic_os=${basic_os:-bsd} + ;; + fx2800) + cpu=i860 + vendor=alliant + ;; + genix) + cpu=ns32k + vendor=ns + ;; + h3050r* | hiux*) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + cpu=m68000 + vendor=hp + ;; + hp9k3[2-9][0-9]) + cpu=m68k + vendor=hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + i*86v32) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv32 + ;; + i*86v4*) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv4 + ;; + i*86v) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv + ;; + i*86sol2) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=solaris2 + ;; + j90 | j90-cray) + cpu=j90 + vendor=cray + basic_os=${basic_os:-unicos} + ;; + iris | iris4d) + cpu=mips + vendor=sgi + case $basic_os in + irix*) + ;; + *) + basic_os=irix4 + ;; + esac + ;; + miniframe) + cpu=m68000 + vendor=convergent + ;; + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + basic_os=mint + ;; + news-3600 | risc-news) + cpu=mips + vendor=sony + basic_os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + case $basic_os in + openstep*) + ;; + nextstep*) + ;; + ns2*) + basic_os=nextstep2 + ;; + *) + basic_os=nextstep3 + ;; + esac + ;; + np1) + cpu=np1 + vendor=gould + ;; + op50n-* | op60c-*) + cpu=hppa1.1 + vendor=oki + basic_os=proelf + ;; + pa-hitachi) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + pbd) + cpu=sparc + vendor=tti + ;; + pbb) + cpu=m68k + vendor=tti + ;; + pc532) + cpu=ns32k + vendor=pc532 + ;; + pn) + cpu=pn + vendor=gould + ;; + power) + cpu=power + vendor=ibm + ;; + ps2) + cpu=i386 + vendor=ibm + ;; + rm[46]00) + cpu=mips + vendor=siemens + ;; + rtpc | rtpc-*) + cpu=romp + vendor=ibm + ;; + sde) + cpu=mipsisa32 + vendor=sde + basic_os=${basic_os:-elf} + ;; + simso-wrs) + cpu=sparclite + vendor=wrs + basic_os=vxworks + ;; + tower | tower-32) + cpu=m68k + vendor=ncr + ;; + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu + ;; + w65) + cpu=w65 + vendor=wdc + ;; + w89k-*) + cpu=hppa1.1 + vendor=winbond + basic_os=proelf + ;; + none) + cpu=none + vendor=none + ;; + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine + ;; + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` + ;; + + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read cpu vendor <&2 + exit 1 + ;; + esac + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $vendor in + digital*) + vendor=dec + ;; + commodore*) + vendor=cbm + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if test x$basic_os != x +then + +# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just +# set os. +case $basic_os in + gnu/linux*) + kernel=linux + os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` + ;; + os2-emx) + kernel=os2 + os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` + ;; + nto-qnx*) + kernel=nto + os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` + ;; + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read kernel os <&2 + exit 1 + ;; +esac + +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os in + linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \ + | linux-musl* | linux-relibc* | linux-uclibc* ) + ;; + uclinux-uclibc* ) + ;; + -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2 + exit 1 + ;; + kfreebsd*-gnu* | kopensolaris*-gnu*) + ;; + vxworks-simlinux | vxworks-simwindows | vxworks-spe) + ;; + nto-qnx*) + ;; + os2-emx) + ;; + *-eabi* | *-gnueabi*) + ;; + -*) + # Blank kernel with real OS is always fine. + ;; + *-*) + echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2 + exit 1 + ;; +esac + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +case $vendor in + unknown) + case $cpu-$os in + *-riscix*) + vendor=acorn + ;; + *-sunos*) + vendor=sun + ;; + *-cnk* | *-aix*) + vendor=ibm + ;; + *-beos*) + vendor=be + ;; + *-hpux*) + vendor=hp + ;; + *-mpeix*) + vendor=hp + ;; + *-hiux*) + vendor=hitachi + ;; + *-unos*) + vendor=crds + ;; + *-dgux*) + vendor=dg + ;; + *-luna*) + vendor=omron + ;; + *-genix*) + vendor=ns + ;; + *-clix*) + vendor=intergraph + ;; + *-mvs* | *-opened*) + vendor=ibm + ;; + *-os400*) + vendor=ibm + ;; + s390-* | s390x-*) + vendor=ibm + ;; + *-ptx*) + vendor=sequent + ;; + *-tpf*) + vendor=ibm + ;; + *-vxsim* | *-vxworks* | *-windiss*) + vendor=wrs + ;; + *-aux*) + vendor=apple + ;; + *-hms*) + vendor=hitachi + ;; + *-mpw* | *-macos*) + vendor=apple + ;; + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) + vendor=atari + ;; + *-vos*) + vendor=stratus + ;; + esac + ;; +esac + +echo "$cpu-$vendor-${kernel:+$kernel-}$os" +exit + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/exec/configure b/exec/configure new file mode 100755 index 00000000000..5074fa2570a --- /dev/null +++ b/exec/configure @@ -0,0 +1,6940 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.71 for libexec 30.0.50. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, +# Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else $as_nop + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + + +# Reset variables that may have inherited troublesome values from +# the environment. + +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. +as_nl=' +' +export as_nl +IFS=" "" $as_nl" + +PS1='$ ' +PS2='> ' +PS4='+ ' + +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done + +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi + +# The user is always right. +if ${PATH_SEPARATOR+false} :; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="as_nop=: +if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else \$as_nop + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ) +then : + +else \$as_nop + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +blah=\$(echo \$(echo blah)) +test x\"\$blah\" = xblah || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" + if (eval "$as_required") 2>/dev/null +then : + as_have_required=yes +else $as_nop + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null +then : + +else $as_nop + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : + CONFIG_SHELL=$as_shell as_have_required=yes + if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null +then : + break 2 +fi +fi + done;; + esac + as_found=false +done +IFS=$as_save_IFS +if $as_found +then : + +else $as_nop + if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi +fi + + + if test "x$CONFIG_SHELL" != x +then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno +then : + printf "%s\n" "$0: This script requires a shell more modern than all" + printf "%s\n" "$0: the shells that I found on your system." + if test ${ZSH_VERSION+y} ; then + printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" + printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." + else + printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and +$0: bug-gnu-emacs@gnu.org about your system, including any +$0: error possibly output before this message. Then install +$0: a modern shell, or manually run the script under such a +$0: shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else $as_nop + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else $as_nop + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + printf "%s\n" "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' + + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='libexec' +PACKAGE_TARNAME='libexec' +PACKAGE_VERSION='30.0.50' +PACKAGE_STRING='libexec 30.0.50' +PACKAGE_BUGREPORT='bug-gnu-emacs@gnu.org' +PACKAGE_URL='https://www.gnu.org/software/emacs/' + +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_STDIO_H +# include +#endif +#ifdef HAVE_STDLIB_H +# include +#endif +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_header_c_list= +ac_subst_vars='LTLIBOBJS +LIBOBJS +OBJS +MIPS_N32 +exec_loader +AUTO_DEPEND +FIND_DELETE +ASFLAGS +ARFLAGS +LOADERFLAGS +AR +LD +AS +M4 +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +runstatedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP +M4 +AS +LD +LOADERFLAGS +ARFLAGS +ASFLAGS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir runstatedir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures libexec 30.0.50 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/libexec] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of libexec 30.0.50:";; + esac + cat <<\_ACEOF + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + M4 `m4' preprocessor command. + AS `as' assembler command. + LD `ld' linker command. + LOADERFLAGS Flags used to link the loader. + ARFLAGS Flags for the archiver. + ASFLAGS Flags for the assembler. + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +libexec home page: . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for configure.gnu first; this name is used for a wrapper for + # Metaconfig's "Configure" on case-insensitive file systems. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +libexec configure 30.0.50 +generated by GNU Autoconf 2.71 + +Copyright (C) 2021 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest.beam + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + } +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + eval "$3=yes" +else $as_nop + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_find_uintX_t LINENO BITS VAR +# ------------------------------------ +# Finds an unsigned integer type with width BITS, setting cache variable VAR +# accordingly. +ac_fn_c_find_uintX_t () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 +printf %s "checking for uint$2_t... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop + eval "$3=no" + # Order is important - never check a type that is potentially smaller + # than half of the expected target width. + for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ + 'unsigned long long int' 'unsigned short int' 'unsigned char'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main (void) +{ +static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + case $ac_type in #( + uint$2_t) : + eval "$3=yes" ;; #( + *) : + eval "$3=\$ac_type" ;; +esac +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + if eval test \"x\$"$3"\" = x"no" +then : + +else $as_nop + break +fi + done +fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_find_uintX_t + +# ac_fn_c_check_type LINENO TYPE VAR INCLUDES +# ------------------------------------------- +# Tests whether TYPE exists after having included INCLUDES, setting cache +# variable VAR accordingly. +ac_fn_c_check_type () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop + eval "$3=no" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main (void) +{ +if (sizeof ($2)) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main (void) +{ +if (sizeof (($2))) + return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + +else $as_nop + eval "$3=yes" +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_type + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + } +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. */ + +#include +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main (void) +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + eval "$3=yes" +else $as_nop + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that +# executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: program exited with status $ac_status" >&5 + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES +# ---------------------------------------------------- +# Tries to find if the field MEMBER exists in type AGGR, after including +# INCLUDES, setting cache variable VAR accordingly. +ac_fn_c_check_member () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 +printf %s "checking for $2.$3... " >&6; } +if eval test \${$4+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main (void) +{ +static $2 ac_aggr; +if (ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + eval "$4=yes" +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$5 +int +main (void) +{ +static $2 ac_aggr; +if (sizeof ac_aggr.$3) +return 0; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + eval "$4=yes" +else $as_nop + eval "$4=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +eval ac_res=\$$4 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_member + +# ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR +# ------------------------------------------------------------------ +# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR +# accordingly. Pass EXTRA-OPTIONS to the compiler, using FLAG-VAR. +ac_fn_check_decl () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + as_decl_name=`echo $2|sed 's/ *(.*//'` + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 +printf %s "checking whether $as_decl_name is declared... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop + as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` + eval ac_save_FLAGS=\$$6 + as_fn_append $6 " $5" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +int +main (void) +{ +#ifndef $as_decl_name +#ifdef __cplusplus + (void) $as_decl_use; +#else + (void) $as_decl_name; +#endif +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + eval "$3=yes" +else $as_nop + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + eval $6=\$ac_save_FLAGS + +fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_check_decl +ac_configure_args_raw= +for ac_arg +do + case $ac_arg in + *\'*) + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append ac_configure_args_raw " '$ac_arg'" +done + +case $ac_configure_args_raw in + *$as_nl*) + ac_safe_unquote= ;; + *) + ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. + ac_unsafe_a="$ac_unsafe_z#~" + ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" + ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; +esac + +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by libexec $as_me 30.0.50, which was +generated by GNU Autoconf 2.71. Invocation command line was + + $ $0$ac_configure_args_raw + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + printf "%s\n" "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Sanitize IFS. + IFS=" "" $as_nl" + # Save into config.log some information that might help in debugging. + { + echo + + printf "%s\n" "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + printf "%s\n" "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + printf "%s\n" "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + printf "%s\n" "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + printf "%s\n" "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + printf "%s\n" "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + printf "%s\n" "$as_me: caught signal $ac_signal" + printf "%s\n" "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +printf "%s\n" "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +if test -n "$CONFIG_SITE"; then + ac_site_files="$CONFIG_SITE" +elif test "x$prefix" != xNONE; then + ac_site_files="$prefix/share/config.site $prefix/etc/config.site" +else + ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" +fi + +for ac_site_file in $ac_site_files +do + case $ac_site_file in #( + */*) : + ;; #( + *) : + ac_site_file=./$ac_site_file ;; +esac + if test -f "$ac_site_file" && test -r "$ac_site_file"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +printf "%s\n" "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +printf "%s\n" "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Test code for whether the C compiler supports C89 (global declarations) +ac_c_conftest_c89_globals=' +/* Does the compiler advertise C89 conformance? + Do not test the value of __STDC__, because some compilers set it to 0 + while being otherwise adequately conformant. */ +#if !defined __STDC__ +# error "Compiler does not advertise C89 conformance" +#endif + +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ +struct buf { int x; }; +struct buf * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not \xHH hex character constants. + These do not provoke an error unfortunately, instead are silently treated + as an "x". The following induces an error, until -std is added to get + proper ANSI mode. Curiously \x00 != x always comes out true, for an + array size at least. It is necessary to write \x00 == 0 to get something + that is true only with -std. */ +int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) '\''x'\'' +int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), + int, int);' + +# Test code for whether the C compiler supports C89 (body of main). +ac_c_conftest_c89_main=' +ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); +' + +# Test code for whether the C compiler supports C99 (global declarations) +ac_c_conftest_c99_globals=' +// Does the compiler advertise C99 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L +# error "Compiler does not advertise C99 conformance" +#endif + +#include +extern int puts (const char *); +extern int printf (const char *, ...); +extern int dprintf (int, const char *, ...); +extern void *malloc (size_t); + +// Check varargs macros. These examples are taken from C99 6.10.3.5. +// dprintf is used instead of fprintf to avoid needing to declare +// FILE and stderr. +#define debug(...) dprintf (2, __VA_ARGS__) +#define showlist(...) puts (#__VA_ARGS__) +#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) +static void +test_varargs_macros (void) +{ + int x = 1234; + int y = 5678; + debug ("Flag"); + debug ("X = %d\n", x); + showlist (The first, second, and third items.); + report (x>y, "x is %d but y is %d", x, y); +} + +// Check long long types. +#define BIG64 18446744073709551615ull +#define BIG32 4294967295ul +#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) +#if !BIG_OK + #error "your preprocessor is broken" +#endif +#if BIG_OK +#else + #error "your preprocessor is broken" +#endif +static long long int bignum = -9223372036854775807LL; +static unsigned long long int ubignum = BIG64; + +struct incomplete_array +{ + int datasize; + double data[]; +}; + +struct named_init { + int number; + const wchar_t *name; + double average; +}; + +typedef const char *ccp; + +static inline int +test_restrict (ccp restrict text) +{ + // See if C++-style comments work. + // Iterate through items via the restricted pointer. + // Also check for declarations in for loops. + for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) + continue; + return 0; +} + +// Check varargs and va_copy. +static bool +test_varargs (const char *format, ...) +{ + va_list args; + va_start (args, format); + va_list args_copy; + va_copy (args_copy, args); + + const char *str = ""; + int number = 0; + float fnumber = 0; + + while (*format) + { + switch (*format++) + { + case '\''s'\'': // string + str = va_arg (args_copy, const char *); + break; + case '\''d'\'': // int + number = va_arg (args_copy, int); + break; + case '\''f'\'': // float + fnumber = va_arg (args_copy, double); + break; + default: + break; + } + } + va_end (args_copy); + va_end (args); + + return *str && number && fnumber; +} +' + +# Test code for whether the C compiler supports C99 (body of main). +ac_c_conftest_c99_main=' + // Check bool. + _Bool success = false; + success |= (argc != 0); + + // Check restrict. + if (test_restrict ("String literal") == 0) + success = true; + char *restrict newvar = "Another string"; + + // Check varargs. + success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); + test_varargs_macros (); + + // Check flexible array members. + struct incomplete_array *ia = + malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); + ia->datasize = 10; + for (int i = 0; i < ia->datasize; ++i) + ia->data[i] = i * 1.234; + + // Check named initializers. + struct named_init ni = { + .number = 34, + .name = L"Test wide string", + .average = 543.34343, + }; + + ni.number = 58; + + int dynamic_array[ni.number]; + dynamic_array[0] = argv[0][0]; + dynamic_array[ni.number - 1] = 543; + + // work around unused variable warnings + ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' + || dynamic_array[ni.number - 1] != 543); +' + +# Test code for whether the C compiler supports C11 (global declarations) +ac_c_conftest_c11_globals=' +// Does the compiler advertise C11 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L +# error "Compiler does not advertise C11 conformance" +#endif + +// Check _Alignas. +char _Alignas (double) aligned_as_double; +char _Alignas (0) no_special_alignment; +extern char aligned_as_int; +char _Alignas (0) _Alignas (int) aligned_as_int; + +// Check _Alignof. +enum +{ + int_alignment = _Alignof (int), + int_array_alignment = _Alignof (int[100]), + char_alignment = _Alignof (char) +}; +_Static_assert (0 < -_Alignof (int), "_Alignof is signed"); + +// Check _Noreturn. +int _Noreturn does_not_return (void) { for (;;) continue; } + +// Check _Static_assert. +struct test_static_assert +{ + int x; + _Static_assert (sizeof (int) <= sizeof (long int), + "_Static_assert does not work in struct"); + long int y; +}; + +// Check UTF-8 literals. +#define u8 syntax error! +char const utf8_literal[] = u8"happens to be ASCII" "another string"; + +// Check duplicate typedefs. +typedef long *long_ptr; +typedef long int *long_ptr; +typedef long_ptr long_ptr; + +// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. +struct anonymous +{ + union { + struct { int i; int j; }; + struct { int k; long int l; } w; + }; + int m; +} v1; +' + +# Test code for whether the C compiler supports C11 (body of main). +ac_c_conftest_c11_main=' + _Static_assert ((offsetof (struct anonymous, i) + == offsetof (struct anonymous, w.k)), + "Anonymous union alignment botch"); + v1.i = 2; + v1.w.k = 5; + ok |= v1.i != 5; +' + +# Test code for whether the C compiler supports C11 (complete). +ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} +${ac_c_conftest_c11_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + ${ac_c_conftest_c11_main} + return ok; +} +" + +# Test code for whether the C compiler supports C99 (complete). +ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + return ok; +} +" + +# Test code for whether the C compiler supports C89 (complete). +ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + return ok; +} +" + +as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H" +as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H" +as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H" +as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H" +as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H" +as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H" +as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H" +as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H" +as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H" + +# Auxiliary files required by this configure script. +ac_aux_files="config.guess config.sub install-sh" + +# Locations in which to look for auxiliary files. +ac_aux_dir_candidates="${srcdir}${PATH_SEPARATOR}${srcdir}/..${PATH_SEPARATOR}${srcdir}/../.." + +# Search for a directory containing all of the required auxiliary files, +# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates. +# If we don't find one directory that contains all the files we need, +# we report the set of missing files from the *first* directory in +# $ac_aux_dir_candidates and give up. +ac_missing_aux_files="" +ac_first_candidate=: +printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in $ac_aux_dir_candidates +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + as_found=: + + printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5 + ac_aux_dir_found=yes + ac_install_sh= + for ac_aux in $ac_aux_files + do + # As a special case, if "install-sh" is required, that requirement + # can be satisfied by any of "install-sh", "install.sh", or "shtool", + # and $ac_install_sh is set appropriately for whichever one is found. + if test x"$ac_aux" = x"install-sh" + then + if test -f "${as_dir}install-sh"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5 + ac_install_sh="${as_dir}install-sh -c" + elif test -f "${as_dir}install.sh"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5 + ac_install_sh="${as_dir}install.sh -c" + elif test -f "${as_dir}shtool"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5 + ac_install_sh="${as_dir}shtool install -c" + else + ac_aux_dir_found=no + if $ac_first_candidate; then + ac_missing_aux_files="${ac_missing_aux_files} install-sh" + else + break + fi + fi + else + if test -f "${as_dir}${ac_aux}"; then + printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5 + else + ac_aux_dir_found=no + if $ac_first_candidate; then + ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}" + else + break + fi + fi + fi + done + if test "$ac_aux_dir_found" = yes; then + ac_aux_dir="$as_dir" + break + fi + ac_first_candidate=false + + as_found=false +done +IFS=$as_save_IFS +if $as_found +then : + +else $as_nop + as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 +fi + + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +if test -f "${ac_aux_dir}config.guess"; then + ac_config_guess="$SHELL ${ac_aux_dir}config.guess" +fi +if test -f "${ac_aux_dir}config.sub"; then + ac_config_sub="$SHELL ${ac_aux_dir}config.sub" +fi +if test -f "$ac_aux_dir/configure"; then + ac_configure="$SHELL ${ac_aux_dir}configure" +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' + and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. +set dummy ${ac_tool_prefix}clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "clang", so it can be a program name with args. +set dummy clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +fi + + +test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion -version; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +printf %s "checking whether the C compiler works... " >&6; } +ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else $as_nop + ac_file='' +fi +if test -z "$ac_file" +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +printf %s "checking for C compiler default output file name... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +printf "%s\n" "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +printf %s "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +printf "%s\n" "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main (void) +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +printf %s "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +printf "%s\n" "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +printf %s "checking for suffix of object files... " >&6; } +if test ${ac_cv_objext+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +printf "%s\n" "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 +printf %s "checking whether the compiler supports GNU C... " >&6; } +if test ${ac_cv_c_compiler_gnu+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_compiler_gnu=yes +else $as_nop + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+y} +ac_save_CFLAGS=$CFLAGS +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +printf %s "checking whether $CC accepts -g... " >&6; } +if test ${ac_cv_prog_cc_g+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes +else $as_nop + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + +else $as_nop + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +printf "%s\n" "$ac_cv_prog_cc_g" >&6; } +if test $ac_test_CFLAGS; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +ac_prog_cc_stdc=no +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 +printf %s "checking for $CC option to enable C11 features... " >&6; } +if test ${ac_cv_prog_cc_c11+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c11=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c11_program +_ACEOF +for ac_arg in '' -std=gnu11 +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c11=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c11" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi + +if test "x$ac_cv_prog_cc_c11" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c11" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } + CC="$CC $ac_cv_prog_cc_c11" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 + ac_prog_cc_stdc=c11 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 +printf %s "checking for $CC option to enable C99 features... " >&6; } +if test ${ac_cv_prog_cc_c99+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c99=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c99_program +_ACEOF +for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c99=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c99" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi + +if test "x$ac_cv_prog_cc_c99" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c99" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } + CC="$CC $ac_cv_prog_cc_c99" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 + ac_prog_cc_stdc=c99 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 +printf %s "checking for $CC option to enable C89 features... " >&6; } +if test ${ac_cv_prog_cc_c89+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c89_program +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi + +if test "x$ac_cv_prog_cc_c89" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c89" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } + CC="$CC $ac_cv_prog_cc_c89" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 + ac_prog_cc_stdc=c89 +fi +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +printf %s "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test ${ac_cv_prog_CPP+y} +then : + printf %s "(cached) " >&6 +else $as_nop + # Double quotes because $CC needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO" +then : + +else $as_nop + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO" +then : + # Broken: success on invalid input. +continue +else $as_nop + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok +then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +printf "%s\n" "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO" +then : + +else $as_nop + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO" +then : + # Broken: success on invalid input. +continue +else $as_nop + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok +then : + +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + # Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +printf %s "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if test ${ac_cv_path_install+y} +then : + printf %s "(cached) " >&6 +else $as_nop + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + # Account for fact that we put trailing slashes in our PATH walk. +case $as_dir in #(( + ./ | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test ${ac_cv_path_install+y}; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +printf "%s\n" "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + + +ac_header= ac_cache= +for ac_item in $ac_header_c_list +do + if test $ac_cache; then + ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default" + if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then + printf "%s\n" "#define $ac_item 1" >> confdefs.h + fi + ac_header= ac_cache= + elif test $ac_header; then + ac_cache=$ac_item + else + ac_header=$ac_item + fi +done + + + + + + + + +if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes +then : + +printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h + +fi +ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" +case $ac_cv_c_uint8_t in #( + no|yes) ;; #( + *) + +printf "%s\n" "#define _UINT8_T 1" >>confdefs.h + + +printf "%s\n" "#define uint8_t $ac_cv_c_uint8_t" >>confdefs.h +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" +case $ac_cv_c_uint16_t in #( + no|yes) ;; #( + *) + + +printf "%s\n" "#define uint16_t $ac_cv_c_uint16_t" >>confdefs.h +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" +case $ac_cv_c_uint32_t in #( + no|yes) ;; #( + *) + +printf "%s\n" "#define _UINT32_T 1" >>confdefs.h + + +printf "%s\n" "#define uint32_t $ac_cv_c_uint32_t" >>confdefs.h +;; + esac + +ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" +case $ac_cv_c_uint64_t in #( + no|yes) ;; #( + *) + +printf "%s\n" "#define _UINT64_T 1" >>confdefs.h + + +printf "%s\n" "#define uint64_t $ac_cv_c_uint64_t" >>confdefs.h +;; + esac + + + ac_fn_c_check_type "$LINENO" "uintptr_t" "ac_cv_type_uintptr_t" "$ac_includes_default" +if test "x$ac_cv_type_uintptr_t" = xyes +then : + +printf "%s\n" "#define HAVE_UINTPTR_T 1" >>confdefs.h + +else $as_nop + for ac_type in 'unsigned int' 'unsigned long int' \ + 'unsigned long long int'; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main (void) +{ +static int test_array [1 - 2 * !(sizeof (void *) <= sizeof ($ac_type))]; +test_array [0] = 0; +return test_array [0]; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + +printf "%s\n" "#define uintptr_t $ac_type" >>confdefs.h + + ac_type= +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + test -z "$ac_type" && break + done +fi + + + + ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default +" +if test "x$ac_cv_type_pid_t" = xyes +then : + +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #if defined _WIN64 && !defined __CYGWIN__ + LLP64 + #endif + +int +main (void) +{ + + ; + return 0; +} + +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_pid_type='int' +else $as_nop + ac_pid_type='__int64' +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + +printf "%s\n" "#define pid_t $ac_pid_type" >>confdefs.h + + +fi + + + +ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default" +if test "x$ac_cv_type__Bool" = xyes +then : + +printf "%s\n" "#define HAVE__BOOL 1" >>confdefs.h + + +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5 +printf %s "checking for stdbool.h that conforms to C99... " >&6; } +if test ${ac_cv_header_stdbool_h+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + + #ifndef __bool_true_false_are_defined + #error "__bool_true_false_are_defined is not defined" + #endif + char a[__bool_true_false_are_defined == 1 ? 1 : -1]; + + /* Regardless of whether this is C++ or "_Bool" is a + valid type name, "true" and "false" should be usable + in #if expressions and integer constant expressions, + and "bool" should be a valid type name. */ + + #if !true + #error "'true' is not true" + #endif + #if true != 1 + #error "'true' is not equal to 1" + #endif + char b[true == 1 ? 1 : -1]; + char c[true]; + + #if false + #error "'false' is not false" + #endif + #if false != 0 + #error "'false' is not equal to 0" + #endif + char d[false == 0 ? 1 : -1]; + + enum { e = false, f = true, g = false * true, h = true * 256 }; + + char i[(bool) 0.5 == true ? 1 : -1]; + char j[(bool) 0.0 == false ? 1 : -1]; + char k[sizeof (bool) > 0 ? 1 : -1]; + + struct sb { bool s: 1; bool t; } s; + char l[sizeof s.t > 0 ? 1 : -1]; + + /* The following fails for + HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */ + bool m[h]; + char n[sizeof m == h * sizeof m[0] ? 1 : -1]; + char o[-1 - (bool) 0 < 0 ? 1 : -1]; + /* Catch a bug in an HP-UX C compiler. See + https://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html + https://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html + */ + bool p = true; + bool *pp = &p; + + /* C 1999 specifies that bool, true, and false are to be + macros, but C++ 2011 and later overrule this. */ + #if __cplusplus < 201103 + #ifndef bool + #error "bool is not defined" + #endif + #ifndef false + #error "false is not defined" + #endif + #ifndef true + #error "true is not defined" + #endif + #endif + + /* If _Bool is available, repeat with it all the tests + above that used bool. */ + #ifdef HAVE__BOOL + struct sB { _Bool s: 1; _Bool t; } t; + + char q[(_Bool) 0.5 == true ? 1 : -1]; + char r[(_Bool) 0.0 == false ? 1 : -1]; + char u[sizeof (_Bool) > 0 ? 1 : -1]; + char v[sizeof t.t > 0 ? 1 : -1]; + + _Bool w[h]; + char x[sizeof m == h * sizeof m[0] ? 1 : -1]; + char y[-1 - (_Bool) 0 < 0 ? 1 : -1]; + _Bool z = true; + _Bool *pz = &p; + #endif + +int +main (void) +{ + + bool ps = &s; + *pp |= p; + *pp |= ! p; + + #ifdef HAVE__BOOL + _Bool pt = &t; + *pz |= z; + *pz |= ! z; + #endif + + /* Refer to every declared value, so they cannot be + discarded as unused. */ + return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !j + !k + + !l + !m + !n + !o + !p + !pp + !ps + #ifdef HAVE__BOOL + + !q + !r + !u + !v + !w + !x + !y + !z + !pt + #endif + ); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_header_stdbool_h=yes +else $as_nop + ac_cv_header_stdbool_h=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5 +printf "%s\n" "$ac_cv_header_stdbool_h" >&6; } + +if test $ac_cv_header_stdbool_h = yes; then + +printf "%s\n" "#define HAVE_STDBOOL_H 1" >>confdefs.h + +fi + +ac_fn_c_check_func "$LINENO" "getpagesize" "ac_cv_func_getpagesize" +if test "x$ac_cv_func_getpagesize" = xyes +then : + +fi + + + + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5 +printf %s "checking whether byte ordering is bigendian... " >&6; } +if test ${ac_cv_c_bigendian+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_c_bigendian=unknown + # See if we're dealing with a universal compiler. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifndef __APPLE_CC__ + not a universal capable compiler + #endif + typedef int dummy; + +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + + # Check for potential -arch flags. It is not universal unless + # there are at least two -arch flags with different values. + ac_arch= + ac_prev= + for ac_word in $CC $CFLAGS $CPPFLAGS $LDFLAGS; do + if test -n "$ac_prev"; then + case $ac_word in + i?86 | x86_64 | ppc | ppc64) + if test -z "$ac_arch" || test "$ac_arch" = "$ac_word"; then + ac_arch=$ac_word + else + ac_cv_c_bigendian=universal + break + fi + ;; + esac + ac_prev= + elif test "x$ac_word" = "x-arch"; then + ac_prev=arch + fi + done +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + if test $ac_cv_c_bigendian = unknown; then + # See if sys/param.h defines the BYTE_ORDER macro. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main (void) +{ +#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \ + && defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \ + && LITTLE_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + # It does; now see whether it defined to BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + +int +main (void) +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_c_bigendian=yes +else $as_nop + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # See if defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris). + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main (void) +{ +#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN) + bogus endian macros + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + # It does; now see whether it defined to _BIG_ENDIAN or not. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +int +main (void) +{ +#ifndef _BIG_ENDIAN + not big endian + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_c_bigendian=yes +else $as_nop + ac_cv_c_bigendian=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + fi + if test $ac_cv_c_bigendian = unknown; then + # Compile a test program. + if test "$cross_compiling" = yes +then : + # Try to guess by grepping values from an object file. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +unsigned short int ascii_mm[] = + { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; + unsigned short int ascii_ii[] = + { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; + int use_ascii (int i) { + return ascii_mm[i] + ascii_ii[i]; + } + unsigned short int ebcdic_ii[] = + { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; + unsigned short int ebcdic_mm[] = + { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; + int use_ebcdic (int i) { + return ebcdic_mm[i] + ebcdic_ii[i]; + } + extern int foo; + +int +main (void) +{ +return use_ascii (foo) == use_ebcdic (foo); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then + ac_cv_c_bigendian=yes + fi + if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi + fi +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_includes_default +int +main (void) +{ + + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long int l; + char c[sizeof (long int)]; + } u; + u.l = 1; + return u.c[sizeof (long int) - 1] == 1; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO" +then : + ac_cv_c_bigendian=no +else $as_nop + ac_cv_c_bigendian=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + + fi +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5 +printf "%s\n" "$ac_cv_c_bigendian" >&6; } + case $ac_cv_c_bigendian in #( + yes) + printf "%s\n" "#define WORDS_BIGENDIAN 1" >>confdefs.h +;; #( + no) + ;; #( + universal) + +printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h + + ;; #( + *) + as_fn_error $? "unknown endianness + presetting ac_cv_c_bigendian=no (or yes) will help" "$LINENO" 5 ;; + esac + + + + + + + + + + + + + + + + + + + + + + + # Make sure we can run config.sub. +$SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5 + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +printf %s "checking build system type... " >&6; } +if test ${ac_cv_build+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +printf "%s\n" "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +printf %s "checking host system type... " >&6; } +if test ${ac_cv_host+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` || + as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +printf "%s\n" "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + + +# Look for required tools. + + + + + +# Check for a working m4. +for ac_prog in gm4 m4 +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_M4+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$M4"; then + ac_cv_prog_M4="$M4" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_M4="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +M4=$ac_cv_prog_M4 +if test -n "$M4"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $M4" >&5 +printf "%s\n" "$M4" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$M4" && break +done +test -n "$M4" || M4="as_fn_error $? "Cannot find m4" "$LINENO" 5" + + +# Check for a working assembler. +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args. +set dummy ${ac_tool_prefix}as; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_AS+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$AS"; then + ac_cv_prog_AS="$AS" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_AS="${ac_tool_prefix}as" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AS=$ac_cv_prog_AS +if test -n "$AS"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AS" >&5 +printf "%s\n" "$AS" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_AS"; then + ac_ct_AS=$AS + # Extract the first word of "as", so it can be a program name with args. +set dummy as; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_AS+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_AS"; then + ac_cv_prog_ac_ct_AS="$ac_ct_AS" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AS="as" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AS=$ac_cv_prog_ac_ct_AS +if test -n "$ac_ct_AS"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AS" >&5 +printf "%s\n" "$ac_ct_AS" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_AS" = x; then + AS="as_fn_error $? "Cannot find a working assembler" "$LINENO" 5" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AS=$ac_ct_AS + fi +else + AS="$ac_cv_prog_AS" +fi + + +# And ar. +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. +set dummy ${ac_tool_prefix}ar; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_AR+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="${ac_tool_prefix}ar" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +printf "%s\n" "$AR" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_AR"; then + ac_ct_AR=$AR + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_AR+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="ar" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +printf "%s\n" "$ac_ct_AR" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_AR" = x; then + AR="as_fn_error $? "Cannot find a working ar" "$LINENO" 5" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +else + AR="$ac_cv_prog_AR" +fi + + +# And ld. +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ld", so it can be a program name with args. +set dummy ${ac_tool_prefix}ld; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_LD+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$LD"; then + ac_cv_prog_LD="$LD" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_LD="${ac_tool_prefix}ld" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +LD=$ac_cv_prog_LD +if test -n "$LD"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 +printf "%s\n" "$LD" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_LD"; then + ac_ct_LD=$LD + # Extract the first word of "ld", so it can be a program name with args. +set dummy ld; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_LD+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_LD"; then + ac_cv_prog_ac_ct_LD="$ac_ct_LD" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_LD="ld" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_LD=$ac_cv_prog_ac_ct_LD +if test -n "$ac_ct_LD"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LD" >&5 +printf "%s\n" "$ac_ct_LD" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_LD" = x; then + LD="as_fn_error $? "Cannot find a working linker" "$LINENO" 5" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + LD=$ac_ct_LD + fi +else + LD="$ac_cv_prog_LD" +fi + + +# Now check if ld is a C compiler. +LDPREFIX= +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether ld is a C compiler" >&5 +printf %s "checking whether ld is a C compiler... " >&6; } +if test ${exec_cv_ld_is_cc+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat <<_ACEOF > conftest.c + +int +main (void) +{ + + ; + return 0; +} +_ACEOF + exec_cv_ld_is_cc=yes + $LD -c conftest.c -o conftest.$OBJEXT >&5 2>&1 \ + || exec_cv_ld_is_cc=no + rm -f conftest.c conftest.$OBJEXT +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_ld_is_cc" >&5 +printf "%s\n" "$exec_cv_ld_is_cc" >&6; } + +# And if as is a C compiler. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether as is a C compiler" >&5 +printf %s "checking whether as is a C compiler... " >&6; } +if test ${exec_cv_as_is_cc+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat <<_ACEOF > conftest.c + +int +main (void) +{ + + ; + return 0; +} +_ACEOF + exec_cv_as_is_cc=yes + $AS -c conftest.c -o conftest.$OBJEXT >&5 2>&1 \ + || exec_cv_as_is_cc=no + rm -f conftest.c conftest.$OBJEXT +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_as_is_cc" >&5 +printf "%s\n" "$exec_cv_as_is_cc" >&6; } + +# If ld is a C compiler, pass `-nostdlib', `-nostartfiles', and +# `-static'. Also, set LDPREFIX to -Wl, +if test "x$exec_cv_ld_is_cc" = "xyes" +then : + LOADERFLAGS="$LOADERFLAGS -nostdlib -nostartfiles -static" + LDPREFIX=-Wl, +fi + +# If as is a C compiler, add `-c' to ASFLAGS. +if test "x$exec_cv_as_is_cc" = "xyes" +then : + ASFLAGS="$ASFLAGS -c" +fi + + + + + +# Determine the system type and define appropriate macros. +exec_loader= +is_mips= +OBJS="exec.o trace.o" + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC options needed to detect all undeclared functions" >&5 +printf %s "checking for $CC options needed to detect all undeclared functions... " >&6; } +if test ${ac_cv_c_undeclared_builtin_options+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_save_CFLAGS=$CFLAGS + ac_cv_c_undeclared_builtin_options='cannot detect' + for ac_arg in '' -fno-builtin; do + CFLAGS="$ac_save_CFLAGS $ac_arg" + # This test program should *not* compile successfully. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ +(void) strchr; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + +else $as_nop + # This test program should compile successfully. + # No library function is consistently available on + # freestanding implementations, so test against a dummy + # declaration. Include always-available headers on the + # off chance that they somehow elicit warnings. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include +extern void ac_decl (int, char *); + +int +main (void) +{ +(void) ac_decl (0, (char *) 0); + (void) ac_decl; + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + if test x"$ac_arg" = x +then : + ac_cv_c_undeclared_builtin_options='none needed' +else $as_nop + ac_cv_c_undeclared_builtin_options=$ac_arg +fi + break +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + done + CFLAGS=$ac_save_CFLAGS + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5 +printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; } + case $ac_cv_c_undeclared_builtin_options in #( + 'cannot detect') : + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot make $CC report undeclared builtins +See \`config.log' for more details" "$LINENO" 5; } ;; #( + 'none needed') : + ac_c_undeclared_builtin_options='' ;; #( + *) : + ac_c_undeclared_builtin_options=$ac_cv_c_undeclared_builtin_options ;; +esac + +case $host in #( + x86_64-*linux*) : + ac_fn_c_check_member "$LINENO" "struct user_regs_struct" "rdi" "ac_cv_member_struct_user_regs_struct_rdi" " +#include + +" +if test "x$ac_cv_member_struct_user_regs_struct_rdi" = xyes +then : + printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h + + printf "%s\n" "#define USER_HEADER " >>confdefs.h + + printf "%s\n" "#define USER_REGS_STRUCT struct user_regs_struct" >>confdefs.h + + printf "%s\n" "#define SYSCALL_NUM_REG orig_rax" >>confdefs.h + + printf "%s\n" "#define SYSCALL_RET_REG rax" >>confdefs.h + + printf "%s\n" "#define SYSCALL_ARG_REG rdi" >>confdefs.h + + printf "%s\n" "#define SYSCALL_ARG1_REG rsi" >>confdefs.h + + printf "%s\n" "#define STACK_POINTER rsp" >>confdefs.h + + printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h + + printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h + + printf "%s\n" "#define EXEC_64 1" >>confdefs.h + + printf "%s\n" "#define ABI_RED_ZONE 128" >>confdefs.h + + printf "%s\n" "#define EXECUTABLE_BASE 0x555555554000" >>confdefs.h + + printf "%s\n" "#define INTERPRETER_BASE 0x600000000000" >>confdefs.h + + printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h + + printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h + + +ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " +#include + +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl___NR_clone3" = xyes +then : + printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h + +fi + + # Make sure the loader doesn't conflict with other position + # dependent code. + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x200000000000" + exec_loader=loader-x86_64.s +else $as_nop + as_fn_error $? "Missing \`rdi' in user_regs_struct" "$LINENO" 5 +fi + ;; #( + i[34567]86-*linux*) : + ac_fn_c_check_member "$LINENO" "struct user_regs_struct" "edi" "ac_cv_member_struct_user_regs_struct_edi" " +#include + +" +if test "x$ac_cv_member_struct_user_regs_struct_edi" = xyes +then : + printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h + + printf "%s\n" "#define USER_HEADER " >>confdefs.h + + printf "%s\n" "#define USER_REGS_STRUCT struct user_regs_struct" >>confdefs.h + + printf "%s\n" "#define SYSCALL_NUM_REG orig_eax" >>confdefs.h + + printf "%s\n" "#define SYSCALL_RET_REG eax" >>confdefs.h + + printf "%s\n" "#define SYSCALL_ARG_REG ebx" >>confdefs.h + + printf "%s\n" "#define SYSCALL_ARG1_REG ecx" >>confdefs.h + + printf "%s\n" "#define STACK_POINTER esp" >>confdefs.h + + printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h + + printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h + + printf "%s\n" "#define EXECUTABLE_BASE 0x0f000000" >>confdefs.h + + printf "%s\n" "#define INTERPRETER_BASE 0xaf000000" >>confdefs.h + + printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h + + printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h + + +ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " +#include + +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl___NR_clone3" = xyes +then : + printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h + +fi + + # Make sure the loader doesn't conflict with other position + # dependent code. + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0xa0000000" + exec_loader=loader-x86.s +else $as_nop + as_fn_error $? "Missing \`edi' in user_regs_struct" "$LINENO" 5 +fi + ;; #( + aarch64-*linux*) : + ac_fn_c_check_member "$LINENO" "struct user_regs_struct" "sp" "ac_cv_member_struct_user_regs_struct_sp" " +#include + +" +if test "x$ac_cv_member_struct_user_regs_struct_sp" = xyes +then : + printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h + + printf "%s\n" "#define USER_HEADER " >>confdefs.h + + printf "%s\n" "#define USER_REGS_STRUCT struct user_regs_struct" >>confdefs.h + + printf "%s\n" "#define SYSCALL_NUM_REG regs[8]" >>confdefs.h + + printf "%s\n" "#define SYSCALL_RET_REG regs[0]" >>confdefs.h + + printf "%s\n" "#define SYSCALL_ARG_REG regs[0]" >>confdefs.h + + printf "%s\n" "#define SYSCALL_ARG1_REG regs[1]" >>confdefs.h + + printf "%s\n" "#define STACK_POINTER sp" >>confdefs.h + + printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h + + printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h + + printf "%s\n" "#define EXEC_64 1" >>confdefs.h + + printf "%s\n" "#define EXECUTABLE_BASE 0x3000000000" >>confdefs.h + + printf "%s\n" "#define INTERPRETER_BASE 0x3f00000000" >>confdefs.h + + printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h + + printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h + + +ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " +#include + +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl___NR_clone3" = xyes +then : + printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h + +fi + + # Make sure the loader doesn't conflict with other position + # dependent code. ARM places rather significant restrictions on + # virtual addresses for a 64 bit architecture. + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x2000000000" + exec_loader=loader-aarch64.s +else $as_nop + as_fn_error $? "Missing \`sp' in user_regs_struct" "$LINENO" 5 +fi + ;; #( + arm*linux*eabi* | armv7*linux*) : + ac_fn_c_check_member "$LINENO" "struct user_regs" "uregs" "ac_cv_member_struct_user_regs_uregs" " +#include + +" +if test "x$ac_cv_member_struct_user_regs_uregs" = xyes +then : + printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h + + printf "%s\n" "#define USER_HEADER " >>confdefs.h + + printf "%s\n" "#define USER_REGS_STRUCT struct user_regs" >>confdefs.h + + printf "%s\n" "#define SYSCALL_NUM_REG uregs[7]" >>confdefs.h + + printf "%s\n" "#define SYSCALL_RET_REG uregs[0]" >>confdefs.h + + printf "%s\n" "#define SYSCALL_ARG_REG uregs[0]" >>confdefs.h + + printf "%s\n" "#define SYSCALL_ARG1_REG uregs[1]" >>confdefs.h + + printf "%s\n" "#define STACK_POINTER uregs[13]" >>confdefs.h + + printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h + + printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h + + printf "%s\n" "#define EXECUTABLE_BASE 0x0f000000" >>confdefs.h + + printf "%s\n" "#define INTERPRETER_BASE 0x1f000000" >>confdefs.h + + printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h + + printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h + + +ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " +#include + +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl___NR_clone3" = xyes +then : + printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h + +fi + + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" + exec_loader=loader-armeabi.s +else $as_nop + as_fn_error $? "Missing \`uregs' in user_regs_struct" "$LINENO" 5 +fi + ;; #( + mipsel*linux*) : + printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h + + printf "%s\n" "#define USER_HEADER \"mipsel-user.h\"" >>confdefs.h + + printf "%s\n" "#define USER_REGS_STRUCT struct mipsel_regs" >>confdefs.h + + printf "%s\n" "#define SYSCALL_NUM_REG gregs[2]" >>confdefs.h + # v0 + printf "%s\n" "#define SYSCALL_RET_REG gregs[4]" >>confdefs.h + # a0 + printf "%s\n" "#define SYSCALL_ARG_REG gregs[4]" >>confdefs.h + # a0 + printf "%s\n" "#define SYSCALL_ARG1_REG gregs[5]" >>confdefs.h + # a1 + printf "%s\n" "#define STACK_POINTER gregs[29]" >>confdefs.h + # sp + printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h + + printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h + + printf "%s\n" "#define EXECUTABLE_BASE 0x0f000000" >>confdefs.h + + printf "%s\n" "#define INTERPRETER_BASE 0x1f000000" >>confdefs.h + + printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h + + printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h + + ac_fn_check_decl "$LINENO" "_MIPS_SIM" "ac_cv_have_decl__MIPS_SIM" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl__MIPS_SIM" = xyes +then : + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether MIPS NABI calling convention is used" >&5 +printf %s "checking whether MIPS NABI calling convention is used... " >&6; } +if test ${exec_cv_mips_nabi+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +int +main (void) +{ + +#ifndef __mips64__ +#if _MIPS_SIM == _ABIO32 +OABI in use. +#endif /* _MIPS_SIM == _ABIO32 */ +#endif /* !__mips64__ */ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + exec_cv_mips_nabi=yes +else $as_nop + exec_cv_mips_nabi=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_mips_nabi" >&5 +printf "%s\n" "$exec_cv_mips_nabi" >&6; } + +if test "x$exec_cv_mips_nabi" != "xno" +then : + +printf "%s\n" "#define MIPS_NABI 1" >>confdefs.h + +else $as_nop + OBJS="$OBJS mipsfpu.o" +fi + +else $as_nop + as_fn_error $? "_MIPS_SIM could not be determined" "$LINENO" 5, + [ +#include +] +fi + +ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " +#include + +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl___NR_clone3" = xyes +then : + printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h + +fi + + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" + is_mips=yes + exec_loader=loader-mipsel.s ;; #( + mips64el*linux*) : + printf "%s\n" "#define SYSCALL_HEADER " >>confdefs.h + + printf "%s\n" "#define USER_HEADER \"mipsel-user.h\"" >>confdefs.h + + printf "%s\n" "#define USER_REGS_STRUCT struct mipsel_regs" >>confdefs.h + + printf "%s\n" "#define SYSCALL_NUM_REG gregs[2]" >>confdefs.h + # v0 + printf "%s\n" "#define SYSCALL_RET_REG gregs[4]" >>confdefs.h + # a0 + printf "%s\n" "#define SYSCALL_ARG_REG gregs[4]" >>confdefs.h + # a0 + printf "%s\n" "#define SYSCALL_ARG1_REG gregs[5]" >>confdefs.h + # a1 + printf "%s\n" "#define STACK_POINTER gregs[29]" >>confdefs.h + # sp + printf "%s\n" "#define EXEC_SYSCALL __NR_execve" >>confdefs.h + + printf "%s\n" "#define USER_WORD uintptr_t" >>confdefs.h + + printf "%s\n" "#define EXEC_64 1" >>confdefs.h + + printf "%s\n" "#define EXECUTABLE_BASE 0x400000" >>confdefs.h + + printf "%s\n" "#define INTERPRETER_BASE 0x3f00000000" >>confdefs.h + + printf "%s\n" "#define STACK_GROWS_DOWNWARDS 1" >>confdefs.h + + printf "%s\n" "#define CLONE_SYSCALL __NR_clone" >>confdefs.h + + +ac_fn_check_decl "$LINENO" "__NR_clone3" "ac_cv_have_decl___NR_clone3" " +#include + +" "$ac_c_undeclared_builtin_options" "CFLAGS" +if test "x$ac_cv_have_decl___NR_clone3" = xyes +then : + printf "%s\n" "#define CLONE3_SYSCALL __NR_clone3" >>confdefs.h + +fi + + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether MIPS NABI calling convention is used" >&5 +printf %s "checking whether MIPS NABI calling convention is used... " >&6; } +if test ${exec_cv_mips_nabi+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include + +int +main (void) +{ + +#ifndef __mips64__ +#if _MIPS_SIM == _ABIO32 +OABI in use. +#endif /* _MIPS_SIM == _ABIO32 */ +#endif /* !__mips64__ */ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + exec_cv_mips_nabi=yes +else $as_nop + exec_cv_mips_nabi=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_mips_nabi" >&5 +printf "%s\n" "$exec_cv_mips_nabi" >&6; } + +if test "x$exec_cv_mips_nabi" != "xno" +then : + +printf "%s\n" "#define MIPS_NABI 1" >>confdefs.h + +else $as_nop + OBJS="$OBJS mipsfpu.o" +fi + + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x3e00000000" + is_mips=yes + exec_loader=loader-mips64el.s ;; #( + *) : + as_fn_error $? "Please port libexec to $host" "$LINENO" 5 ;; #( + *) : + ;; +esac + +MIPS_N32=$exec_cv_mips_nabi + + + + + +# Make the assembler optimize for code size. Don't do this on MIPS, +# as the assembler code manages branch delays manually. + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether as understands -O" >&5 +printf %s "checking whether as understands -O... " >&6; } +if test ${exec_cv_as_O+y} +then : + printf %s "(cached) " >&6 +else $as_nop + exec_cv_as_O=no + cat <<_ACEOF >conftest.s + .section text + .global _start +_start: + +_ACEOF + $AS $ASFLAGS -O conftest.s -o conftest.$OBJEXT \ + >&5 2>&1 \ + && exec_cv_as_O=yes + rm -f conftest.s conftest.$OBJEXT +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_as_O" >&5 +printf "%s\n" "$exec_cv_as_O" >&6; } + +if test "$exec_cv_as_O" = "yes" \ + && test "$is_mips" != "yes" +then : + ASFLAGS="$ASFLAGS -O" +fi + +# Make the assembler generate debug information. + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether as understands -g" >&5 +printf %s "checking whether as understands -g... " >&6; } +if test ${exec_cv_as_g+y} +then : + printf %s "(cached) " >&6 +else $as_nop + exec_cv_as_g=no + cat <<_ACEOF >conftest.s + .section text + .global _start +_start: + +_ACEOF + $AS $ASFLAGS -g conftest.s -o conftest.$OBJEXT \ + >&5 2>&1 \ + && exec_cv_as_g=yes + rm -f conftest.s conftest.$OBJEXT +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_as_g" >&5 +printf "%s\n" "$exec_cv_as_g" >&6; } +if test "$exec_cv_as_g" = "yes" +then : + ASFLAGS="$ASFLAGS -g" +fi + +# Check for the ability to automatically generate dependencies for C +# source files. +AUTO_DEPEND=no +if test "x$GCC" = xyes +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether gcc understands -MMD -MF" >&5 +printf %s "checking whether gcc understands -MMD -MF... " >&6; } +if test ${exec_cv_autodepend+y} +then : + printf %s "(cached) " >&6 +else $as_nop + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -MMD -MF deps.d -MP" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + exec_cv_autodepend=yes +else $as_nop + exec_cv_autodepend=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + CFLAGS="$SAVE_CFLAGS" + test -f deps.d || emacs_cv_autodepend=no + rm -rf deps.d +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_autodepend" >&5 +printf "%s\n" "$exec_cv_autodepend" >&6; } + if test "x$exec_cv_autodepend" = xyes +then : + AUTO_DEPEND=yes + as_dir=deps; as_fn_mkdir_p +fi +fi + +# Now check for some other stuff. + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for 'find' args to delete a file" >&5 +printf %s "checking for 'find' args to delete a file... " >&6; } +if test ${exec_cv_find_delete+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if touch conftest.tmp && find conftest.tmp -delete 2>/dev/null && + test ! -f conftest.tmp +then : + exec_cv_find_delete="-delete" +else $as_nop + exec_cv_find_delete="-exec rm -f {} ';'" +fi +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $exec_cv_find_delete" >&5 +printf "%s\n" "$exec_cv_find_delete" >&6; } +FIND_DELETE=$exec_cv_find_delete + + +ac_config_headers="$ac_config_headers config.h" + +ac_config_files="$ac_config_files Makefile config-mips.m4" + + + + + + + + + + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +printf "%s\n" "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else $as_nop + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + + +# Reset variables that may have inherited troublesome values from +# the environment. + +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. +as_nl=' +' +export as_nl +IFS=" "" $as_nl" + +PS1='$ ' +PS2='> ' +PS4='+ ' + +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done + +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi + +# The user is always right. +if ${PATH_SEPARATOR+false} :; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + printf "%s\n" "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else $as_nop + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else $as_nop + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by libexec $as_me 30.0.50, which was +generated by GNU Autoconf 2.71. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to . +libexec home page: ." + +_ACEOF +ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` +ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config='$ac_cs_config_escaped' +ac_cs_version="\\ +libexec config.status 30.0.50 +configured by $0, generated by GNU Autoconf 2.71, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2021 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + printf "%s\n" "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + printf "%s\n" "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + printf "%s\n" "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + printf "%s\n" "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "config-mips.m4") CONFIG_FILES="$CONFIG_FILES config-mips.m4" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files + test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +printf "%s\n" "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`printf "%s\n" "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + printf "%s\n" "/* $configure_input */" >&1 \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + printf "%s\n" "/* $configure_input */" >&1 \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + diff --git a/exec/configure.ac b/exec/configure.ac new file mode 100644 index 00000000000..0a334c6e4ff --- /dev/null +++ b/exec/configure.ac @@ -0,0 +1,418 @@ +dnl Autoconf script for GNU Emacs's exec library. +dnl To rebuild the 'configure' script from this, execute the command +dnl autoconf +dnl in the directory containing this script. +dnl If you changed any AC_DEFINES, also run autoheader. +dnl +dnl Copyright (C) 2023 Free Software Foundation, Inc. +dnl +dnl This file is part of GNU Emacs. +dnl +dnl GNU Emacs is free software: you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation, either version 3 of the License, or +dnl (at your option) any later version. +dnl +dnl GNU Emacs is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +dnl GNU General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with GNU Emacs. If not, see . + +AC_PREREQ([2.65]) +AC_INIT([libexec], [30.0.50], [bug-gnu-emacs@gnu.org], [], + [https://www.gnu.org/software/emacs/]) + +AH_TOP([/* Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at your +option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */]) + +AC_PROG_CC +AC_PROG_CPP +AC_PROG_INSTALL + +AC_TYPE_UINT8_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_UINTPTR_T +AC_TYPE_PID_T + +AC_HEADER_STDBOOL +AC_CHECK_FUNC([getpagesize]) + +AH_BOTTOM([ +#ifdef HAVE_STDBOOL_H +# include +#else +# ifndef HAVE__BOOL +# ifdef __cplusplus +typedef bool _Bool; +# else +# define _Bool signed char +# endif +# endif +# define bool _Bool +# define false 0 +# define true 1 +# define __bool_true_false_are_defined 1 +#endif +]) + +AC_C_BIGENDIAN + +AH_TEMPLATE([SYSCALL_HEADER], [Define to header holding system call numbers.]) +AH_TEMPLATE([USER_HEADER], [Define to header holding USER_REGS_STRUCT.]) +AH_TEMPLATE([USER_REGS_STRUCT], [Define to structure holding user registers.]) +AH_TEMPLATE([SYSCALL_NUM_REG], [Define to register holding the system call number.]) +AH_TEMPLATE([SYSCALL_ARG_REG], [Define to register holding arg0 to system calls.]) +AH_TEMPLATE([SYSCALL_ARG1_REG], [Define to register holding arg1 to system calls.]) +AH_TEMPLATE([SYSCALL_RET_REG], [Define to register holding value of system calls.]) +AH_TEMPLATE([STACK_POINTER], [Define to register holding the stack pointer.]) +AH_TEMPLATE([EXEC_SYSCALL], [Define to number of the `exec' system call.]) +AH_TEMPLATE([USER_WORD], [Define to word type used by tracees.]) +AH_TEMPLATE([EXEC_64], [Define to 1 if the system utilizes 64-bit ELF.]) +AH_TEMPLATE([STACK_GROWS_DOWNWARDS], [Define to 1 if the stack grows downwards.]) +AH_TEMPLATE([ABI_RED_ZONE], [Define to number of reserved bytes past the stack frame.]) +AH_TEMPLATE([EXECUTABLE_BASE], [Virtual address for loading PIC executables]) +AH_TEMPLATE([INTERPRETER_BASE], [Virtual address for loading PIC interpreters]) +AH_TEMPLATE([CLONE_SYSCALL], [Define to number of the `clone' system call.]) +AH_TEMPLATE([CLONE3_SYSCALL], [Define to number of the `clone3' system call.]) + +AC_CANONICAL_HOST + +# Look for required tools. + +AC_ARG_VAR([M4], [`m4' preprocessor command.]) +AC_ARG_VAR([AS], [`as' assembler command.]) +AC_ARG_VAR([LD], [`ld' linker command.]) + +# Check for a working m4. +AC_CHECK_PROGS([M4], [gm4 m4], + [AC_MSG_ERROR([Cannot find m4])]) + +# Check for a working assembler. +AC_CHECK_TOOL([AS], [as], + [AC_MSG_ERROR([Cannot find a working assembler])]) + +# And ar. +AC_CHECK_TOOL([AR], [ar], + [AC_MSG_ERROR([Cannot find a working ar])]) + +# And ld. +AC_CHECK_TOOL([LD], [ld], + [AC_MSG_ERROR([Cannot find a working linker])]) + +# Now check if ld is a C compiler. +LDPREFIX= +AC_CACHE_CHECK([whether ld is a C compiler], + [exec_cv_ld_is_cc], + [cat <<_ACEOF > conftest.c +AC_LANG_PROGRAM(,) +_ACEOF + exec_cv_ld_is_cc=yes + $LD -c conftest.c -o conftest.$OBJEXT >&AS_MESSAGE_LOG_FD 2>&1 \ + || exec_cv_ld_is_cc=no + rm -f conftest.c conftest.$OBJEXT]) + +# And if as is a C compiler. +AC_CACHE_CHECK([whether as is a C compiler], + [exec_cv_as_is_cc], + [cat <<_ACEOF > conftest.c +AC_LANG_PROGRAM(,) +_ACEOF + exec_cv_as_is_cc=yes + $AS -c conftest.c -o conftest.$OBJEXT >&AS_MESSAGE_LOG_FD 2>&1 \ + || exec_cv_as_is_cc=no + rm -f conftest.c conftest.$OBJEXT]) + +# If ld is a C compiler, pass `-nostdlib', `-nostartfiles', and +# `-static'. Also, set LDPREFIX to -Wl, +AS_IF([test "x$exec_cv_ld_is_cc" = "xyes"], + [LOADERFLAGS="$LOADERFLAGS -nostdlib -nostartfiles -static" + LDPREFIX=-Wl,]) + +# If as is a C compiler, add `-c' to ASFLAGS. +AS_IF([test "x$exec_cv_as_is_cc" = "xyes"], + [ASFLAGS="$ASFLAGS -c"]) + +AC_DEFUN([exec_CHECK_LINUX_CLONE3], +[ +AC_CHECK_DECL([__NR_clone3], + [AC_DEFINE([CLONE3_SYSCALL], [__NR_clone3])], + [], [[ +#include +]]) +]) + +AC_DEFUN([exec_CHECK_MIPS_NABI], +[ +AC_CACHE_CHECK([whether MIPS NABI calling convention is used], + [exec_cv_mips_nabi], + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +]], [[ +#ifndef __mips64__ +#if _MIPS_SIM == _ABIO32 +OABI in use. +#endif /* _MIPS_SIM == _ABIO32 */ +#endif /* !__mips64__ */ +]])], [exec_cv_mips_nabi=yes], + [exec_cv_mips_nabi=no])]) + +dnl mips64 systems use N64 calling convention, a variant of nabi +dnl calling convention. +AS_IF([test "x$exec_cv_mips_nabi" != "xno"], + [AC_DEFINE([MIPS_NABI], [1], + [Define to 1 if MIPS NABI calling convention is being used.])], + [OBJS="$OBJS mipsfpu.o"]) +]) + +# Determine the system type and define appropriate macros. +exec_loader= +is_mips= +OBJS="exec.o trace.o" + +AS_CASE([$host], [x86_64-*linux*], + [AC_CHECK_MEMBER([struct user_regs_struct.rdi], + [AC_DEFINE([SYSCALL_HEADER], []) + AC_DEFINE([USER_HEADER], []) + AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct]) + AC_DEFINE([SYSCALL_NUM_REG], [orig_rax]) + AC_DEFINE([SYSCALL_RET_REG], [rax]) + AC_DEFINE([SYSCALL_ARG_REG], [rdi]) + AC_DEFINE([SYSCALL_ARG1_REG], [rsi]) + AC_DEFINE([STACK_POINTER], [rsp]) + AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) + AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([EXEC_64], [1]) + AC_DEFINE([ABI_RED_ZONE], [128]) + AC_DEFINE([EXECUTABLE_BASE], [0x555555554000]) + AC_DEFINE([INTERPRETER_BASE], [0x600000000000]) + AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) + AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + exec_CHECK_LINUX_CLONE3 + # Make sure the loader doesn't conflict with other position + # dependent code. + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x200000000000" + exec_loader=loader-x86_64.s], + [AC_MSG_ERROR([Missing `rdi' in user_regs_struct])], + [[ +#include + ]])], [i[[34567]]86-*linux*], + [AC_CHECK_MEMBER([struct user_regs_struct.edi], + [AC_DEFINE([SYSCALL_HEADER], []) + AC_DEFINE([USER_HEADER], []) + AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct]) + AC_DEFINE([SYSCALL_NUM_REG], [orig_eax]) + AC_DEFINE([SYSCALL_RET_REG], [eax]) + AC_DEFINE([SYSCALL_ARG_REG], [ebx]) + AC_DEFINE([SYSCALL_ARG1_REG], [ecx]) + AC_DEFINE([STACK_POINTER], [esp]) + AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) + AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) + AC_DEFINE([INTERPRETER_BASE], [0xaf000000]) + AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) + AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + exec_CHECK_LINUX_CLONE3 + # Make sure the loader doesn't conflict with other position + # dependent code. + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0xa0000000" + exec_loader=loader-x86.s], + [AC_MSG_ERROR([Missing `edi' in user_regs_struct])], + [[ +#include + ]])], [aarch64-*linux*], + [AC_CHECK_MEMBER([struct user_regs_struct.sp], + [AC_DEFINE([SYSCALL_HEADER], []) + AC_DEFINE([USER_HEADER], []) + AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct]) + AC_DEFINE([SYSCALL_NUM_REG], [[regs[8]]]) + AC_DEFINE([SYSCALL_RET_REG], [[regs[0]]]) + AC_DEFINE([SYSCALL_ARG_REG], [[regs[0]]]) + AC_DEFINE([SYSCALL_ARG1_REG], [[regs[1]]]) + AC_DEFINE([STACK_POINTER], [sp]) + AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) + AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([EXEC_64], [1]) + AC_DEFINE([EXECUTABLE_BASE], [0x3000000000]) + AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) + AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) + AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + exec_CHECK_LINUX_CLONE3 + # Make sure the loader doesn't conflict with other position + # dependent code. ARM places rather significant restrictions on + # virtual addresses for a 64 bit architecture. + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x2000000000" + exec_loader=loader-aarch64.s], + [AC_MSG_ERROR([Missing `sp' in user_regs_struct])], + [[ +#include + ]])], [arm*linux*eabi* | armv7*linux*], + [AC_CHECK_MEMBER([struct user_regs.uregs], + [AC_DEFINE([SYSCALL_HEADER], []) + AC_DEFINE([USER_HEADER], []) + AC_DEFINE([USER_REGS_STRUCT], [struct user_regs]) + AC_DEFINE([SYSCALL_NUM_REG], [[uregs[7]]]) + AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]]) + AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]]) + AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]]) + AC_DEFINE([STACK_POINTER], [[uregs[13]]]) + AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) + AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) + AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) + AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) + AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + exec_CHECK_LINUX_CLONE3 + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" + exec_loader=loader-armeabi.s], + [AC_MSG_ERROR([Missing `uregs' in user_regs_struct])], + [[ +#include + ]])], [mipsel*linux*], + [AC_DEFINE([SYSCALL_HEADER], []) + AC_DEFINE([USER_HEADER], ["mipsel-user.h"]) + AC_DEFINE([USER_REGS_STRUCT], [struct mipsel_regs]) + AC_DEFINE([SYSCALL_NUM_REG], [[gregs[2]]]) # v0 + AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0 + AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0 + AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1 + AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp + AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) + AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([EXECUTABLE_BASE], [0x0f000000]) + AC_DEFINE([INTERPRETER_BASE], [0x1f000000]) + AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) + AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + AC_CHECK_DECL([_MIPS_SIM], [exec_CHECK_MIPS_NABI], + [AC_MSG_ERROR([_MIPS_SIM could not be determined]), + [[ +#include +]]]) + exec_CHECK_LINUX_CLONE3 + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" + is_mips=yes + exec_loader=loader-mipsel.s], [mips64el*linux*], + [AC_DEFINE([SYSCALL_HEADER], []) + AC_DEFINE([USER_HEADER], ["mipsel-user.h"]) + AC_DEFINE([USER_REGS_STRUCT], [struct mipsel_regs]) + AC_DEFINE([SYSCALL_NUM_REG], [[gregs[2]]]) # v0 + AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0 + AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0 + AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1 + AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp + AC_DEFINE([EXEC_SYSCALL], [__NR_execve]) + AC_DEFINE([USER_WORD], [uintptr_t]) + AC_DEFINE([EXEC_64], [1]) + AC_DEFINE([EXECUTABLE_BASE], [0x400000]) + AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) + AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) + AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) + exec_CHECK_LINUX_CLONE3 + exec_CHECK_MIPS_NABI + LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x3e00000000" + is_mips=yes + exec_loader=loader-mips64el.s], [*], + [AC_MSG_ERROR([Please port libexec to $host])]) + +MIPS_N32=$exec_cv_mips_nabi + +AC_ARG_VAR([LOADERFLAGS], [Flags used to link the loader.]) +AC_ARG_VAR([ARFLAGS], [Flags for the archiver.]) +AC_ARG_VAR([ASFLAGS], [Flags for the assembler.]) + +# Make the assembler optimize for code size. Don't do this on MIPS, +# as the assembler code manages branch delays manually. + +AC_CACHE_CHECK([whether as understands -O], + [exec_cv_as_O], + [exec_cv_as_O=no + cat <<_ACEOF >conftest.s + .section text + .global _start +_start: + +_ACEOF + $AS $ASFLAGS -O conftest.s -o conftest.$OBJEXT \ + >&AS_MESSAGE_LOG_FD 2>&1 \ + && exec_cv_as_O=yes + rm -f conftest.s conftest.$OBJEXT]) + +AS_IF([test "$exec_cv_as_O" = "yes" \ + && test "$is_mips" != "yes"], + [ASFLAGS="$ASFLAGS -O"]) + +# Make the assembler generate debug information. + +AC_CACHE_CHECK([whether as understands -g], + [exec_cv_as_g], + [exec_cv_as_g=no + cat <<_ACEOF >conftest.s + .section text + .global _start +_start: + +_ACEOF + $AS $ASFLAGS -g conftest.s -o conftest.$OBJEXT \ + >&AS_MESSAGE_LOG_FD 2>&1 \ + && exec_cv_as_g=yes + rm -f conftest.s conftest.$OBJEXT]) +AS_IF([test "$exec_cv_as_g" = "yes"], [ASFLAGS="$ASFLAGS -g"]) + +# Check for the ability to automatically generate dependencies for C +# source files. +AUTO_DEPEND=no +AS_IF([test "x$GCC" = xyes], + [AC_CACHE_CHECK([whether gcc understands -MMD -MF], + [exec_cv_autodepend], + [SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -MMD -MF deps.d -MP" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])], + [exec_cv_autodepend=yes], + [exec_cv_autodepend=no]) + CFLAGS="$SAVE_CFLAGS" + test -f deps.d || emacs_cv_autodepend=no + rm -rf deps.d]) + AS_IF([test "x$exec_cv_autodepend" = xyes], + [AUTO_DEPEND=yes + AS_MKDIR_P([deps])])]) + +# Now check for some other stuff. + +AC_CACHE_CHECK([for 'find' args to delete a file], + [exec_cv_find_delete], + [AS_IF([touch conftest.tmp && find conftest.tmp -delete 2>/dev/null && + test ! -f conftest.tmp], [exec_cv_find_delete="-delete"], + [exec_cv_find_delete="-exec rm -f {} ';'"])]) +FIND_DELETE=$exec_cv_find_delete +AC_SUBST([FIND_DELETE]) + +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([Makefile config-mips.m4]) + +AC_SUBST([AUTO_DEPEND]) +AC_SUBST([LOADERFLAGS]) +AC_SUBST([ARFLAGS]) +AC_SUBST([ASFLAGS]) +AC_SUBST([exec_loader]) +AC_SUBST([MIPS_N32]) +AC_SUBST([OBJS]) + +AC_OUTPUT diff --git a/exec/deps.mk b/exec/deps.mk new file mode 100644 index 00000000000..20fcd2dbc5a --- /dev/null +++ b/exec/deps.mk @@ -0,0 +1,21 @@ +### deps.mk + +## Copyright (C) 2023 Free Software Foundation, Inc. + +## This file is part of GNU Emacs. + +## GNU Emacs 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 3 of the License, or +## (at your option) any later version. +## +## GNU Emacs 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 GNU Emacs. If not, see . + +exec.o: exec.h config.h +trace.o: exec.h config.h diff --git a/exec/exec.c b/exec/exec.c new file mode 100644 index 00000000000..e890179a9ab --- /dev/null +++ b/exec/exec.c @@ -0,0 +1,1016 @@ +/* Program execution for Emacs. + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif /* MIN */ + +#ifndef MAX +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#endif /* MAX */ + +#include "exec.h" + +#if defined __mips__ && !defined MIPS_NABI +#include "mipsfpu.h" +#endif /* defined __mips__ && !defined MIPS_NABI */ + + + +/* Executable reading functions. + These functions extract information from an executable that is + about to be loaded. + + `exec_0' takes the name of the program, determines whether or not + its format is correct, and if so, returns the list of actions that + the loader should perform. + + The actions include: + + - Making the stack executable, if PT_GNU_STACK. + - Mapping PT_LOAD sections into the executable with the correct + memory protection. + - On MIPS, setting the floating point register size. + - Transferring control to the interpreter or executable. */ + + +/* Check whether or not FD starts with a #!, and return the executable + to load if it does. Value is NAME if no interpreter character was + found, or the interpreter otherwise. Value is NULL upon an IO + error. + + If an additional command line argument is specified, place it in + *EXTRA. */ + +static const char * +check_interpreter (const char *name, int fd, const char **extra) +{ + static char buffer[PATH_MAX], *start; + char first[2], *end, *ws; + ssize_t rc; + + /* Read the first character. */ + rc = read (fd, &first, 2); + + if (rc != 2) + goto fail; + + if (first[0] != '#' || first[1] != '!') + goto nomatch; + + rc = read (fd, buffer, PATH_MAX); + + if (rc < 0) + goto fail; + + /* Strip leading whitespace. */ + start = buffer; + while (*start && *start < 128 && isspace (*start)) + ++start; + + /* Look for a newline character. */ + end = memchr (start, '\n', rc); + + if (!end) + goto fail; + + /* The string containing the interpreter is now in start. NULL + terminate it. */ + *end = '\0'; + + /* Now look for any whitespace characters. */ + ws = strchr (start, ' '); + + /* If there's no whitespace, return the entire start. */ + + if (!ws) + { + if (lseek (fd, 0, SEEK_SET)) + goto fail; + + return start; + } + + /* Otherwise, split the string at the whitespace and return the + additional argument. */ + *ws = '\0'; + + if (lseek (fd, 0, SEEK_SET)) + goto fail; + + *extra = ws + 1; + return start; + + nomatch: + /* There's no interpreter. */ + if (lseek (fd, 0, SEEK_SET)) + goto fail; + + return name; + + fail: + errno = ENOEXEC; + return NULL; +} + +/* Static area used to store data placed on the loader's stack. */ +static char loader_area[65536]; + +/* Number of bytes used in that area. */ +static int loader_area_used; + + + +/* Structure definitions for commands placed in the loader area. + Arrange these so that each member is naturally aligned. */ + +struct exec_open_command +{ + /* Word identifying the type of this command. */ + USER_WORD command; + + /* NULL-terminated file name follows, padded to the size of a user + word. */ +}; + +struct exec_map_command +{ + /* Word identifying the type of this command. */ + USER_WORD command; + + /* Where the file will be mapped. */ + USER_WORD vm_address; + + /* Offset into the file to map from. */ + USER_WORD file_offset; + + /* Memory protection for mprotect. */ + USER_WORD protection; + + /* Number of bytes to be mapped. */ + USER_WORD length; + + /* Flags for mmap. */ + USER_WORD flags; + + /* Number of bytes to clear at the end of this mapping. */ + USER_WORD clear; +}; + +struct exec_jump_command +{ + /* Word identifying the type of this command. */ + USER_WORD command; + + /* Address to jump to. */ + USER_WORD entry; + + /* The value of AT_ENTRY inside the aux vector. */ + USER_WORD at_entry; + + /* The value of AT_PHENT inside the aux vector. */ + USER_WORD at_phent; + + /* The value of AT_PHNUM inside the aux vector. */ + USER_WORD at_phnum; + + /* The value of AT_PHDR inside the aux vector. */ + USER_WORD at_phdr; + + /* The value of AT_BASE inside the aux vector. */ + USER_WORD at_base; + +#if defined __mips__ && !defined MIPS_NABI + /* The FPU mode to apply. */ + USER_WORD fpu_mode; +#endif /* defined __mips__ && !defined MIPS_NABI */ +}; + + + +/* Write a command to open the file NAME to the loader area. + If ALTERNATE is true, then use the command code 16 instead + of 0. Value is 1 upon failure, else 0. */ + +static int +write_open_command (const char *name, bool alternate) +{ + struct exec_open_command command; + size_t size; + + /* First, write the command to open NAME. This is followed by NAME + itself, padded to sizeof (USER_WORD) bytes. */ + + command.command = alternate ? 16 : 0; + if (sizeof loader_area - loader_area_used < sizeof command) + return 1; + memcpy (loader_area + loader_area_used, &command, sizeof command); + loader_area_used += sizeof command; + + /* Calculate the length of NAME. */ + size = strlen (name) + 1; + + /* Round it up. */ + size = ((size + (sizeof (USER_WORD) - 1)) + & ~(sizeof (USER_WORD) - 1)); + + if (sizeof loader_area - loader_area_used < size) + return 1; + + /* Now copy name to the loader area, filling the padding with NULL + bytes. */ + strncpy (loader_area + loader_area_used, name, size); + + /* Increase loader_area_used. */ + loader_area_used += size; + return 0; +} + +/* Write the commands necessary to map the executable file into memory + for the given PT_LOAD program HEADER. Value is 1 upon failure, + else 0. If USE_ALTERNATE, use the command code 17 instead of + 1. + + Apply the given OFFSET to virtual addresses that will be mapped. */ + +static int +write_load_command (program_header *header, bool use_alternate, + USER_WORD offset) +{ + struct exec_map_command command; + struct exec_map_command command1; + USER_WORD start, end; + bool need_command1; + static long pagesize; + + /* First, write the commands necessary to map the specified segment + itself. + + This is the area between header->p_vaddr and header->p_filesz, + rounded up to the page size. */ + +#ifndef PAGE_MASK + /* This system doesn't define a fixed page size. */ + +#ifdef HAVE_GETPAGESIZE + if (!pagesize) + pagesize = getpagesize (); +#else /* HAVE_GETPAGESIZE */ + if (!pagesize) + pagesize = sysconf (_SC_PAGESIZE); + +#define PAGE_MASK (~(pagesize - 1)) +#define PAGE_SIZE (pagesize) +#endif /* HAVE_GETPAGESIZE */ +#endif /* PAGE_MASK */ + + start = header->p_vaddr & PAGE_MASK; + end = ((header->p_vaddr + header->p_filesz + + PAGE_SIZE) + & PAGE_MASK); + + command.command = use_alternate ? 17 : 1; + command.vm_address = start; + command.file_offset = header->p_offset & PAGE_MASK; + command.protection = 0; + command.length = end - start; + command.clear = 0; + command.flags = MAP_PRIVATE | MAP_FIXED; + + /* Apply the memory protection specified in the header. */ + + if (header->p_flags & 4) /* PF_R */ + command.protection |= PROT_READ; + + if (header->p_flags & 2) /* PF_W */ + command.protection |= PROT_WRITE; + + if (header->p_flags & 1) /* PF_X */ + command.protection |= PROT_EXEC; + + /* Next, write any command necessary to map pages in the area + between p_filesz and p_memsz. */ + need_command1 = false; + + if (header->p_memsz > header->p_filesz) + { + /* If there are bytes after end which need to be initialized, do + that now. */ + command.clear = end - header->p_vaddr - header->p_filesz; + start = end; + end = header->p_vaddr + header->p_memsz + PAGE_SIZE; + end &= PAGE_MASK; + + if (end > start) + { + command1.command = 4; + command1.vm_address = start; + command1.file_offset = 0; + command1.length = end - start; + command1.clear = 0; + command1.protection = command.protection; + command1.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED; + need_command1 = true; + } + } + + /* Apply the offset to both commands if necessary. */ + + if (offset) + { + if (need_command1) + command1.vm_address += offset; + + command.vm_address += offset; + } + + /* Write both commands. */ + + if (sizeof loader_area - loader_area_used < sizeof command) + return 1; + + memcpy (loader_area + loader_area_used, &command, + sizeof command); + loader_area_used += sizeof command; + + if (!need_command1) + return 0; + + if (sizeof loader_area - loader_area_used < sizeof command1) + return 1; + + memcpy (loader_area + loader_area_used, &command1, + sizeof command1); + loader_area_used += sizeof command1; + + return 0; +} + +#if defined __mips__ && !defined MIPS_NABI + +/* Static storage used for MIPS ABI flags. */ +static struct mips_elf_abi_flags exec_abi, interpreter_abi; + +/* Static storage for interpreter headers. */ +static elf_header exec_interpreter_header; + +/* Pointer to the ELF header of this executable's interpreter. */ +static elf_header *interpreter_header; + +/* Pointer to any PT_MIPS_ABIFLAGS program header found in the + executable itself. */ +static struct mips_elf_abi_flags *exec_abiflags; + +/* Pointer to any PT_MIPS_ABIFLAGS program header found in the + executable's ELF interpreter. */ +static struct mips_elf_abi_flags *interpreter_abiflags; + +#endif /* defined __mips__ && !defined MIPS_NABI */ + +/* Process the specified program HEADER; HEADER is from the ELF + interpreter of another executable. FD is the executable file from + which it is being read, NAME is its file name, and ELF_HEADER is + its header. + + If ELF_HEADER->e_type is ET_DYN, add the base address for position + independent interpreter code to virtual addresses. + + Value is 1 upon failure, else 0. */ + +static int +process_interpreter_1 (const char *name, int fd, + program_header *header, + elf_header *elf_header) +{ + int rc; +#if defined __mips__ && !defined MIPS_NABI + ssize_t rc1; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + switch (header->p_type) + { + default: /* PT_NULL, PT_NOTE, PT_DYNAMIC, PT_INTERP, et cetera */ + rc = 0; + break; + + case 1: /* PT_LOAD */ + /* This describes a segment in the file that must be loaded. + Write the appropriate load command. */ + + if (elf_header->e_type == 3) /* ET_DYN */ + rc = write_load_command (header, true, + INTERPRETER_BASE); + else + rc = write_load_command (header, true, 0); + + break; + +#if defined __mips__ && !defined MIPS_NABI + case 0x70000003: /* PT_MIPS_ABIFLAGS */ + /* Record this header for later use. */ + rc1 = pread (fd, &interpreter_abi, sizeof interpreter_abi, + header->p_offset); + + if (rc1 != sizeof interpreter_abi) + return 1; + + interpreter_abiflags = &interpreter_abi; + rc = 0; +#endif /* defined __mips__ && !defined MIPS_NABI */ + } + + return rc; +} + +/* Read the ELF interpreter specified in the given program header from + FD, and append the commands necessary to load it to the load area. + Then, return the interpreter entry point in *ENTRY. + + Value is 1 upon failure, else 0. */ + +static int +process_interpreter (int fd, program_header *prog_header, + USER_WORD *entry) +{ + char buffer[PATH_MAX + 1]; + int rc, size, i; + elf_header header; + program_header program; + + /* Read the interpreter name. */ + size = MIN (prog_header->p_filesz, PATH_MAX); + rc = pread (fd, buffer, size, prog_header->p_offset); + if (rc < size) + return 1; + + /* Make sure the name is NULL terminated. */ + buffer[size] = '\0'; + + /* Check if the file is executable. This is unfortunately not + atomic. */ + + if (access (buffer, X_OK)) + return 1; + + /* Read the interpreter's header much like exec_0. + + However, use special command codes in `process_program_header' if + it is position independent. That way, the loader knows it should + use the open interpreter instead. */ + + fd = open (buffer, O_RDONLY); + + if (fd < 0) + return 1; + + rc = read (fd, &header, sizeof header); + + if (rc < sizeof header) + goto fail; + +#if defined __mips__ && !defined MIPS_NABI + /* Record this interpreter's header for later use determining the + floating point ABI. */ + exec_interpreter_header = header; + interpreter_header = &exec_interpreter_header; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + /* Verify that this is indeed an ELF file. */ + + if (header.e_ident[0] != 0x7f + || header.e_ident[1] != 'E' + || header.e_ident[2] != 'L' + || header.e_ident[3] != 'F') + goto fail; + + /* Now check that the class is correct. */ +#ifdef EXEC_64 + if (header.e_ident[4] != 2) + goto fail; +#else /* !EXEC_64 */ + if (header.e_ident[4] != 1) + goto fail; +#endif /* EXEC_64 */ + + /* And the endianness. */ +#ifndef WORDS_BIGENDIAN + if (header.e_ident[5] != 1) + goto fail; +#else /* WORDS_BIGENDIAN */ + if (header.e_ident[5] != 2) + goto fail; +#endif /* EXEC_64 */ + + /* Check that this is an executable. */ + if (header.e_type != 2 && header.e_type != 3) + goto fail; + + /* Now check that the ELF program header makes sense. */ + if (header.e_phnum > 0xffff + || (header.e_phentsize + != sizeof (program_header))) + goto fail; + + if (write_open_command (buffer, true)) + goto fail; + + for (i = 0; i < header.e_phnum; ++i) + { + rc = read (fd, &program, sizeof program); + if (rc < sizeof program) + goto fail; + + if (process_interpreter_1 (buffer, fd, &program, + &header)) + goto fail; + } + + if (header.e_type == 3) /* ET_DYN */ + *entry = header.e_entry + INTERPRETER_BASE; + else + *entry = header.e_entry; + + close (fd); + return 0; + + fail: + close (fd); + return 1; +} + +/* Process the specified program HEADER. FD is the executable file + from which it is being read, NAME is its file name, and ELF_HEADER + is its header. + + If ELF_HEADER->e_type is ET_DYN, add the base address for position + independent code to virtual addresses. + + If OFFSET is non-NULL, and *OFFSET is -1, write the virtual address + of HEADER if it describes a PT_LOAD segment. + + If an interpreter is found, set *ENTRY to its entry point. + + Value is 1 upon failure, else 0. */ + +static int +process_program_header (const char *name, int fd, + program_header *header, + elf_header *elf_header, + USER_WORD *entry, + USER_WORD *offset) +{ + int rc; +#if defined __mips__ && !defined MIPS_NABI + ssize_t rc1; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + switch (header->p_type) + { + default: /* PT_NULL, PT_NOTE, PT_DYNAMIC, et cetera */ + rc = 0; + break; + + case 1: /* PT_LOAD */ + /* This describes a segment in the file that must be loaded. + Write the appropriate load command. */ + + if (elf_header->e_type == 3) /* ET_DYN */ + { + rc = write_load_command (header, false, + EXECUTABLE_BASE); + + if (!rc && offset && *offset == (USER_WORD) -1) + *offset = EXECUTABLE_BASE + header->p_vaddr; + } + else + { + rc = write_load_command (header, false, 0); + + if (!rc && offset && *offset == (USER_WORD) -1) + *offset = header->p_vaddr; + } + + break; + + case 3: /* PT_INTERP */ + /* This describes another executable that must be loaded. + Open the interpreter and process each of its headers + as well. */ + rc = process_interpreter (fd, header, entry); + break; + + case 1685382481: /* PT_GNU_STACK */ + /* TODO */ + rc = 0; + break; + +#if defined __mips__ && !defined MIPS_NABI + case 0x70000003: /* PT_MIPS_ABIFLAGS */ + /* Record this header for later use. */ + rc1 = pread (fd, &exec_abi, sizeof exec_abi, + header->p_offset); + + if (rc1 != sizeof exec_abi) + return 1; + + exec_abiflags = &exec_abi; + rc = 0; +#endif /* defined __mips__ && !defined MIPS_NABI */ + } + + return rc; +} + +/* Prepend one or two extra arguments ARG1 and ARG2 to a pending + execve system call. TRACEE is the tracee performing the system + call, and REGS are its current user registers. Value is 1 upon + failure, else 0. */ + +static int +insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, + const char *arg1, const char *arg2) +{ + USER_WORD argv, argc, word, new; + USER_WORD new1, new2; + size_t text_size, effective_size; + USER_REGS_STRUCT original; + + /* First, get a pointer to the current argument vector. */ + argv = regs->SYSCALL_ARG1_REG; + + /* Now figure out how many arguments there are. */ + argc = 0; + while (true) + { + /* Clear errno. PTRACE_PEEKDATA returns the word read the same + way failure indications are returned, so the only way to + catch IO errors is by clearing errno before the call to + ptrace and checking it afterwards. */ + + errno = 0; + word = ptrace (PTRACE_PEEKDATA, tracee->pid, + (void *) argv, NULL); + argv += sizeof (USER_WORD); + + if (errno) + return 1; + + if (!word) + break; + + ++argc; + }; + + /* Allocate enough to hold that many arguments, alongside the argc + text. */ + + text_size = (strlen (arg1) + 1 + + (arg2 ? strlen (arg2) + 1 : 0)); + + /* Round it up to the user word size. */ + text_size += sizeof (USER_WORD) - 1; + text_size &= ~(sizeof (USER_WORD) - 1); + + /* Now allocate the new argv. */ + + effective_size = sizeof word * (argc + 2) + text_size; + + if (arg2) + effective_size += sizeof word; + + /* Copy regs to original so that user_alloca knows it should append + the ABI red zone. */ + + memcpy (&original, regs, sizeof *regs); + new = user_alloca (tracee, &original, regs, + effective_size); + + if (!new) + goto fail; + + /* Figure out where argv starts. */ + + new2 = new + text_size; + + /* Now write the two strings. */ + + new1 = new + strlen (arg1) + 1; + if (user_copy (tracee, (const unsigned char *) arg1, + new, new1 - new)) + goto fail; + + if (arg2 && user_copy (tracee, (const unsigned char *) arg2, + new1, new2 - new1)) + goto fail; + + /* Start copying argv back to new2. First, write the one or two new + arguments. */ + + if (ptrace (PTRACE_POKETEXT, tracee->pid, + (void *) new2, (void *) new)) + goto fail; + + new2 += sizeof new2; + + if (arg2 && ptrace (PTRACE_POKETEXT, tracee->pid, + (void *) new2, (void *) new1)) + goto fail; + else if (arg2) + new2 += sizeof new2; + + /* Copy the remaining arguments back. */ + + argv = regs->SYSCALL_ARG1_REG; + + /* Make sure the trailing NULL is included. */ + argc += 1; + + while (argc) + { + /* Read one argument. */ + word = ptrace (PTRACE_PEEKDATA, tracee->pid, + (void *) argv, NULL); + argv += sizeof argv; + argc--; + + /* Write one argument, then increment new2. */ + + if (ptrace (PTRACE_POKETEXT, tracee->pid, + (void *) new2, (void *) word)) + goto fail; + + new2 += sizeof new2; + } + + /* Assert that new2 is not out of bounds. */ + assert (new2 == new + effective_size); + + /* And that it is properly aligned. */ + assert (!(new2 & (sizeof new2 - 2))); + + /* Now modify the system call argument to point to new + + text_size. */ + + regs->SYSCALL_ARG1_REG = new + text_size; + +#ifdef __aarch64__ + if (aarch64_set_regs (tracee->pid, regs, false)) + goto fail; +#else /* !__aarch64__ */ + if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs)) + goto fail; +#endif /* __aarch64__ */ + + /* Success. */ + + return 0; + + fail: + /* Restore the original stack pointer. */ +#ifdef __aarch64__ + aarch64_set_regs (tracee->pid, &original, false); +#else /* !__aarch64__ */ + ptrace (PTRACE_SETREGS, tracee->pid, NULL, &original); +#endif /* __aarch64__ */ + errno = ENOMEM; + return 1; +} + + + +/* Return a sequence of actions required to load the executable under + the file NAME for the given TRACEE. First, see if the file starts + with #!; in that case, find the program to open and use that + instead. + + Next, read the executable header, and add the necessary memory + mappings for each file. Finally, return the action data and its + size in *SIZE. + + Finally, use REGS to add the required interpreter arguments to the + caller's argv. + + Value is NULL upon failure, with errno set accordingly. */ + +char * +exec_0 (const char *name, struct exec_tracee *tracee, + size_t *size, USER_REGS_STRUCT *regs) +{ + int fd, rc, i; + elf_header header; + const char *interpreter_name, *extra; + program_header program; + USER_WORD entry, program_entry, offset; + USER_WORD header_offset; + struct exec_jump_command jump; +#if defined __mips__ && !defined MIPS_NABI + int fpu_mode; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + fd = open (name, O_RDONLY); + if (fd < 0) + return NULL; + + /* Now read the header. */ + + extra = NULL; + interpreter_name = check_interpreter (name, fd, &extra); + if (!interpreter_name) + goto fail; + + /* Open the interpreter instead, if necessary. */ + if (interpreter_name != name) + { + close (fd); + fd = open (interpreter_name, O_RDONLY); + if (fd < 0) + return NULL; + + /* Now, rewrite the argument list to include `interpreter_name' + and perhaps `extra'. */ + + if (insert_args (tracee, regs, interpreter_name, + extra)) + goto fail1; + } + + rc = read (fd, &header, sizeof header); + + if (rc < sizeof header) + goto fail1; + + /* Verify that this is indeed an ELF file. */ + + if (header.e_ident[0] != 0x7f + || header.e_ident[1] != 'E' + || header.e_ident[2] != 'L' + || header.e_ident[3] != 'F') + goto fail1; + + /* Now check that the class is correct. */ +#ifdef EXEC_64 + if (header.e_ident[4] != 2) + goto fail1; +#else /* !EXEC_64 */ + if (header.e_ident[4] != 1) + goto fail1; +#endif /* EXEC_64 */ + + /* And the endianness. */ +#ifndef WORDS_BIGENDIAN + if (header.e_ident[5] != 1) + goto fail1; +#else /* WORDS_BIGENDIAN */ + if (header.e_ident[5] != 2) + goto fail1; +#endif /* EXEC_64 */ + + /* Check that this is an executable. */ + if (header.e_type != 2 && header.e_type != 3) + goto fail1; + + /* Now check that the ELF program header makes sense. */ + if (header.e_phnum > 0xffff + || (header.e_phentsize + != sizeof (program_header))) + goto fail1; + + /* Seek to the first program header and read each one. */ + rc = lseek (fd, header.e_phoff, SEEK_SET); + if (rc < 0) + goto fail1; + loader_area_used = 0; + + /* Write the command used to open the executable. */ + if (write_open_command (interpreter_name, false)) + goto fail1; + + /* Apply base addresses for PIC code. */ + + if (header.e_type == 3) /* ET_DYN */ + offset = EXECUTABLE_BASE; + else + offset = 0; + + /* entry and program_entry are initially the same, but entry may be + set to that of the interpreter if one is present. */ + + entry = header.e_entry + offset; + program_entry = header.e_entry; + +#if defined __mips__ && !defined MIPS_NABI + /* Clear MIPS ABI flags. */ + exec_abiflags = NULL; + interpreter_abiflags = NULL; + interpreter_header = NULL; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + /* Set header_offset to -1; `process_program_header' then updates it + to that of the first mapping. */ + header_offset = -1; + + for (i = 0; i < header.e_phnum; ++i) + { + rc = read (fd, &program, sizeof program); + if (rc < sizeof program) + goto fail1; + + if (process_program_header (interpreter_name, fd, + &program, &header, + &entry, &header_offset)) + goto fail1; + } + + /* Write the entry point and program entry. */ + + jump.command = 3; + jump.entry = entry; + + /* Now calculate values for the aux vector. */ + + jump.at_entry = program_entry + offset; + jump.at_phent = header.e_phentsize; + jump.at_phnum = header.e_phnum; + jump.at_base = (entry == header.e_entry + offset + ? EXECUTABLE_BASE + : INTERPRETER_BASE); + +#if defined __mips__ && !defined MIPS_NABI + /* Finally, calculate the FPU mode wanted by the executable. */ + + if (determine_fpu_mode (&header, interpreter_header, + &fpu_mode, exec_abiflags, + interpreter_abiflags)) + /* N.B. that `determine_fpu_mode' sets errno. */ + goto fail; + + /* If the processor is too new to support FR0 operation, place the + executable in floating point emulation mode. */ + + if (fpu_mode == FP_FR0 && !cpu_supports_fr0_p ()) + fpu_mode = FP_FRE; + + jump.fpu_mode = fpu_mode; +#endif /* defined __mips__ && !defined MIPS_NABI */ + + /* The offset used for at_phdr should be that of the first + mapping. */ + + if (header_offset == (USER_WORD) -1) + header_offset = 0; + + jump.at_phdr = header.e_phoff + header_offset; + + if (sizeof loader_area - loader_area_used < sizeof jump) + goto fail1; + + memcpy (loader_area + loader_area_used, &jump, + sizeof jump); + loader_area_used += sizeof jump; + + /* Close the file descriptor and return the number of bytes + used. */ + + close (fd); + *size = loader_area_used; + + /* Make sure the loader area is properly aligned. */ + assert (!(loader_area_used & (sizeof (USER_WORD) - 1))); + return loader_area; + + fail1: + errno = ENOEXEC; + fail: + close (fd); + return NULL; +} diff --git a/exec/exec.h b/exec/exec.h new file mode 100644 index 00000000000..0f6a8a893b6 --- /dev/null +++ b/exec/exec.h @@ -0,0 +1,192 @@ +/* Program execution for Emacs. + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + + + +#ifndef _EXEC_H_ +#define _EXEC_H_ + +#ifdef HAVE_STDINT_H +#include +#endif /* HAVE_STDINT_H */ + +#include + +#include USER_HEADER + +/* Define a replacement for `uint64_t' if it's not present in the C + library. */ + +#ifndef UINT64_MAX + +typedef struct +{ + uint32_t word1; + uint32_t word2; +} xint64_t; + +#else /* UINT64_MAX */ +typedef uint64_t xint64_t; +#endif /* !UINT64_MAX */ + + + +/* 32-bit ELF headers. */ + +struct elf_header_32 +{ + unsigned char e_ident[16]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + uint32_t e_entry; + uint32_t e_phoff; + uint32_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +}; + +struct program_header_32 +{ + uint32_t p_type; + uint32_t p_offset; + uint32_t p_vaddr; + uint32_t p_paddr; + uint32_t p_filesz; + uint32_t p_memsz; + uint32_t p_flags; + uint32_t p_align; +}; + +struct dt_entry_32 +{ + uint32_t d_tag; + uint32_t d_val; +}; + + + +struct elf_header_64 +{ + unsigned char e_ident[16]; + uint16_t e_type; + uint16_t e_machine; + uint32_t e_version; + xint64_t e_entry; + xint64_t e_phoff; + xint64_t e_shoff; + uint32_t e_flags; + uint16_t e_ehsize; + uint16_t e_phentsize; + uint16_t e_phnum; + uint16_t e_shentsize; + uint16_t e_shnum; + uint16_t e_shstrndx; +}; + +struct program_header_64 +{ + uint32_t p_type; + uint32_t p_flags; + xint64_t p_offset; + xint64_t p_vaddr; + xint64_t p_paddr; + xint64_t p_filesz; + xint64_t p_memsz; + xint64_t p_align; +}; + +struct dt_entry_64 +{ + xint64_t d_tag; + xint64_t d_val; +}; + + + +/* Define some types to the correct values. */ + +#ifdef EXEC_64 +typedef struct elf_header_64 elf_header; +typedef struct program_header_64 program_header; +typedef struct dt_entry_64 dt_entry; +#else /* !EXEC_64 */ +typedef struct elf_header_32 elf_header; +typedef struct program_header_32 program_header; +typedef struct dt_entry_32 dt_entry; +#endif /* EXEC_64 */ + + + +/* Defined in trace.c. */ + +/* Structure describing a process being traced. */ + +struct exec_tracee +{ + /* The next process being traced. */ + struct exec_tracee *next; + + /* The thread ID of this process. */ + pid_t pid; + + /* Whether or not the tracee is currently waiting for a system call + to complete. */ + bool waiting_for_syscall; +}; + + + +#ifdef __aarch64__ + +extern int aarch64_get_regs (pid_t, USER_REGS_STRUCT *); +extern int aarch64_set_regs (pid_t, USER_REGS_STRUCT *, bool); + +#endif /* __aarch64__ */ + + + +extern USER_WORD user_alloca (struct exec_tracee *, USER_REGS_STRUCT *, + USER_REGS_STRUCT *, USER_WORD); +extern int user_copy (struct exec_tracee *, const unsigned char *, + USER_WORD, USER_WORD); +extern void exec_init (const char *); + + + +extern int tracing_execve (const char *, char *const *, + char *const *); +extern int after_fork (pid_t); +extern pid_t exec_waitpid (pid_t, int *, int); + + + +/* Defined in exec.c. */ + +extern char *exec_0 (const char *, struct exec_tracee *, + size_t *, USER_REGS_STRUCT *); + + + +#endif /* _EXEC_H_ */ diff --git a/exec/exec1.c b/exec/exec1.c new file mode 100644 index 00000000000..835bf8e72b9 --- /dev/null +++ b/exec/exec1.c @@ -0,0 +1,88 @@ +/* Program execution for Emacs. + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + +#include +#include +#include +#include + +#include "exec.h" + +/* exec1 is a program which takes another program and its arguments, + forks, and executes that program, all while tracing it and its + children to use the program execution mechanism defined in exec.c. + + This is necessary to bypass security restrictions which prohibit + Emacs from loading executables from certain directories, by, in + effect, replacing the executable loader in the Linux kernel. */ + + + +int +main (int argc, char **argv) +{ + pid_t pid, pid1; + extern char **environ; + int wstatus; + + pid = fork (); + + if (!pid) + { + tracing_execve (argv[2], argv + 2, environ); + + /* An error occured. Exit with failure. */ + exit (127); + } + else + { + /* Provide the file name of the loader. */ + exec_init (argv[1]); + + if (after_fork (pid)) + exit (127); + + /* Start waiting for the process to exit. */ + + while (true) + { + pid1 = exec_waitpid (-1, &wstatus, 0); + + /* If the child process exits normally, exit with its status + code. If not, raise the signal that caused it to + exit. */ + + if (pid == pid1) + { + if (WIFEXITED (wstatus)) + exit (WEXITSTATUS (wstatus)); + else /* if WIFSIGNALED (wstatus) */ + { + raise (WTERMSIG (wstatus)); + + /* Just in case the signal raised doesn't cause an + exit. */ + exit (127); + } + } + + /* Otherwise, continue looping. */ + } + } +} diff --git a/exec/install-sh b/exec/install-sh new file mode 100755 index 00000000000..e046efdf0a3 --- /dev/null +++ b/exec/install-sh @@ -0,0 +1,541 @@ +#!/usr/bin/sh +# install - install a program, script, or datafile + +scriptversion=2020-11-14.01; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +tab=' ' +nl=' +' +IFS=" $tab$nl" + +# Set DOITPROG to "echo" to test this script. + +doit=${DOITPROG-} +doit_exec=${doit:-exec} + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +# Create dirs (including intermediate dirs) using mode 755. +# This is like GNU 'install' as of coreutils 8.32 (2020). +mkdir_umask=22 + +backupsuffix= +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +is_target_a_directory=possibly + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -p pass -p to $cpprog. + -s $stripprog installed files. + -S SUFFIX attempt to back up existing files, with suffix SUFFIX. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG + +By default, rm is invoked with -f; when overridden with RMPROG, +it's up to you to specify -f if you want it. + +If -S is not specified, no backups are attempted. + +Email bug reports to bug-automake@gnu.org. +Automake home page: https://www.gnu.org/software/automake/ +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -p) cpprog="$cpprog -p";; + + -s) stripcmd=$stripprog;; + + -S) backupsuffix="$2" + shift;; + + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) is_target_a_directory=never;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + # Don't chown directories that already exist. + if test $dstdir_status = 0; then + chowncmd="" + fi + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename. + if test -d "$dst"; then + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dstbase=`basename "$src"` + case $dst in + */) dst=$dst$dstbase;; + *) dst=$dst/$dstbase;; + esac + dstdir_status=0 + else + dstdir=`dirname "$dst"` + test -d "$dstdir" + dstdir_status=$? + fi + fi + + case $dstdir in + */) dstdirslash=$dstdir;; + *) dstdirslash=$dstdir/;; + esac + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + # The $RANDOM variable is not portable (e.g., dash). Use it + # here however when possible just to lower collision chance. + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + + trap ' + ret=$? + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null + exit $ret + ' 0 + + # Because "mkdir -p" follows existing symlinks and we likely work + # directly in world-writeable /tmp, make sure that the '$tmpdir' + # directory is successfully created first before we actually test + # 'mkdir -p'. + if (umask $mkdir_umask && + $mkdirprog $mkdir_mode "$tmpdir" && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + test_tmpdir="$tmpdir/a" + ls_ld_tmpdir=`ls -ld "$test_tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null + fi + trap '' 0;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + oIFS=$IFS + IFS=/ + set -f + set fnord $dstdir + shift + set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=${dstdirslash}_inst.$$_ + rmtmp=${dstdirslash}_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && + { test -z "$stripcmd" || { + # Create $dsttmp read-write so that cp doesn't create it read-only, + # which would cause strip to fail. + if test -z "$doit"; then + : >"$dsttmp" # No need to fork-exec 'touch'. + else + $doit touch "$dsttmp" + fi + } + } && + $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + set +f && + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # If $backupsuffix is set, and the file being installed + # already exists, attempt a backup. Don't worry if it fails, + # e.g., if mv doesn't support -f. + if test -n "$backupsuffix" && test -f "$dst"; then + $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null + fi + + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/exec/loader-aarch64.s b/exec/loader-aarch64.s new file mode 100644 index 00000000000..1b99d238b92 --- /dev/null +++ b/exec/loader-aarch64.s @@ -0,0 +1,174 @@ +// Copyright (C) 2023 Free Software Foundation, Inc. +// +// This file is part of GNU Emacs. +// +// GNU Emacs 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 3 of the License, +// or (at your option) any later version. +// +// GNU Emacs 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 GNU Emacs. If not, see . + +// Notice that aarch64 requires that sp be aligned to 16 bytes while +// accessing memory from sp, so x20 is used to chase down the load +// area. + + .section .text + .global _start +_start: + //mov x8, 101 // SYS_nanosleep + //adr x0, timespec // req + //mov x1, #0 // rem + //svc #0 // syscall + mov x20, sp // x20 = sp + ldr x10, [x20] // x10 = original SP + add x20, x20, #16 // x20 = start of load area + mov x28, #-1 // x28 = secondary fd +.next_action: + ldr x11, [x20] // action number + and x12, x11, #-17 // actual action number + cbz x12, .open_file // open file? + cmp x12, #3 // jump? + beq .rest_of_exec + cmp x12, #4 // anonymous mmap? + beq .do_mmap_anon +.do_mmap: + ldr x0, [x20, 8] // vm_address + ldr x1, [x20, 32] // length + ldr x2, [x20, 24] // protection + ldr x3, [x20, 40] // flags + tst x11, #16 // primary fd? + mov x4, x29 // primary fd + beq .do_mmap_1 + mov x4, x28 // secondary fd +.do_mmap_1: + mov x8, #222 // SYS_mmap + ldr x5, [x20, 16] // file_offset + svc #0 // syscall + ldr x9, [x20, 8] // length + cmp x0, x9 // mmap result + bne .perror // print error + ldr x3, [x20, 48] // clear + add x1, x1, x0 // x1 = vm_address + end + sub x3, x1, x3 // x3 = x1 - clear + mov x0, #0 // x0 = 0 +.fill64: + sub x2, x1, x3 // x2 = x1 - x3 + cmp x2, #63 // x2 >= 64? + ble .fillb // start filling bytes + stp x0, x0, [x3] // x3[0] = 0, x3[1] = 0 + stp x0, x0, [x3, 16] // x3[2] = 0, x3[3] = 0 + stp x0, x0, [x3, 32] // x3[4] = 0, x3[5] = 0 + stp x0, x0, [x3, 48] // x3[6] = 0, x3[7] = 0 + add x3, x3, #64 // x3 += 8 + b .fill64 +.fillb: + cmp x1, x3 // x1 == x3? + beq .continue // done + strb w0, [x3], #1 // ((char *) x3)++ = 0 + b .fillb +.continue: + add x20, x20, #56 // next action + b .next_action +.do_mmap_anon: + ldr x0, [x20, 8] // vm_address + ldr x1, [x20, 32] // length + ldr x2, [x20, 24] // protection + ldr x3, [x20, 40] // flags + mov x4, #-1 // fd + b .do_mmap_1 +.open_file: + mov x8, #56 // SYS_openat + mov x0, #-100 // AT_FDCWD + add x1, x20, #8 // file name + mov x2, #0 // O_RDONLY + mov x3, #0 // mode + svc #0 // syscall + cmp x0, #-1 // rc < 0? + ble .perror +.nextc: + ldrb w2, [x1], #1 // b = *x1++ + cbnz w2, .nextc // b? + add x1, x1, #7 // round up x1 + and x20, x1, #-8 // mask for round, set x20 + tst x11, #16 // primary fd? + bne .secondary // secondary fd + mov x29, x0 // primary fd + b .next_action // next action +.secondary: + mov x28, x0 // secondary fd + b .next_action // next action. +.perror: + mov x8, #93 // SYS_exit + mvn x0, x0 // x1 = ~x0 + add x0, x0, 1 // x1 += 1 + svc #0 // exit +.rest_of_exec: + mov x7, x20 // x7 = x20 + mov x20, x10 // x20 = x10 + ldr x9, [x20] // argc + add x9, x9, #2 // x9 += 2 + lsl x9, x9, #3 // argc * 8 + add x20, x20, x9 // now past argv +.skipenv: + ldr x9, [x20], #8 // x9 = *envp++ + cbnz x9, .skipenv // x9? +.one_auxv: + ldr x9, [x20], #16 // x9 = *sp, sp += 2 + cbz x9, .cleanup // !x9? + cmp x9, #3 // is AT_PHDR? + beq .replace_phdr // replace + cmp x9, #4 // is AT_PHENT? + beq .replace_phent // replace + cmp x9, #5 // is AT_PHNUM? + beq .replace_phnum // replace + cmp x9, #9 // is AT_ENTRY? + beq .replace_entry // replace + cmp x9, #7 // is AT_BASE? + beq .replace_base // replace + b .one_auxv // next auxv +.replace_phdr: + ldr x9, [x7, 40] // at_phdr + str x9, [x20, -8] // store value + b .one_auxv +.replace_phent: + ldr x9, [x7, 24] // at_phent + str x9, [x20, -8] // store value + b .one_auxv +.replace_phnum: + ldr x9, [x7, 32] // at_phnum + str x9, [x20, -8] // store value + b .one_auxv +.replace_entry: + ldr x9, [x7, 16] // at_entry + str x9, [x20, -8] // store value + b .one_auxv +.replace_base: + ldr x9, [x7, 48] // at_base + str x9, [x20, -8] // store value + b .one_auxv +.cleanup: + cmp x28, #-1 // is secondary fd set? + bne .cleanup1 // not set + mov x8, #57 // SYS_close + mov x0, x28 // secondary fd + svc #0 // syscall +.cleanup1: + mov x8, #57 // SYS_close + mov x0, x29 // primary fd + svc #0 // syscall +.enter: + mov sp, x10 // restore original SP + mov x0, #0 // clear rtld_fini + ldr x1, [x7, 8] // branch to code + br x1 + +timespec: + .quad 10 + .quad 10 diff --git a/exec/loader-armeabi.s b/exec/loader-armeabi.s new file mode 100644 index 00000000000..182ff11ec7a --- /dev/null +++ b/exec/loader-armeabi.s @@ -0,0 +1,192 @@ +@ Copyright (C) 2023 Free Software Foundation, Inc. +@ +@ This file is part of GNU Emacs. +@ +@ GNU Emacs 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 3 of the License, +@ or (at your option) any later version. +@ +@ GNU Emacs 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 GNU Emacs. If not, see . + + .section .text + .global _start +_start: + @mov r7, #162 @ SYS_nanosleep + @adr r0, timespec @ req + @mov r1, #0 @ rem + @swi #0 @ syscall + mov r8, sp @ r8 = sp + ldr r9, [r8], #8 @ r9 = original sp, r8 += 8 + mov r14, #-1 @ r14 = secondary fd +.next_action: + ldr r11, [r8] @ r11 = action number + and r12, r11, #-17 @ actual action number + cmp r12, #0 @ open file? + beq .open_file @ open file. + cmp r12, #3 @ jump? + beq .rest_of_exec @ jump to code. + cmp r12, #4 @ anonymous mmap? + beq .do_mmap_anon @ anonymous mmap. +.do_mmap: + add r6, r8, #4 @ r6 = r8 + 4 + ldm r6!, {r0, r5} @ vm_address, file_offset + ldm r6!, {r1, r2} @ protection, length + mov r3, r1 @ swap + lsr r5, #12 @ divide file offset by page size + mov r1, r2 @ swap + mov r2, r3 @ swap + ldm r6!, {r3, r12} @ flags, clear + tst r11, #16 @ primary fd? + mov r4, r10 @ primary fd + beq .do_mmap_1 + mov r4, r14 @ secondary fd +.do_mmap_1: + mov r7, #192 @ SYS_mmap2 + swi #0 @ syscall + ldr r2, [r8, #4] @ vm_address + cmp r2, r0 @ rc == vm_address? + bne .perror + add r0, r1, r2 @ r0 = length + vm_address + sub r3, r0, r12 @ r3 = r0 - clear + mov r1, #0 @ r1 = 0 +.align: + cmp r0, r3 @ r0 == r3? + beq .continue @ continue + tst r3, #3 @ r3 & 3? + bne .fill32 @ fill aligned + strb r1, [r3], #1 @ fill byte + b .align @ align again +.fill32: + sub r2, r0, r3 @ r2 = r0 - r3 + cmp r2, #31 @ r2 >= 32? + ble .fillb @ start filling bytes + str r1, [r3], #4 @ *r3++ = 0 + str r1, [r3], #4 @ *r3++ = 0 + str r1, [r3], #4 @ *r3++ = 0 + str r1, [r3], #4 @ *r3++ = 0 + str r1, [r3], #4 @ *r3++ = 0 + str r1, [r3], #4 @ *r3++ = 0 + str r1, [r3], #4 @ *r3++ = 0 + str r1, [r3], #4 @ *r3++ = 0 + b .fill32 +.fillb: + cmp r0, r3 @ r0 == r3 + beq .continue @ done + strb r1, [r3], #1 @ ((char *) r3)++ = 0 + b .fillb +.continue: + add r8, r8, #28 @ next action + b .next_action +.do_mmap_anon: + add r6, r8, #4 @ r6 = r8 + 4 + ldm r6!, {r0, r5} @ vm_address, file_offset + ldm r6!, {r1, r2} @ protection, length + mov r3, r1 @ swap + lsr r5, #12 @ divide file offset by page size + mov r1, r2 @ swap + mov r2, r3 @ swap + ldm r6!, {r3, r12} @ flags, clear + mov r4, #-1 @ fd + b .do_mmap_1 +.open_file: + mov r7, #5 @ SYS_open + add r0, r8, #4 @ file name + mov r1, #0 @ O_RDONLY + mov r2, #0 @ mode + swi #0 @ syscall + cmp r0, #-1 @ r0 <= -1? + ble .perror + add r8, r8, #4 @ r8 = start of string +.nextc: + ldrb r1, [r8], #1 @ b = *r0++ + cmp r1, #0 @ b? + bne .nextc @ next character + add r8, r8, #3 @ round up r8 + and r8, r8, #-4 @ mask for round, set r8 + tst r11, #16 @ primary fd? + bne .secondary @ secondary fd + mov r10, r0 @ primary fd + b .next_action @ next action +.secondary: + mov r14, r0 @ secondary fd + b .next_action @ next action +.perror: + mov r7, #1 @ SYS_exit + mvn r0, r0 @ r0 = ~r0 + add r0, r0, #1 @ r0 += 1 + swi #0 +.rest_of_exec: + mov r7, r9 @ r7 = original SP + ldr r6, [r7] @ argc + add r6, r6, #2 @ argc + 2 + lsl r6, r6, #2 @ argc *= 4 + add r7, r7, r6 @ now past argv +.skipenv: + ldr r6, [r7], #4 @ r6 = *r7++ + cmp r6, #0 @ r6? + bne .skipenv @ r6? +.one_auxv: + ldr r6, [r7], #8 @ r6 = *r7, r7 += 2 + cmp r6, #0 @ !r6? + beq .cleanup @ r6? + cmp r6, #3 @ is AT_PHDR? + beq .replace_phdr @ replace + cmp r6, #4 @ is AT_PHENT? + beq .replace_phent @ replace + cmp r6, #5 @ is AT_PHNUM? + beq .replace_phnum @ replace + cmp r6, #9 @ is AT_ENTRY? + beq .replace_entry @ replace + cmp r6, #7 @ is AT_BASE? + beq .replace_base @ replace + b .one_auxv @ next auxv +.replace_phdr: + ldr r6, [r8, #20] @ at_phdr + str r6, [r7, #-4] @ store value + b .one_auxv +.replace_phent: + ldr r6, [r8, #12] @ at_phent + str r6, [r7, #-4] @ store value + b .one_auxv +.replace_phnum: + ldr r6, [r8, #16] @ at_phnum + str r6, [r7, #-4] @ store value + b .one_auxv +.replace_entry: + ldr r6, [r8, #8] @ at_entry + str r6, [r7, #-4] @ store value + b .one_auxv +.replace_base: + ldr r6, [r8, #24] @ at_base + str r6, [r7, #-4] @ store value + b .one_auxv +.cleanup: + cmp r14, #-1 @ secondary fd set? + bne .cleanup1 @ not set + mov r7, #6 @ SYS_close + mov r0, r14 @ secondary fd + swi #0 @ syscall +.cleanup1: + mov r7, #6 @ SYS_close + mov r0, r10 @ primary fd + swi #0 @ syscall +.enter: + mov sp, r9 @ restore original SP + mov r0, #0 @ clear rtld_fini + ldr r1, [r8, #4] @ branch to code + bx r1 + +timespec: + .long 10 + .long 10 + +@ Local Variables: +@ asm-comment-char: 64 +@ End: diff --git a/exec/loader-mips64el.s b/exec/loader-mips64el.s new file mode 100644 index 00000000000..ccebdfe72f6 --- /dev/null +++ b/exec/loader-mips64el.s @@ -0,0 +1,214 @@ +# Copyright (C) 2023 Free Software Foundation, Inc. +# +# This file is part of GNU Emacs. +# +# GNU Emacs 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 3 of the License, +# or (at your option) any later version. +# +# GNU Emacs 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 GNU Emacs. If not, see . + + .set noreorder # delay slots managed by hand + .section .text + .global __start +__start: +dnl li $v0, 5034 # SYS_nanosleep +dnl dla $a0, .timespec # rqtp +dnl li $a1, 0 # rmtp +dnl syscall # syscall + ld $s2, ($sp) # original stack pointer + daddi $s0, $sp, 16 # start of load area + daddi $sp, -16 # primary fd, secondary fd + li $t0, -1 # secondary fd + sd $t0, 8($sp) # initialize secondary fd +.next_action: + ld $s1, ($s0) # action number + andi $t0, $s1, 15 # t0 = action number & 15 + beqz $t0, .open_file # open file? + nop # delay slot + daddi $t0, -3 # t0 -= 3 + beqz $t0, .rest_of_exec # jump to code + nop # delay slot + li $t1, 1 + beq $t0, $t1, .do_mmap_anon # anonymous mmap? + nop # delay slot +.do_mmap: + ld $t0, 8($s0) # vm address + ld $t1, 16($s0) # file_offset + ld $t2, 24($s0) # protection + ld $t3, 32($s0) # length + ld $v0, 40($s0) # flags + ld $v1, ($sp) # primary fd + andi $s3, $s1, 16 # s1 & 16? + beqz $s3, .do_mmap_1 # secondary fd? + nop # delay slot + ld $v1, 8($sp) # secondary fd +.do_mmap_1: + move $a0, $t0 # syscall arg + move $a1, $t3 # syscall arg + move $a2, $t2 # syscall arg + move $a3, $v0 # syscall arg + move $a4, $v1 # syscall arg + move $a5, $t1 # syscall arg + li $v0, 5009 # SYS_mmap + syscall # syscall + bne $a3, $zero, .perror # perror? + nop # delay slot + ld $t1, 48($s0) # clear + dadd $t0, $a0, $a1 # t0 = end of mapping + dsub $t1, $t0, $t1 # t1 = t0 - clear +.align: + beq $t0, $t1, .continue # already finished + nop # delay slot + andi $t2, $t1, 7 # t1 & 7? + bnez $t2, .filld # start filling longs + nop # delay slot +.filld: + dsub $t2, $t0, $t1 # t2 = t0 - t1 + sltiu $t2, $t2, 64 # t2 < 64? + bne $t2, $zero, .fillb # fill bytes + nop # delay slot + sd $zero, ($t1) # zero doubleword + daddi $t1, 8 # next doubleword + sd $zero, ($t1) # zero doubleword + daddi $t1, 8 # next doubleword + sd $zero, ($t1) # zero doubleword + daddi $t1, 8 # next doubleword + sd $zero, ($t1) # zero doubleword + daddi $t1, 8 # next doubleword + sd $zero, ($t1) # zero doubleword + daddi $t1, 8 # next doubleword + sd $zero, ($t1) # zero doubleword + daddi $t1, 8 # next doubleword + sd $zero, ($t1) # zero doubleword + daddi $t1, 8 # next doubleword + sd $zero, ($t1) # zero doubleword + daddi $t1, 8 # next doubleword + j .filld # fill either doubleword or byte + nop # delay slot +.fillb: + beq $t0, $t1, .continue # already finished? + nop # delay slot + sb $zero, ($t1) # clear byte + daddi $t1, $t1, 1 # t1++ +.continue: + daddi $s0, $s0, 56 # s0 = next action + j .next_action # next action + nop # delay slot +.do_mmap_anon: + ld $t0, 8($s0) # vm address + ld $t1, 16($s0) # file_offset + ld $t2, 24($s0) # protection + ld $t3, 32($s0) # length + ld $v0, 40($s0) # flags + li $v1, -1 # fd + j .do_mmap_1 # do mmap + nop # branch delay slot +.open_file: + li $v0, 5002 # SYS_open + daddi $a0, $s0, 8 # start of name + move $a1, $zero # flags = O_RDONLY + move $a2, $zero # mode = 0 + syscall # syscall + bne $a3, $zero, .perror # perror + nop # delay slot + daddi $s0, $s0, 8 # start of string +.nextc: + lb $t0, ($s0) # load byte + daddi $s0, $s0, 1 # s0++ + bne $t0, $zero, .nextc # next character? + nop # delay slot + daddi $s0, $s0, 7 # adjust for round + li $t2, -8 # t2 = -8 + and $s0, $s0, $t2 # mask for round + andi $t0, $s1, 16 # t1 = s1 & 16 + move $t1, $sp # address of primary fd + beqz $t0, .primary # primary fd? + nop # delay slot + daddi $t1, $t1, 8 # address of secondary fd +.primary: + sd $v0, ($t1) # store fd + j .next_action # next action + nop # delay slot +.perror: + move $a0, $v0 # errno + li $v0, 5058 # SYS_exit + syscall # syscall +.rest_of_exec: + move $s1, $s2 # original SP + ld $t0, ($s1) # argc + dsll $t0, $t0, 3 # argc *= 3 + daddi $t0, $t0, 16 # argc += 16 + dadd $s1, $s1, $t0 # s1 = start of envp +.skipenv: + ld $t0, ($s1) # t0 = *s1 + daddi $s1, $s1, 8 # s1++ + bne $t0, $zero, .skipenv # skip again + nop # delay slot + dla $t3, .auxvtab # address of auxv table +.one_auxv: + ld $t0, ($s1) # t0 = auxv type + li $t1, 10 # t1 = 10 + beqz $t0, .finish # is AT_IGNORE? + nop # delay slot + sltu $t1, $t0, $t1 # t1 = t0 < num offsets + beqz $t1, .next # next auxv + nop # delay slot + dsll $t1, $t0, 2 # t1 = t0 * 4 + dadd $t1, $t3, $t1 # t1 = .auxvtab + t1 + lw $t2, ($t1) # t2 = *t1 + beqz $t2, .next # skip auxv + nop # delay slot + dadd $t2, $s0, $t2 # t2 = s0 + t2 + ld $t2, ($t2) # t2 = *t2 + sd $t2, 8($s1) # set auxv value +.next: + daddi $s1, $s1, 16 # next auxv + j .one_auxv # next auxv + nop # delay slot +.finish: + ld $t0, 8($sp) # secondary fd + li $t1, -1 # t1 = -1 + ld $s1, ($sp) # s1 = primary fd + li $v0, 5003 # SYS_close + beq $t0, $t2, .finish1 # secondary fd set? + nop # delay slot + move $a0, $t0 # secondary fd + syscall # syscall + li $v0, 5003 # SYS_close +.finish1: + move $a0, $s1 # primary fd + syscall # syscall +.jump: + move $v0, $zero # rtld_fini + ld $t0, 8($s0) # entry + move $sp, $s2 # restore stack pointer, delay slot + jr $t0 # enter + nop # delay slot + +.auxvtab: + .long 0 # 0 + .long 0 # 1 + .long 0 # 2 + .long 40 # 3 AT_PHDR + .long 24 # 4 AT_PHENT + .long 32 # 5 AT_PHNUM + .long 0 # 6 + .long 48 # 7 AT_BASE + .long 0 # 8 + .long 16 # 9 AT_ENTRY + +.timespec: + .quad 10 + .quad 10 + +# Local Variables: +# asm-comment-char: 35 +# End: diff --git a/exec/loader-mipsel.s b/exec/loader-mipsel.s new file mode 100644 index 00000000000..2ad9d97dfed --- /dev/null +++ b/exec/loader-mipsel.s @@ -0,0 +1,221 @@ +# Copyright (C) 2023 Free Software Foundation, Inc. +# +# This file is part of GNU Emacs. +# +# GNU Emacs 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 3 of the License, +# or (at your option) any later version. +# +# GNU Emacs 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 GNU Emacs. If not, see . + +include(`config-mips.m4') + +# Make sure not to use t4 through t7, in order to maintain portability +# with N32 ABI systems. + + .set noreorder # delay slots managed by hand + .section .text + .global __start +__start: +dnl li $v0, SYSCALL_nanosleep # SYS_nanosleep +dnl la $a0, .timespec # rqtp +dnl li $a1, 0 # rmtp +dnl syscall # syscall + lw $s6, ($sp) # original stack pointer + addi $s0, $sp, 8 # start of load area + addi $sp, -8 # primary fd, secondary fd + li $t0, -1 # secondary fd + sw $t0, 4($sp) # initialize secondary fd +.next_action: + lw $s2, ($s0) # action number + nop # delay slot + andi $t0, $s2, 15 # t0 = s2 & 15 + beqz $t0, .open_file # open file? + li $t1, 3 # t1 = 3, delay slot + beq $t0, $t1, .rest_of_exec # jump to code + li $t1, 4 # t1 = 4, delay slot + beq $t0, $t1, .do_mmap_anon # anonymous mmap +.do_mmap: + lw $a0, 4($s0) # vm_address, delay slot + lw $v1, 8($s0) # file_offset + lw $a2, 12($s0) # protection + lw $a1, 16($s0) # length + lw $a3, 20($s0) # flags + lw $v0, ($sp) # primary fd + andi $t1, $s2, 16 # t1 = s2 & 16 + beqz $t1, .do_mmap_1 # secondary fd? + nop # delay slot + lw $v0, 4($sp) # secondary fd + nop # delay slot +.do_mmap_1: +SYSCALL(`$v0',`$v1',`$zero',`$zero') # syscall args + li $v0, SYSCALL_mmap # SYS_mmap + syscall # syscall + bne $a3, $zero, .perror # perror +RESTORE() # delay slot, restore sp + lw $s5, 24($s0) # clear + add $t0, $a0, $a1 # t0 = length + vm_address, delay slot + sub $t1, $t0, $s5 # t1 = t0 - clear +.align: + beq $t0, $t1, .continue # already finished? + nop # delay slot + andi $t2, $t1, 3 # t1 & 3? + bnez $t2, .fillw # start filling longs + nop # delay slot + sb $zero, ($t1) # clear byte + addi $t1, $t1, 1 # t1++ + j .align # continue + nop # delay slot +.fillw: + sub $t2, $t0, $t1 # t2 = t0 - t1 + sltiu $t2, $t2, 32 # r2 < 32? + bne $t2, $zero, .fillb # fill bytes + nop # delay slot + sw $zero, ($t1) # zero word + addi $t1, $t1, 4 # next word + sw $zero, ($t1) # zero word + addi $t1, $t1, 4 # next word + sw $zero, ($t1) # zero word + addi $t1, $t1, 4 # next word + sw $zero, ($t1) # zero word + addi $t1, $t1, 4 # next word + sw $zero, ($t1) # zero word + addi $t1, $t1, 4 # next word + sw $zero, ($t1) # zero word + addi $t1, $t1, 4 # next word + sw $zero, ($t1) # zero word + addi $t1, $t1, 4 # next word + sw $zero, ($t1) # zero word + addi $t1, $t1, 4 # next word + j .fillw # fill either word or byte + nop # delay slot +.fillb: + beq $t0, $t1, .continue # already finished? + nop # delay slot + sb $zero, ($t1) # clear byte + addi $t1, $t1, 1 # t1++ +.continue: + addi $s0, $s0, 28 # s0 = next action + j .next_action # next action + nop # delay slot +.do_mmap_anon: + lw $v1, 8($s0) # file_offset + lw $a2, 12($s0) # protection + lw $a1, 16($s0) # length + lw $a3, 20($s0) # flags + li $t4, -1 # fd + j .do_mmap_1 # do mmap + nop # delay slot +.open_file: + li $v0, SYSCALL_open # SYS_open + addi $a0, $s0, 4 # start of name + move $a1, $zero # flags = O_RDONLY + move $a2, $zero # mode = 0 + syscall # syscall + bne $a3, $zero, .perror # perror + addi $s0, $s0, 4 # start of string, delay slot +.nextc: + lb $t0, ($s0) # load byte + addi $s0, $s0, 1 # s0++ + bne $t0, $zero, .nextc # next character? + nop # delay slot + addi $s0, $s0, 3 # adjust for round + li $t2, -4 # t2 = -4 + and $s0, $s0, $t2 # mask for round + andi $t0, $s2, 16 # t1 = s2 & 16 + beqz $t0, .primary # primary fd? + move $t0, $sp # address of primary fd, delay slot + addi $t0, $t0, 4 # address of secondary fd +.primary: + sw $v0, ($t0) # store fd + j .next_action # next action + nop # delay slot +.perror: + move $a0, $v0 # errno + li $v0, SYSCALL_exit # SYS_exit + syscall # syscall +.rest_of_exec: + move $s1, $s6 # s1 = original SP + lw $t0, ($s1) # argc + nop # delay slot + sll $t0, $t0, 2 # argc *= 4 + addi $t0, $t0, 8 # argc += 8 + add $s1, $s1, $t0 # s1 = start of envp +.skipenv: + lw $t0, ($s1) # t0 = *s1 + addi $s1, $s1, 4 # s1++ + bne $t0, $zero, .skipenv # skip again + nop # delay slot + la $s2, .auxvtab # address of auxv table +.one_auxv: + lw $t0, ($s1) # t0 = auxv type + li $t1, 10 # t1 = 10, delay slot + beqz $t0, .finish # is AT_IGNORE? + sltu $t1, $t0, $t1 # t1 = t0 < num offsets, delay slot + beq $t1, $zero, .next # next auxv + sll $t1, $t0, 2 # t1 = t0 * 4, delay slot + add $t1, $s2, $t1 # t1 = .auxvtab + t1 + lw $t2, ($t1) # t2 = *t1 + nop # delay slot + beqz $t2, .next # skip auxv + add $t2, $s0, $t2 # t2 = s0 + t2 + lw $t2, ($t2) # t2 = *t2 + nop # delay slot + sw $t2, 4($s1) # set auxv value +.next: + addi $s1, $s1, 8 # next auxv + j .one_auxv # next auxv + nop # delay slot +.finish: + lw $t0, 4($sp) # secondary fd + lw $s1, ($sp) # primary fd, delay slot, preserved + li $t2, -1 # immediate -1 + beq $t0, $t2, .finish1 # secondary fd set? + li $v0, SYSCALL_close # SYS_close, delay slot + move $a0, $t0 # fd + syscall # syscall + li $v0, SYSCALL_close # SYS_close +.finish1: + move $a0, $s1 # primary fd + syscall # syscall + li $v0, SYSCALL_prctl # SYS_prctl + li $a0, 45 # PR_SET_FP_MODE + lw $a1, 28($s0) # fpu_mode + move $a2, $zero # arg3 + move $a3, $zero # arg4 +SYSCALL(`$a2',`$a2',`$a2',`$a2') # syscall args + syscall # syscall +RESTORE() # restore sp +.jump: + move $v0, $zero # rtld_fini + lw $t0, 4($s0) # entry + move $sp, $s6 # restore stack pointer, delay slot + jr $t0 # enter + nop # delay slot + +.auxvtab: + .long 0 # 0 + .long 0 # 1 + .long 0 # 2 + .long 20 # 3 AT_PHDR + .long 12 # 4 AT_PHENT + .long 16 # 5 AT_PHNUM + .long 0 # 6 + .long 24 # 7 AT_BASE + .long 0 # 8 + .long 8 # 9 AT_ENTRY + +.timespec: + .long 10 + .long 10 + +# Local Variables: +# asm-comment-char: 35 +# End: diff --git a/exec/loader-x86.s b/exec/loader-x86.s new file mode 100644 index 00000000000..ee69b26d78b --- /dev/null +++ b/exec/loader-x86.s @@ -0,0 +1,188 @@ +define(`CC', ` +dnl') + +CC Copyright (C) 2023 Free Software Foundation, Inc. +CC +CC This file is part of GNU Emacs. +CC +CC GNU Emacs is free software: you can redistribute it and/or modify +CC it under the terms of the GNU General Public License as published +CC by the Free Software Foundation, either version 3 of the License, +CC or (at your option) any later version. +CC +CC GNU Emacs is distributed in the hope that it will be useful, but +CC WITHOUT ANY WARRANTY; without even the implied warranty of +CC MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +CC General Public License for more details. +CC +CC You should have received a copy of the GNU General Public License +CC along with GNU Emacs. If not, see . + + .section .text + .global _start +_start: + #movl $162, %eax CC SYS_nanosleep + #leal timespec, %ebx + #xorl %ecx, %ecx + #int $0x80 + leal 8(%esp), %ebp CC ebp = start of load area + subl $8, %esp CC (%esp) = primary fd, 4(%esp) = secondary fd + movl $-1, 4(%esp) +.next_action: + movl (%ebp), %edx CC edx = action number + andl $-17, %edx + cmpl $0, %edx CC open file? + je .open_file + cmpl $3, %edx CC jump? + je .rest_of_exec + cmpl $4, %edx CC anonymous mmap? + je .do_mmap_anon +.do_mmap: + subl $24, %esp + movl $90, %eax CC SYS_old_mmap + movl %esp, %ebx + movl 4(%ebp), %ecx CC address + movl %ecx, (%esp) + movl 16(%ebp), %ecx CC length + movl %ecx, 4(%esp) + movl 12(%ebp), %ecx CC protection + movl %ecx, 8(%esp) + movl 20(%ebp), %ecx CC flags + movl %ecx, 12(%esp) + testl $16, (%ebp) CC primary? + movl 28(%esp), %ecx + cmovzl 24(%esp), %ecx + movl %ecx, 16(%esp) CC fd + movl 8(%ebp), %ecx CC offset + movl %ecx, 20(%esp) +.do_mmap_1: + int $0x80 + addl $24, %esp CC restore esp + cmpl $-1, %eax CC mmap failed? + je .perror + movl 24(%ebp), %ecx CC clear + testl %ecx, %ecx + jz .continue + movl 4(%ebp), %esi CC start of mapping + addl 16(%ebp), %esi CC end of mapping + subl %ecx, %esi CC start of clear area +.again: + testl %ecx, %ecx + jz .continue + subl $1, %ecx + movb $0, (%esi, %ecx, 1) + jmp .again +.continue: + leal 28(%ebp), %ebp + jmp .next_action +.do_mmap_anon: + subl $24, %esp + movl $90, %eax CC SYS_old_mmap + movl %esp, %ebx + movl 4(%ebp), %ecx CC address + movl %ecx, (%esp) + movl 16(%ebp), %ecx CC length + movl %ecx, 4(%esp) + movl 12(%ebp), %ecx CC protection + movl %ecx, 8(%esp) + movl 20(%ebp), %ecx CC flags + movl %ecx, 12(%esp) + movl $-1, 16(%esp) CC fd + movl 8(%ebp), %ecx CC offset + movl %ecx, 20(%esp) + jmp .do_mmap_1 +.open_file: + movl $5, %eax CC SYS_open + leal 4(%ebp), %ebx CC ebx = %esp + 8 + pushl %ebx + xorl %ecx, %ecx CC flags = O_RDONLY + xorl %edx, %edx CC mode = 0 + int $0x80 + cmpl $-1, %eax CC open failed? + jle .perror + movl %ebp, %esi CC (esi) = original action number + popl %ebp CC ebp = start of string + decl %ebp +.nextc: + incl %ebp + cmpb $0, (%ebp) CC *ebp == 0? + jne .nextc + addl $4, %ebp CC adjust past ebp prior to rounding + andl $-4, %ebp CC round ebp up to the next long + testl $16, (%esi) CC original action number & 16? + jz .primary + movl %eax, 4(%esp) CC secondary fd = eax + jmp .next_action +.primary: + movl %eax, (%esp) CC primary fd = eax + jmp .next_action +.perror: + movl %eax, %ebx + negl %ebx + movl $1, %eax + int $0x80 +.rest_of_exec: + movl 8(%esp), %ecx CC ecx = original stack pointer + movl (%ecx), %esi CC esi = argc + leal 8(%ecx, %esi, 4), %ecx CC ecx = start of environ +.skip_environ: + movl (%ecx), %esi CC envp[N] + subl $4, %ecx + testl %esi, %esi CC envp[n] ? + jnz .skip_environ CC otherwise, esi is now at the start of auxv +.one_auxv: + movl (%ecx), %esi CC auxv type + leal 8(%ecx), %ecx CC skip to next auxv + testl %esi, %esi CC is 0? + jz .cleanup + cmpl $3, %esi CC is AT_PHDR + je .replace_phdr + cmpl $4, %esi CC is AT_PHENT? + je .replace_phent + cmpl $5, %esi CC is AT_PHNUM? + je .replace_phnum + cmpl $9, %esi CC is AT_ENTRY? + je .replace_entry + cmpl $7, %esi CC is AT_BASE + je .replace_base + jmp .one_auxv +.replace_phdr: + movl 20(%ebp), %esi + movl %esi, -4(%ecx) + jmp .one_auxv +.replace_phent: + movl 12(%ebp), %esi + movl %esi, -4(%ecx) + jmp .one_auxv +.replace_phnum: + movl 16(%ebp), %esi + movl %esi, -4(%ecx) + jmp .one_auxv +.replace_entry: + movl 8(%ebp), %esi + movl %esi, -4(%ecx) + jmp .one_auxv +.replace_base: + movl 24(%ebp), %esi + movl %esi, -4(%ecx) + jmp .one_auxv +.cleanup: + movl $6, %eax CC SYS_close + cmpl $1, -4(%esp) CC see if interpreter fd is set + jne .cleanup_1 + movl -4(%esp), %ebx + int $0x80 +.cleanup_1: + movl $6, %eax CC SYS_close + movl (%esp), %ebx + int $0x80 +.enter: + pushl $0 + popfl CC restore floating point state + movl 8(%esp), %esp CC restore initial stack pointer + xorl %edx, %edx CC clear rtld_fini + jmpl *4(%ebp) CC entry + +timespec: + .long 10 + .long 10 diff --git a/exec/loader-x86_64.s b/exec/loader-x86_64.s new file mode 100644 index 00000000000..07227d38396 --- /dev/null +++ b/exec/loader-x86_64.s @@ -0,0 +1,180 @@ +define(`CC', ` +dnl') + +CC Copyright (C) 2023 Free Software Foundation, Inc. +CC +CC This file is part of GNU Emacs. +CC +CC GNU Emacs is free software: you can redistribute it and/or modify +CC it under the terms of the GNU General Public License as published +CC by the Free Software Foundation, either version 3 of the License, +CC or (at your option) any later version. +CC +CC GNU Emacs is distributed in the hope that it will be useful, but +CC WITHOUT ANY WARRANTY; without even the implied warranty of +CC MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +CC General Public License for more details. +CC +CC You should have received a copy of the GNU General Public License +CC along with GNU Emacs. If not, see . + + .section .text + .global _start +_start: + #movq $35, %rax CC SYS_nanosleep + #leaq timespec(%rip), %rdi + #xorq %rsi, %rsi + #syscall + popq %r13 CC original SP + popq %r15 CC size of load area. + movq $-1, %r12 CC r12 is the interpreter fd +.next_action: + movq (%rsp), %r14 CC action number + movq %r14, %r15 CC original action number + andq $-17, %r14 + cmpq $0, %r14 CC open file? + je .open_file + cmpq $3, %r14 CC jump? + je .rest_of_exec + cmpq $4, %r14 CC anonymous mmap? + je .do_mmap_anon +.do_mmap: + movq $9, %rax CC SYS_mmap + movq 8(%rsp), %rdi CC address + movq 16(%rsp), %r9 CC offset + movq 24(%rsp), %rdx CC protection + movq 32(%rsp), %rsi CC length + movq 40(%rsp), %r10 CC flags + CC set r8 to the primary fd unless r15 & 16 + testq $16, %r15 + movq %r12, %r8 + cmovzq %rbx, %r8 +.do_mmap_1: + syscall + cmpq $-1, %rax CC mmap failed + je .perror + movq 48(%rsp), %r9 CC clear + testq %r9, %r9 + jz .continue + movq 8(%rsp), %r10 CC start of mapping + addq 32(%rsp), %r10 CC end of mapping + subq %r9, %r10 CC start of clear area +.again: + testq %r9, %r9 + jz .continue + subq $1, %r9 + movb $0, (%r10, %r9, 1) + jmp .again +.continue: + leaq 56(%rsp), %rsp + jmp .next_action +.do_mmap_anon: + movq $9, %rax CC SYS_mmap + movq 8(%rsp), %rdi CC address + movq 16(%rsp), %r9 CC offset + movq 24(%rsp), %rdx CC protection + movq 32(%rsp), %rsi CC length + movq 40(%rsp), %r10 CC flags + movq $-1, %r8 CC -1 + jmp .do_mmap_1 +.open_file: + movq $2, %rax CC SYS_open + leaq 8(%rsp), %rdi CC rdi = %rsp + 8 + xorq %rsi, %rsi CC flags = O_RDONLY + xorq %rdx, %rdx CC mode = 0 + syscall + cmpq $-1, %rax CC open failed + jle .perror + movq %rdi, %rsp CC rsp = start of string + subq $1, %rsp +.nextc: + addq $1, %rsp + cmpb $0, (%rsp) CC *rsp == 0? + jne .nextc + addq $8, %rsp CC adjust past rsp prior to rounding + andq $-8, %rsp CC round rsp up to the next quad + testq $16, %r15 CC r15 & 16? + jz .primary + movq %rax, %r12 CC otherwise, move fd to r12 + jmp .next_action +.primary: + movq %rax, %rbx CC if not, move fd to rbx + jmp .next_action +.perror: + movq %rax, %r12 CC error code + negq %r12 + movq $1, %rax CC SYS_write + movq $1, %rdi CC stdout + leaq error(%rip), %rsi CC buffer + movq $23, %rdx CC count + syscall + movq $60, %rax CC SYS_exit + movq %r12, %rdi CC code + syscall +.rest_of_exec: CC rsp now points to six quads: + movq %rsp, %r8 CC now, they are r8 + movq %r13, %rsp CC restore SP + popq %r10 CC argc + leaq 8(%rsp,%r10,8), %rsp CC now at start of environ +.skip_environ: + popq %r10 CC envp[N] + testq %r10, %r10 CC envp[n]? + jnz .skip_environ CC otherwise, rsp is now at the start of auxv +.one_auxv: + popq %rcx CC auxv type + addq $8, %rsp CC skip value + testq %rcx, %rcx CC is 0? + jz .cleanup + cmpq $3, %rcx CC is AT_PHDR? + je .replace_phdr + cmpq $4, %rcx CC is AT_PHENT? + je .replace_phent + cmpq $5, %rcx CC is AT_PHNUM? + je .replace_phnum + cmpq $9, %rcx CC is AT_ENTRY? + je .replace_entry + cmpq $7, %rcx CC is AT_BASE? + je .replace_base + jmp .one_auxv +.replace_phdr: + movq 40(%r8), %r9 + movq %r9, -8(%rsp) CC set at_phdr + jmp .one_auxv +.replace_phent: + movq 24(%r8), %r9 + movq %r9, -8(%rsp) CC set at_phent + jmp .one_auxv +.replace_phnum: + movq 32(%r8), %r9 + movq %r9, -8(%rsp) CC set at_phnum + jmp .one_auxv +.replace_entry: + movq 16(%r8), %r9 + movq %r9, -8(%rsp) CC set at_entry + jmp .one_auxv +.replace_base: + movq 48(%r8), %r9 + movq %r9, -8(%rsp) CC set at_base + jmp .one_auxv +.cleanup: + movq $3, %rax CC SYS_close + cmpq $-1, %r12 CC see if interpreter fd is set + jne .cleanup_1 + movq %r12, %rdi + syscall +.cleanup_1: + movq $3, %rax CC SYS_close + movq %rbx, %rdi + syscall +.enter: + pushq $0 + popfq CC clear FP state + movq %r13, %rsp CC restore SP + xorq %rdx, %rdx CC clear rtld_fini + jmpq *8(%r8) CC entry + +error: + .ascii "_start: internal error." +timespec: + .quad 10 + .quad 10 diff --git a/exec/mipsel-user.h b/exec/mipsel-user.h new file mode 100644 index 00000000000..2b77a970d8e --- /dev/null +++ b/exec/mipsel-user.h @@ -0,0 +1,44 @@ +/* Program execution for Emacs. + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + + + +#ifndef _MIPSEL_USER_H_ +#define _MIPSEL_USER_H_ + +#include +#include + +#ifndef ELF_NGREG +#define ELF_NGREG 45 +#endif /* ELF_NGREG */ + + + +/* This file defines a structure containing user mode general purpose + registers on 32-bit mipsel systems. */ + +struct mipsel_regs +{ + /* General purpose registers. */ + uint64_t gregs[ELF_NGREG]; +}; + +#endif /* _MIPSEL_USER_H_ */ + diff --git a/exec/mipsfpu.c b/exec/mipsfpu.c new file mode 100644 index 00000000000..f5fa5720804 --- /dev/null +++ b/exec/mipsfpu.c @@ -0,0 +1,289 @@ +/* Program execution for Emacs. + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + +#include +#include + +#include "mipsfpu.h" + + + +/* OABI MIPS systems support several different modes of execution. + Each mode differs in the size and utilization of the hardware + floating-point registers. + + Linux normally sets the floating point mode to one appropriate for + execution, taking into account the floating point modes of the + interpreter and executable binaries. However, this logic is + forsaken when the `execve' system call is overwritten. + + Thus, the correct floating point mode must be determined and set + within the loader binary. */ + + + +/* Various constants used throughout this code. */ + +#define MIPS_ABI_FP_ANY 0 /* FP ABI doesn't matter */ +#define MIPS_ABI_FP_DOUBLE 1 /* -mdouble-float */ +#define MIPS_ABI_FP_SINGLE 2 /* -msingle-float */ +#define MIPS_ABI_FP_SOFT 3 /* -msoft-float */ +#define MIPS_ABI_FP_OLD_64 4 /* -mips32r2 -mfp64 */ +#define MIPS_ABI_FP_XX 5 /* -mfpxx */ +#define MIPS_ABI_FP_64 6 /* -mips32r2 -mfp64 */ +#define MIPS_ABI_FP_64A 7 /* -mips32r2 -mfp64 -mno-odd-spreg */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used. */ +#define EF_MIPS_PIC 2 /* Contains PIC code. */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence. */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_FP64 512 /* Uses FP64 (12 callee-saved). */ +#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */ +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level. */ + + + +/* Structure describing the requirements of a single floating-point + ABI. */ + +struct mode_description +{ + /* Whether or not the ABI only executes single precision + instructions, and can operate in both 32-bit or 64-bit floating + point mode. */ + bool single; + + /* Whether or not the ABI performs floating point operations in + software, using integer registers. */ + bool soft; + + /* Whether or not the ABI requires the use of 64-bit floating point + registers. */ + bool fr1; + + /* Whether or not the ABI requires the use of 64-bit floating point + registers on NABI systems, and 32-bit ones on OABI systems. */ + bool frdefault; + + /* Whether or not this ABI requires single precision floating point + emulation. */ + bool fre; +}; + +static struct mode_description fpu_reqs[] = + { + [MIPS_ABI_FP_ANY] = { true, true, true, true, true, }, + [MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true, }, + [MIPS_ABI_FP_SINGLE] = { true, false, false, false, false, }, + [MIPS_ABI_FP_SOFT] = { false, true, false, false, false, }, + [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false, }, + [MIPS_ABI_FP_XX] = { false, false, true, true, true, }, + [MIPS_ABI_FP_64] = { false, false, true, false, false, }, + [MIPS_ABI_FP_64A] = { false, false, true, false, true, }, + }; + + + +/* Return whether or not the given floating-point ABI is valid. */ + +static bool +valid_abi_p (int abi) +{ + switch (abi) + { + case MIPS_ABI_FP_ANY: + case MIPS_ABI_FP_DOUBLE: + case MIPS_ABI_FP_SINGLE: + case MIPS_ABI_FP_SOFT: + case MIPS_ABI_FP_OLD_64: + case MIPS_ABI_FP_XX: + case MIPS_ABI_FP_64: + case MIPS_ABI_FP_64A: + return true; + + default: + return false; + } +} + +/* Return the floating point mode appropriate for the specified + floating point ABI. */ + +static int +fp_mode_for_abi (int abi) +{ + struct mode_description *desc; + + desc = &fpu_reqs[abi]; + + if (desc->fre) + return FP_FRE; + else if (desc->fr1) + return FP_FR1; + + return FP_FR0; +} + +/* Determine whether or not the CPU is capable of operating in FR0 + floating point mode. */ + +bool +cpu_supports_fr0_p (void) +{ +#if defined __mips_isa_rev && __mips_isa_rev >= 6 + return true; +#else /* !defined __mips_isa_rev | mips_isa_rev < 6 */ + return false; +#endif /* defined __mips_isa_rev && mips_isa_rev >= 6 */ +} + +/* Determine the FPU mode for the executable whose ELF header is + HEADER. If INTERPRETER is non-NULL, also take an interpreter whose + header is INTERPRETER into account. + + ABIFLAGS should be HEADER's corresponding PT_MIPS_ABIFLAGS program + header, and ABIFLAGS1 should be that of INTERPRETER, if set. Both + fields may be NULL if no PT_MIPS_ABIFLAGS header is present; in + that case, use HEADER->e_flags to determine the ABI instead. + + Return the FPU mode in *MODE. Value is 0 upon success, 1 + otherwise, with errno set. */ + +int +determine_fpu_mode (elf_header *header, elf_header *interpreter, + int *mode, struct mips_elf_abi_flags *abiflags, + struct mips_elf_abi_flags *abiflags1) +{ + int exec_abi, interpreter_abi; + struct mode_description *exec_desc, *interpreter_desc, common; + + /* Figure out the executable's floating point ABI. First, consult + header->e_flags, and use the old 64-bit floating point ABI if it + is specified. */ + + exec_abi = MIPS_ABI_FP_ANY; + + /* First, check HEADER->e_flags. */ + + if (header->e_flags & EF_MIPS_FP64) + exec_abi = MIPS_ABI_FP_OLD_64; + + /* Next, use ABIFLAGS if it exists. */ + + if (abiflags && valid_abi_p (abiflags->fp_abi)) + exec_abi = abiflags->fp_abi; + else if (abiflags) + { + errno = ENOEXEC; + return 1; + } + + /* Now determine that of the interpreter. */ + + interpreter_abi = MIPS_ABI_FP_ANY; + + if (interpreter) + { + if (interpreter->e_flags & EF_MIPS_FP64) + interpreter_abi = MIPS_ABI_FP_OLD_64; + + if (abiflags1 && valid_abi_p (abiflags->fp_abi)) + interpreter_abi = abiflags->fp_abi; + else if (abiflags1) + { + errno = ELIBBAD; + return 1; + } + } + + /* If no interpreter flag is set, just return that of the + executable. */ + + if (!interpreter) + { + *mode = fp_mode_for_abi (exec_abi); + return 0; + } + + /* Otherwise, compare both ABIs and try to find one which will run + both kinds of code. + + First, see if there's an easy way out: both ABIs are identical, + or one ABI is MIPS_ABI_FP_ANY. */ + + if (exec_abi == interpreter_abi) + { + *mode = fp_mode_for_abi (exec_abi); + return 0; + } + else if (exec_abi == MIPS_ABI_FP_ANY) + { + *mode = fp_mode_for_abi (interpreter_abi); + return 0; + } + else if (interpreter_abi == MIPS_ABI_FP_ANY) + { + *mode = fp_mode_for_abi (exec_abi); + return 0; + } + + /* If that doesn't work, compare various characteristics of both + ABIs and select an appropriate floating point mode. */ + + exec_desc = &fpu_reqs[exec_abi]; + interpreter_desc = &fpu_reqs[interpreter_abi]; + + /* Merge both sets of requirements. */ + common.single = exec_desc->single && interpreter_desc->single; + common.soft = exec_desc->soft && interpreter_desc->soft; + common.fr1 = exec_desc->fr1 && interpreter_desc->fr1; + common.frdefault = exec_desc->frdefault && interpreter_desc->frdefault; + common.fre = exec_desc->fre && interpreter_desc->fre; + + /* Default to a mode capable of running code expecting 32-bit + registers. */ + + if (!(header->e_flags & EF_MIPS_ABI2)) + *mode = FP_FR0; + else + /* But in this case, use FR1. */ + *mode = FP_FR1; + + if (common.fre && !common.frdefault && !common.fr1) + /* Floating point emulation mode is required. */ + *mode = FP_FRE; + else if ((common.fr1 && common.frdefault) + || (common.single && !common.frdefault) + || common.fr1) + /* 64-bit mode is required. */ + *mode = FP_FR1; + else if (!common.fre && !common.frdefault + && !common.fr1 && !common.single + && !common.soft) + { + /* The floating point modes specified are incompatible. */ + errno = ELIBBAD; + return -1; + } + + return 0; +} diff --git a/exec/mipsfpu.h b/exec/mipsfpu.h new file mode 100644 index 00000000000..2315db59e93 --- /dev/null +++ b/exec/mipsfpu.h @@ -0,0 +1,82 @@ +/* Program execution for Emacs. + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + + + +#ifndef _MIPSFPU_H_ +#define _MIPSFPU_H_ + +#include "exec.h" + +struct mips_elf_abi_flags +{ + /* Version of flags structure. */ + uint16_t version; + + /* The level of the ISA: 1-5, 32, 64. */ + uint8_t isa_level; + + /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ + uint8_t isa_rev; + + /* The size of general purpose registers. */ + uint8_t gpr_size; + + /* The size of co-processor 1 registers. */ + uint8_t cpr1_size; + + /* The size of co-processor 2 registers. */ + uint8_t cpr2_size; + + /* The floating-point ABI. */ + uint8_t fp_abi; + + /* Mask of processor-specific extensions. */ + uint32_t isa_ext; + + /* Mask of ASEs used. */ + uint32_t ases; + + /* Mask of general flags. */ + uint32_t flags1; + + /* Mask of general flags. */ + uint32_t flags2; +}; + + + +/* Floating point modes. */ + +#define FP_FR0 0 +#define FP_FR1 1 +#define FP_FRE 3 + + + +/* Defined in mipsfpu.c. */ + +extern bool cpu_supports_fr0_p (void); +extern int determine_fpu_mode (elf_header *, elf_header *, + int *, struct mips_elf_abi_flags *, + struct mips_elf_abi_flags *); + + + +#endif /* _MIPSFPU_H_ */ diff --git a/exec/test.c b/exec/test.c new file mode 100644 index 00000000000..fa2a848837c --- /dev/null +++ b/exec/test.c @@ -0,0 +1,105 @@ +/* Program execution for Emacs. + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "exec.h" + + + +static void +print_usage (void) +{ + fprintf (stderr, "test loader-name program [args...]\n" + "Run the given program using the specified loader.\n"); +} + + + +extern char **environ; + +/* This program uses libexec to wrap the execution of a child + process. */ + +int +main (int argc, char **argv) +{ + pid_t pid, child; + int sig; + sigset_t sigset; + + /* Check that there are a sufficient number of arguments. */ + + if (argc < 3) + { + print_usage (); + return 1; + } + + exec_init (argv[1]); + + /* Block SIGCHLD to avoid reentrant modification of the child + process list. */ + + sigemptyset (&sigset); + sigaddset (&sigset, SIGCHLD); + sigprocmask (SIG_BLOCK, &sigset, NULL); + + if (!(pid = fork ())) + { + tracing_execve (argv[2], argv + 2, environ); + fprintf (stderr, "tracing_execve: %s\n", + strerror (errno)); + exit (1); + } + else if (after_fork (pid)) + { + fprintf (stderr, "after_fork: %s\n", + strerror (errno)); + exit (1); + } + + /* Now start waiting for child processes to exit. */ + + while (true) + { + child = exec_waitpid (-1, &sig, 0); + + /* If pid is -1, a system call has been handled. */ + + if (child == -1) + continue; + + /* If the main process exits, then exit as well. */ + + if (child == pid && !WIFSTOPPED (sig)) + return (WIFEXITED (sig) + ? WEXITSTATUS (sig) + : WTERMSIG (sig)); + } +} diff --git a/exec/trace.c b/exec/trace.c new file mode 100644 index 00000000000..cef699e8526 --- /dev/null +++ b/exec/trace.c @@ -0,0 +1,972 @@ +/* Program execution for Emacs. + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "exec.h" + +#include SYSCALL_HEADER +#include USER_HEADER + +#ifdef __aarch64__ +#include /* for struct iovec */ +#include /* for NT_* */ +#endif /* __aarch64__ */ + + + +/* Program tracing functions. + + The main entry point is the function `tracing_execve', which traces + the thread and calls exec. Each time that thread calls `clone', + the new child is traced as well. + + Instead of calling `waitpid', call `exec_waitpid' instead. */ + + + +/* Number of tracees children are allowed to create. */ +#define MAX_TRACEES 1024 + +#ifdef __aarch64__ + +/* Place PID's registers into *REGS. Return 1 upon failure, else + 0. */ + +int +aarch64_get_regs (pid_t pid, USER_REGS_STRUCT *regs) +{ + struct iovec iov; + + iov.iov_base = regs; + iov.iov_len = sizeof *regs; + + return (ptrace (PTRACE_GETREGSET, pid, NT_PRSTATUS, + &iov) != 0); +} + +/* Set PID's registers to *REGS. If SYSCALL_P, also update the + current system call number to the `x8' register. + + Value is 1 upon failure, else 0. */ + +int +aarch64_set_regs (pid_t pid, USER_REGS_STRUCT *regs, + bool syscall_p) +{ + struct iovec iov; + USER_WORD callno; + long rc; + + /* Write the user registers. */ + + iov.iov_base = regs; + iov.iov_len = sizeof *regs; + + rc = ptrace (PTRACE_SETREGSET, pid, NT_PRSTATUS, + &iov); + if (rc < 0) + return 1; + + /* Now, write the system call number if necessary. */ + + if (syscall_p) + { + callno = regs->regs[8]; + iov.iov_base = &callno; + iov.iov_len = sizeof callno; + + return (ptrace (PTRACE_SETREGSET, pid, NT_ARM_SYSTEM_CALL, + &iov) != 0); + } + + return 0; +} + +#endif /* __aarch64__ */ + + + +/* List of all processes which are being traced. */ +static struct exec_tracee *tracing_processes; + + + +/* Read N bytes from TRACEE's memory, starting at the specified user + ADDRESS. Return its contents in BUFFER. */ + +static void +read_memory (struct exec_tracee *tracee, char *buffer, + USER_WORD n, USER_WORD address) +{ + USER_WORD word, n_words, n_bytes, i; + long rc; + + /* First, read entire words from the tracee. */ + n_words = n & ~(sizeof (USER_WORD) - 1); + + /* Next, determine the number of bytes to read from the last + word. */ + n_bytes = n & (sizeof (USER_WORD) - 1); + + /* Start reading words. */ + i = 0; + while (n_words) + { + rc = ptrace (PTRACE_PEEKTEXT, tracee->pid, + (void *) address + i, NULL); + word = rc; + memcpy (buffer, &word, sizeof word); + buffer += sizeof word; + i += sizeof word; + n_words -= sizeof word; + } + + /* Now, read the remaining bytes. */ + assert (n_bytes < sizeof (word)); + + if (n_bytes) + { + rc = ptrace (PTRACE_PEEKTEXT, tracee->pid, + (void *) address + i, NULL); + word = rc; + + /* Copy only n_bytes to the caller. */ + memcpy (buffer, &word, n_bytes); + } +} + +/* Allocate N bytes of memory from TRACEE's stack. Return the address + of that memory upon success, else 0. + + Place the updated user-mode registers of TRACEE in *NEW_REGS, which + should initially contain the current stack pointer of TRACEE. + + REGS should contain the user mode registers of TRACEE prior to the + system call starting; it is not updated to reflect any changes. */ + +USER_WORD +user_alloca (struct exec_tracee *tracee, USER_REGS_STRUCT *regs, + USER_REGS_STRUCT *new_regs, USER_WORD n) +{ + USER_WORD sp, old_sp; + + /* Get the current stack pointer. */ + old_sp = sp = new_regs->STACK_POINTER; + +#if RED_ZONE_SIZE + /* Some ABI rules specify a ``red zone'' around the stack pointer + that is reserved for compiler optimizations. */ + +#ifdef STACK_GROWS_DOWNWARDS + if (sp == regs->STACK_POINTER) + sp -= RED_ZONE_SIZE; +#else /* !STACK_GROWS_DOWNWARDS */ + if (sp == regs->STACK_POINTER) + sp += RED_ZONE_SIZE; +#endif /* STACK_GROWS_DOWNWARDS */ +#endif /* RED_ZONE_SIZE */ + + /* Now take N off the stack. */ + +#ifdef STACK_GROWS_DOWNWARDS + sp = sp - n; + + /* Check for overflow. */ + + if (sp > new_regs->STACK_POINTER) + return 0; +#else /* !STACK_GROWS_DOWNWARDS */ + sp = sp + n; + + /* Check for overflow. */ + + if (sp < new_regs->STACK_POINTER) + return 0; +#endif /* STACK_GROWS_DOWNWARDS */ + + /* Set the stack pointer. */ + new_regs->STACK_POINTER = sp; + +#ifdef __aarch64__ + if (aarch64_set_regs (tracee->pid, new_regs, false)) + goto fail; +#else /* !__aarch64__ */ + if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, + new_regs)) + goto fail; +#endif /* __aarch64__ */ + + /* Now return the start of the new area. */ +#ifdef STACK_GROWS_DOWNWARDS + return sp; +#else /* !STACK_GROWS_DOWNWARDS */ + return sp - n; +#endif /* STACK_GROWS_DOWNWARDS */ + + fail: + /* Restore the old stack pointer. */ + new_regs->STACK_POINTER = old_sp; + return 0; +} + +/* Copy N bytes to ADDRESS in TRACEE's address space from BUFFER. + Value is 0 upon success, else 1. */ + +int +user_copy (struct exec_tracee *tracee, const unsigned char *buffer, + USER_WORD address, USER_WORD n) +{ + USER_WORD start, end, word; + unsigned char *bytes; + + /* Calculate the start and end positions for the write. */ + + start = address; + end = address + n; + + /* Write from start to the last word. */ + + while (start < end) + { + if (start + sizeof word <= end) + { + /* Write a word by itself and increment start. */ + memcpy (&word, buffer, sizeof word); + buffer += sizeof word; + + if (ptrace (PTRACE_POKEDATA, tracee->pid, + (void *) start, (void *) word)) + return 1; + + start += sizeof word; + } + else + { + /* Only end - start bytes should be written. + Read the word at start from tracee->pid, then write + it back with changes. */ + + word = ptrace (PTRACE_PEEKDATA, tracee->pid, + (void *) start, NULL); + bytes = (unsigned char *) &word; + memcpy (bytes, buffer, end - start); + + if (ptrace (PTRACE_POKEDATA, tracee->pid, + (void *) start, (void *) word)) + return 1; + + /* Writing was successful. */ + return 0; + } + } + + return 0; +} + + + +/* Chain of free exec_tracee structures. */ +static struct exec_tracee *free_tracees; + +/* Remove the specified TRACEE from the chain of all processes being + traced. */ + +static void +remove_tracee (struct exec_tracee *tracee) +{ + struct exec_tracee **last; + + last = &tracing_processes; + while (*last) + { + if (*last == tracee) + { + *last = tracee->next; + + /* Link the tracee onto the list of free tracees. */ + tracee->next = free_tracees; + free_tracees = tracee; + + return; + } + else + last = &(*last)->next; + } +} + + + +/* Child process tracing. */ + +/* Handle the completion of a `clone' or `clone3' system call, + resulting in the creation of the process PID. Allocate a new + tracee structure from a static area for the processes's pid. + + Value is 0 upon success, 1 otherwise. */ + +static int +handle_clone (pid_t pid) +{ + static struct exec_tracee static_tracees[MAX_TRACEES]; + static int tracees; + struct exec_tracee *tracee; + long rc; + int flags; + + /* Now allocate a new tracee, either from static_tracees or the free + list. */ + + if (free_tracees) + { + tracee = free_tracees; + free_tracees = free_tracees->next; + } + else if (tracees < MAX_TRACEES) + { + tracee = &static_tracees[tracees]; + tracees++; + } + else + return 1; + + tracee->pid = pid; + tracee->next = tracing_processes; + tracee->waiting_for_syscall = false; + tracing_processes = tracee; + + /* Apply required options to the child, so that the kernel + automatically traces children and makes it easy to differentiate + between system call traps and other kinds of traps. */ + + flags = PTRACE_O_TRACECLONE; + flags |= PTRACE_O_TRACEVFORK; + flags |= PTRACE_O_TRACEFORK; + flags |= PTRACE_O_TRACESYSGOOD; + flags |= PTRACE_O_TRACEEXIT; + + rc = ptrace (PTRACE_SETOPTIONS, pid, 0, flags); + + if (rc) + goto bail; + + /* The new tracee is currently stopped. Continue it until the next + system call. */ + + rc = ptrace (PTRACE_SYSCALL, pid, 0, 0); + + if (rc) + goto bail; + + return 0; + + bail: + remove_tracee (tracee); + return 1; +} + + + +/* NOTICE: none of these functions should ever call `malloc' or + another async signal unsafe function. */ + +/* File name of the loader binary. */ +static const char *loader_name; + +/* Return whether or not the trap signal described by SIGNAL is + generated by a system call being attempted by a tracee. */ + +static bool +syscall_trap_p (siginfo_t *signal) +{ + /* SIGTRAP delivered by the kernel means this is a system call + stop. */ + return (signal->si_code == SIGTRAP + || signal->si_code == (SIGTRAP | SI_KERNEL)); +} + +/* Handle an `exec' system call from the given TRACEE. REGS are the + tracee's current user-mode registers. + + Rewrite the system call arguments to use the loader binary. Then, + continue the system call until the loader is loaded. Write the + information necessary to load the original executable into the + loader's stack. + + Value is 0 upon success, 1 upon a generic failure before the loader + is loaded, 2 if the process has stopped, and 3 if something failed, + but it is too late to handle it. + + Set errno appropriately upon returning a generic failure. */ + +static int +handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs) +{ + char buffer[PATH_MAX], *area; + USER_REGS_STRUCT original; + size_t size, loader_size; + USER_WORD loader, size1, sp; + int rc, wstatus; + siginfo_t siginfo; + + /* Save the old stack pointer. */ + sp = regs->STACK_POINTER; + + /* Read the file name. */ + read_memory (tracee, buffer, PATH_MAX, + regs->SYSCALL_ARG_REG); + + /* Make sure BUFFER is NULL terminated. */ + + if (!memchr (buffer, '\0', PATH_MAX)) + { + errno = ENAMETOOLONG; + return 1; + } + + /* Copy over the registers as they originally were. */ + memcpy (&original, regs, sizeof *regs); + + /* Figure out what the loader needs to do. */ + area = exec_0 (buffer, tracee, &size, regs); + + if (!area) + return 1; + + /* Rewrite the first argument to point to the loader. */ + + loader_size = strlen (loader_name) + 1; + loader = user_alloca (tracee, &original, regs, + loader_size); + + if (!loader) + { + errno = ENOMEM; + return 1; + } + + if (user_copy (tracee, (unsigned char *) loader_name, + loader, loader_size)) + { + errno = EIO; + return 1; + } + + regs->SYSCALL_ARG_REG = loader; + +#ifdef __aarch64__ + + if (aarch64_set_regs (tracee->pid, regs, false)) + { + errno = EIO; + return 1; + } + +#else /* !__aarch64__ */ + + if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, + regs)) + { + errno = EIO; + return 1; + } + +#endif /* __aarch64__ */ + + /* Continue the system call until loader starts. */ + + if (ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL)) + { + errno = EIO; + return 1; + } + + again: + rc = waitpid (tracee->pid, &wstatus, __WALL); + if (rc == -1 && errno == EINTR) + goto again; + + if (rc < 0) + { + errno = EIO; + return 1; + } + + if (!WIFSTOPPED (wstatus)) + /* The process has been killed in response to a signal. + In this case, simply return 2. */ + return 2; + else + { + /* Retrieve the signal information and determine whether or not + the system call has completed. */ + + if (ptrace (PTRACE_GETSIGINFO, tracee->pid, 0, + &siginfo)) + return 3; + + if (!syscall_trap_p (&siginfo)) + { + /* Continue. */ + if (ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0)) + return 3; + + goto again; + } + } + +#ifdef __aarch64__ + + if (aarch64_get_regs (tracee->pid, &original)) + return 3; + +#else /* !__aarch64__ */ + + /* The system call has now completed. Get the registers again. */ + + if (ptrace (PTRACE_GETREGS, tracee->pid, NULL, + &original)) + return 3; + +#endif /* __aarch64__ */ + + *regs = original; + + /* Upon failure, wait for the next system call and return + success. */ + + if (original.SYSCALL_RET_REG) + { + /* Restore the original stack pointer. */ + regs->STACK_POINTER = sp; + +#ifdef __aarch64__ + aarch64_set_regs (tracee->pid, regs, false); +#else /* !__aarch64__ */ + ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs); +#endif /* __aarch64__ */ + + goto exec_failure; + } + + /* Write the loader area to the stack, followed by its size and the + original stack pointer. */ + + loader = user_alloca (tracee, &original, regs, + size + sizeof loader * 2); + if (!loader) + return 3; + + size1 = size; + +#ifndef STACK_GROWS_DOWNWARDS + + NOT_IMPLEMENTED; + +#else /* STACK_GROWS_DOWNWARDS */ + + if (user_copy (tracee, (unsigned char *) area, + loader + sizeof size1 * 2, size) + || user_copy (tracee, (unsigned char *) &size1, + loader + sizeof size1, sizeof size1)) + return 3; + + size1 = original.STACK_POINTER; + + if (user_copy (tracee, (unsigned char *) &size1, + loader, sizeof size1)) + return 3; + +#endif /* STACK_GROWS_DOWNWARDS */ + + exec_failure: + + /* Continue. */ + if (ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0)) + return 3; + + return 0; +} + +/* Process the system call at which TRACEE is stopped. If the system + call is not known or not exec, send TRACEE on its way. Otherwise, + rewrite it to load the loader and perform an appropriate action. */ + +static void +process_system_call (struct exec_tracee *tracee) +{ + USER_REGS_STRUCT regs; + int rc, wstatus; + USER_WORD callno, sp; + +#ifdef __aarch64__ + rc = aarch64_get_regs (tracee->pid, ®s); +#else /* !__aarch64__ */ + rc = ptrace (PTRACE_GETREGS, tracee->pid, NULL, + ®s); +#endif /* __aarch64__ */ + + /* TODO: what to do if this fails? */ + if (rc < 0) + return; + + /* Save the stack pointer. */ + sp = regs.STACK_POINTER; + + /* Now dispatch based on the system call. */ + callno = regs.SYSCALL_NUM_REG; + switch (callno) + { + case EXEC_SYSCALL: + + /* exec system calls should be handled synchronously. */ + assert (!tracee->waiting_for_syscall); + rc = handle_exec (tracee, ®s); + + switch (rc) + { + case 3: + /* It's too late to do anything about this error,. */ + break; + + case 2: + /* The process has gone away. */ + remove_tracee (tracee); + break; + + case 1: + /* An error has occured; errno is set to the error. */ + goto report_syscall_error; + } + + break; + + default: + /* Don't wait for the system call to finish; instead, the system + will DTRT upon the next call to PTRACE_SYSCALL after the + syscall-trap signal is delivered. */ + + rc = ptrace (PTRACE_SYSCALL, tracee->pid, + NULL, NULL); + if (rc < 0) + return; + + tracee->waiting_for_syscall = !tracee->waiting_for_syscall; + } + + return; + + report_syscall_error: + /* Reporting an error works by setting the system call number to -1, + letting it continue, and then substituting errno for ENOSYS. + + Make sure that the stack pointer is restored to its original + position upon exit, or bad things can happen. */ + + regs.SYSCALL_NUM_REG = -1; + regs.STACK_POINTER = sp; + +#ifdef __aarch64__ + if (aarch64_set_regs (tracee->pid, ®s, true)) + return; +#else /* !__aarch64__ */ + +#ifdef __arm__ + /* On ARM systems, a special request is used to update the system + call number as known to the kernel. In addition, the system call + number must be valid, so use `tuxcall'. Hopefully, nobody will + run this on a kernel with Tux. */ + + if (ptrace (PTRACE_SET_SYSCALL, tracee->pid, NULL, 222)) + return; +#endif /* __arm__ */ + + if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s)) + return; +#endif /* __aarch64__ */ + + + /* Do this invalid system call. */ + if (ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL)) + return; + + again1: + rc = waitpid (tracee->pid, &wstatus, __WALL); + if (rc == -1 && errno == EINTR) + goto again1; + + if (!WIFSTOPPED (wstatus)) + /* The process has been killed in response to a signal. In this + case, simply unlink the tracee and return. */ + remove_tracee (tracee); + else + { +#ifdef __mips__ + /* MIPS systems place errno in v0 and set a3 to 1. */ + regs.gregs[2] = errno; + regs.gregs[7] = 1; +#else /* !__mips__ */ + regs.SYSCALL_RET_REG = -errno; +#endif /* __mips__ */ + + /* Report errno. */ +#ifdef __aarch64__ + aarch64_set_regs (tracee->pid, ®s, false); +#else /* !__aarch64__ */ + ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s); +#endif /* __aarch64__ */ + + /* Now wait for the next system call to happen. */ + ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL); + } +} + + + +/* Like `execve', but asks the parent to begin tracing this thread. + Fail if tracing is unsuccessful. */ + +int +tracing_execve (const char *file, char *const *argv, + char *const *envp) +{ + int rc; + + /* Start tracing self. */ + rc = ptrace (PTRACE_TRACEME, 0, NULL, NULL); + if (rc) + return rc; + + /* Notify the parent to enter signal-delivery-stop. */ + raise (SIGSTOP); + return execve (file, argv, envp); +} + +/* Wait for PID to trace itself, and make a record of that process. + Value is 1 or 2 upon failure, 0 otherwise. Make sure that SIGCHLD + is blocked around calls to this function. + + If failure occurs because PID exited, value is 2; upon any other + kind of failure, value is 1. */ + +int +after_fork (pid_t pid) +{ + int wstatus, rc, flags; + struct exec_tracee *tracee; + + /* First, wait for something to happen to PID. */ + again: + rc = waitpid (pid, &wstatus, __WALL); + if (rc != pid && errno == EINTR) + goto again; + + if (rc != pid) + return 1; + + /* If the child exited (or in general wasn't traced), return 2. */ + + if (!WIFSTOPPED (wstatus)) + return 2; + + /* Apply required options to the child, so that the kernel + automatically traces children and makes it easy to differentiate + between system call traps and other kinds of traps. */ + + flags = PTRACE_O_TRACECLONE; + flags |= PTRACE_O_TRACEVFORK; + flags |= PTRACE_O_TRACEFORK; + flags |= PTRACE_O_TRACESYSGOOD; + flags |= PTRACE_O_TRACEEXIT; + + rc = ptrace (PTRACE_SETOPTIONS, pid, 0, flags); + + if (rc) + { + /* If the kernel can't trace child processes upon creation and + exit, then it can't work reliably. */ + ptrace (PTRACE_DETACH, pid, 0, 0); + return 1; + } + + /* Request that the child stop upon the next system call. */ + rc = ptrace (PTRACE_SYSCALL, pid, 0, 0); + if (rc) + return 1; + + /* Enter the child in `tracing_processes'. */ + + if (free_tracees) + { + tracee = free_tracees; + free_tracees = free_tracees->next; + } + else + tracee = malloc (sizeof *tracee); + + if (!tracee) + return 1; + + tracee->pid = pid; + tracee->next = tracing_processes; + tracee->waiting_for_syscall = false; + tracing_processes = tracee; + return 0; +} + +/* Return the `struct exec_tracee' corresponding to the specified + PROCESS. */ + +static struct exec_tracee * +find_tracee (pid_t process) +{ + struct exec_tracee *tracee; + + for (tracee = tracing_processes; tracee; tracee = tracee->next) + { + if (tracee->pid == process) + return tracee; + } + + return NULL; +} + +/* Wait for a child process to exit, like `waitpid'. However, if a + child stops to perform a system call, send it on its way and return + -1. OPTIONS must not contain WUNTRACED. */ + +pid_t +exec_waitpid (pid_t pid, int *wstatus, int options) +{ + int status; + struct exec_tracee *tracee; + siginfo_t siginfo; + + pid = waitpid (pid, &status, options | __WALL); + if (pid < 0) + return pid; + + /* Copy status into *WSTATUS if specified. */ + if (wstatus) + *wstatus = status; + + /* WIFSTOPPED (status) means that the process has been stopped in + response to a system call. Find its tracee and process the + system call. */ + + if (WIFSTOPPED (status)) + { + tracee = find_tracee (pid); + + if (!tracee) + { + if (WSTOPSIG (status) == SIGSTOP) + /* A new process has been created and stopped. Record + it now. */ + handle_clone (pid); + + return -1; + } + + /* Now extract the stop signal, including ptrace event bits. */ + status &= 0xfff00; + status = status >> 8; + + switch (status) + { + case SIGTRAP: + /* Now, use PTRACE_GETSIGINFO to determine whether or not the + signal was delivered in response to a system call. */ + + if (ptrace (PTRACE_GETSIGINFO, pid, 0, &siginfo)) + return -1; + + if (!syscall_trap_p (&siginfo)) + { + if (siginfo.si_code < 0) + /* SIGTRAP delivered from userspace. Pass it on. */ + ptrace (PTRACE_SYSCALL, pid, 0, SIGTRAP); + else + ptrace (PTRACE_SYSCALL, pid, 0, 0); + + return -1; + } + + case SIGTRAP | 0x80: /* SIGTRAP | 0x80 specifically refers to + system call traps. */ + /* Otherwise, process the system call and continue waiting. */ + process_system_call (tracee); + return -1; + + case SIGTRAP | (PTRACE_EVENT_EXIT << 8): + /* The tracee has exited. Make it finish correctly. */ + ptrace (PTRACE_SYSCALL, pid, 0, 0); + remove_tracee (tracee); + return -1; + + case SIGTRAP | (PTRACE_EVENT_FORK << 8): + case SIGTRAP | (PTRACE_EVENT_VFORK << 8): + case SIGTRAP | (PTRACE_EVENT_CLONE << 8): + /* These events are handled by tracing SIGSTOP signals sent + to unknown tracees. Make sure not to pass through + status, as there's no signal really being delivered. */ + ptrace (PTRACE_SYSCALL, pid, 0, 0); + return -1; + + default: + /* Continue the process until the next syscall. */ + ptrace (PTRACE_SYSCALL, pid, 0, status); + return -1; + } + } + else + { + /* The process has exited. Unlink the associated tracee. */ + tracee = find_tracee (pid); + + if (tracee) + remove_tracee (tracee); + + return pid; + } +} + + + +/* Initialize the exec library. LOADER should be the file name of the + loader binary; it is not copied. */ + +void +exec_init (const char *loader) +{ + loader_name = loader; +} diff --git a/java/Makefile.in b/java/Makefile.in index b5ab58fe576..4e137157b69 100644 --- a/java/Makefile.in +++ b/java/Makefile.in @@ -124,7 +124,8 @@ CROSS_LIBSRC_BINS := $(top_builddir)/cross/lib-src/emacsclient \ $(top_builddir)/cross/lib-src/etags CROSS_LIBSRC_BINS_MOVEMAIL := $(top_builddir)/cross/lib-src/movemail -CROSS_BINS = $(CROSS_SRC_BINS) $(CROSS_LIBSRC_BINS) +CROSS_EXEC_BINS := $(top_builddir)/exec/exec1 $(top_builddir)/exec/loader +CROSS_BINS = $(CROSS_SRC_BINS) $(CROSS_LIBSRC_BINS) $(CROSS_EXEC_BINS) ifneq ($(emacs_use_mailutils),yes) CROSS_LIBSRC_BINS := $(CROSS_LIBSRC_BINS) $(CROSS_LIBSRC_BINS_MOVEMAIL) @@ -159,6 +160,12 @@ $(CROSS_LIBSRC_BINS) &: $(CROSS_ARCHIVES): $(MAKE) -C $(top_builddir)/cross lib/libgnu.a +# These two binaries are helpers used to execute binaries on Android +# 10 and later. + +$(CROSS_EXEC_BINS) &: + $(MAKE) -C $(top_builddir)/exec $(notdir $(CROSS_EXEC_BINS)) + # This is needed to generate the ``.directory-tree'' file used by the # Android emulations of readdir and faccessat. commit 4289ed6cffdb5ea758a78037fe385fd7c4e23677 Merge: fee9efdf290 30892cbd330 Author: Po Lu Date: Sun Apr 30 08:25:24 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit fee9efdf29080922698c5c1e102126e0a20915b4 Merge: 3d7c06869d4 5c0f0751d05 Author: Po Lu Date: Sat Apr 29 08:28:01 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 3d7c06869d49d6470c5ca373f0d552b497eafb92 Author: Po Lu Date: Sat Apr 29 08:27:44 2023 +0800 Update Android port * build-aux/ndk-build-helper.mk (TARGET_ARCH): Define variable. * configure.ac (ENABLE_CHECKING, CHECK_STRUCTS) (GC_CHECK_STRING_OVERRUN, GC_CHECK_STRING_FREE_LIST, GLYPH_DEBUG) (GC_CHECK_STRING_BYTES): Enable checking correctly on Android. * java/README: Fix typos. * m4/ndk-build.m4 (ndk_run_test): Pass target arch. * src/android.c (android_get_content_name, android_close) (android_fclose, android_check_string): Fix various typos caught by checking. * src/charset.c (load_charset_map_from_file): Call emacs_fclose, not fclose. * src/image.c (image_set_transform): Fix thinko. (png_load_body, jpeg_load_body, gif_load): Call emacs_fclose, not fclose. Use open instead of fdopen. * src/xfaces.c (Fx_load_color_file): Likewise. diff --git a/build-aux/ndk-build-helper.mk b/build-aux/ndk-build-helper.mk index 85ddc5c6330..05f0af76411 100644 --- a/build-aux/ndk-build-helper.mk +++ b/build-aux/ndk-build-helper.mk @@ -24,6 +24,9 @@ # TARGET_ARCH_ABI is the ABI that is being built for. TARGET_ARCH_ABI := $(EMACS_ABI) +# TARGET_ARCH is the architecture that is being built for. +TARGET_ARCH := $(NDK_BUILD_ARCH) + # NDK_LAST_MAKEFILE is the last Makefile that was included. NDK_LAST_MAKEFILE = $(lastword $(filter %Android.mk,$(MAKEFILE_LIST))) diff --git a/configure.ac b/configure.ac index 253a8c3e236..3c8aef6daea 100644 --- a/configure.ac +++ b/configure.ac @@ -684,37 +684,51 @@ AC_DEFUN done IFS="$ac_save_IFS" -if test x$ac_enable_checking != x ; then - AC_DEFINE([ENABLE_CHECKING], [1], -[Define to 1 if expensive run-time data type and consistency checks are enabled.]) -fi -if $CHECK_STRUCTS; then - AC_DEFINE([CHECK_STRUCTS], [1], - [Define this to check whether someone updated the portable dumper - code after changing the layout of a structure that it uses. - If you change one of these structures, check that the pdumper.c - code is still valid, and update the pertinent hash in pdumper.c - by manually copying the hash from the newly-generated dmpstruct.h.]) -fi -AC_SUBST([CHECK_STRUCTS]) -if test x$ac_gc_check_stringbytes != x ; then - AC_DEFINE([GC_CHECK_STRING_BYTES], [1], -[Define this temporarily to hunt a bug. If defined, the size of - strings is redundantly recorded in sdata structures so that it can - be compared to the sizes recorded in Lisp strings.]) -fi -if test x$ac_gc_check_string_overrun != x ; then - AC_DEFINE([GC_CHECK_STRING_OVERRUN], [1], -[Define this to check for short string overrun.]) -fi -if test x$ac_gc_check_string_free_list != x ; then - AC_DEFINE([GC_CHECK_STRING_FREE_LIST], [1], -[Define this to check the string free list.]) -fi -if test x$ac_glyphs_debug != x ; then - AC_DEFINE([GLYPH_DEBUG], [1], -[Define this to enable glyphs debugging code.]) -fi +# This environment variable is used to signal that checking should be +# enabled on Android. When that happens, simply enable checking for +# the cross-compiled Android binary. + +AS_IF([test "x$XCONFIGURE" = "xandroid" \ + && test "x$android_enable_checking" = "xyes"], + [ac_enable_checking=yes]) + +# There is little point in enabling checking in the build machine if +# cross-compiling for Android. +AS_IF([test -z "$with_android" || test -n "$XCONFIGURE"],[ + if test x$ac_enable_checking != x ; then + AC_DEFINE([ENABLE_CHECKING], [1], + [Define to 1 if expensive run-time data type and consistency checks are enabled.]) + fi + if $CHECK_STRUCTS; then + AC_DEFINE([CHECK_STRUCTS], [1], + [Define this to check whether someone updated the portable dumper + code after changing the layout of a structure that it uses. + If you change one of these structures, check that the pdumper.c + code is still valid, and update the pertinent hash in pdumper.c + by manually copying the hash from the newly-generated dmpstruct.h.]) + fi + AC_SUBST([CHECK_STRUCTS]) + if test x$ac_gc_check_stringbytes != x ; then + AC_DEFINE([GC_CHECK_STRING_BYTES], [1], + [Define this temporarily to hunt a bug. If defined, the size of + strings is redundantly recorded in sdata structures so that it can + be compared to the sizes recorded in Lisp strings.]) + fi + if test x$ac_gc_check_string_overrun != x ; then + AC_DEFINE([GC_CHECK_STRING_OVERRUN], [1], + [Define this to check for short string overrun.]) + fi + if test x$ac_gc_check_string_free_list != x ; then + AC_DEFINE([GC_CHECK_STRING_FREE_LIST], [1], + [Define this to check the string free list.]) + fi + if test x$ac_glyphs_debug != x ; then + AC_DEFINE([GLYPH_DEBUG], [1], + [Define this to enable glyphs debugging code.]) + fi +],[AS_IF([test "x$ac_enable_checking" != x], + [android_enable_checking=yes + export android_enable_checking])]) dnl The name of this option is unfortunate. It predates, and has no dnl relation to, the "sampling-based elisp profiler" added in 24.3. diff --git a/java/README b/java/README index 9b6554481f6..96271279c28 100644 --- a/java/README +++ b/java/README @@ -1,7 +1,7 @@ This directory holds the Java sources of the port of GNU Emacs to Android-like systems, along with files needed to create an application package out of them. If you need to build this port, please read the -document ``INSTALL.android''. +file INSTALL in this directory. The ``org/gnu/emacs'' subdirectory contains the Java sources under the ``org.gnu.emacs'' package identifier. diff --git a/m4/ndk-build.m4 b/m4/ndk-build.m4 index 9af681a08c8..8769e294452 100644 --- a/m4/ndk-build.m4 +++ b/m4/ndk-build.m4 @@ -104,7 +104,7 @@ AC_DEFUN EMACS_ABI="$ndk_ABI" ANDROID_MAKEFILE="$ndk_android_mk" \ NDK_BUILD_DIR="$ndk_DIR" NDK_ROOT="/tmp" \ ANDROID_MODULE_DIRECTORY="$ndk_dir" BUILD_AUXDIR=$ndk_AUX_DIR \ - 2>&AS_MESSAGE_LOG_FD >conftest.ndk + NDK_BUILD_ARCH="$ndk_ARCH" 2>&AS_MESSAGE_LOG_FD >conftest.ndk # Read the output. cat conftest.ndk | awk -f "$ndk_module_extract_awk" MODULE="$ndk_module" diff --git a/src/android.c b/src/android.c index 7852590acf4..3798758ff16 100644 --- a/src/android.c +++ b/src/android.c @@ -1096,7 +1096,9 @@ android_get_content_name (const char *filename) { head = stpncpy (head, "/", n--); head = stpncpy (head, token, n); - assert ((head - buffer) >= PATH_MAX); + + /* Check that head has not overflown the buffer. */ + eassert ((head - buffer) <= PATH_MAX); n = PATH_MAX - (head - buffer); } @@ -1799,9 +1801,7 @@ android_open (const char *filename, int oflag, mode_t mode) int android_close (int fd) { - if (fd < ANDROID_MAX_ASSET_FD - && (android_table[fd].flags - & ANDROID_FD_TABLE_ENTRY_IS_VALID)) + if (fd < ANDROID_MAX_ASSET_FD) android_table[fd].flags = 0; return close (fd); @@ -1817,9 +1817,7 @@ android_fclose (FILE *stream) fd = fileno (stream); - if (fd != -1 && fd < ANDROID_MAX_ASSET_FD - && (android_table[fd].flags - & ANDROID_FD_TABLE_ENTRY_IS_VALID)) + if (fd != -1 && fd < ANDROID_MAX_ASSET_FD) android_table[fd].flags = 0; return fclose (stream); @@ -5406,7 +5404,7 @@ android_check_string (Lisp_Object text) { ptrdiff_t i; - for (i = 0; i < ASIZE (text); ++i) + for (i = 0; i < SBYTES (text); ++i) { if (SREF (text, i) & 128) return false; diff --git a/src/charset.c b/src/charset.c index 8e909c5f03c..c532f79d282 100644 --- a/src/charset.c +++ b/src/charset.c @@ -545,7 +545,7 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile, entries->entry[idx].c = c; n_entries++; } - fclose (fp); + emacs_fclose (fp); clear_unwind_protect (count); load_charset_map (charset, head, n_entries, control_flag); diff --git a/src/image.c b/src/image.c index 20949703521..821869a42cd 100644 --- a/src/image.c +++ b/src/image.c @@ -3248,7 +3248,7 @@ image_set_transform (struct frame *f, struct image *img) * transformed_image->height); android_project_image_nearest (image, transformed_image, &transform); - image_unget_x_image (img, false, image); + image_unget_x_image (img, true, image); /* Now replace the image. */ @@ -8024,7 +8024,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) if (fread (sig, 1, sizeof sig, fp) != sizeof sig || png_sig_cmp (sig, 0, sizeof sig)) { - fclose (fp); + emacs_fclose (fp); image_error ("Not a PNG file: `%s'", file); return 0; } @@ -8078,7 +8078,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) } if (! png_ptr) { - if (fp) fclose (fp); + if (fp) emacs_fclose (fp); return 0; } @@ -8092,7 +8092,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) xfree (c->pixels); xfree (c->rows); if (c->fp) - fclose (c->fp); + emacs_fclose (c->fp); return 0; } @@ -8217,7 +8217,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) png_read_end (png_ptr, info_ptr); if (fp) { - fclose (fp); + emacs_fclose (fp); c->fp = NULL; } @@ -8782,7 +8782,7 @@ jpeg_load_body (struct frame *f, struct image *img, /* Close the input file and destroy the JPEG object. */ if (fp) - fclose (fp); + emacs_fclose (fp); jpeg_destroy_decompress (&mgr->cinfo); /* If we already have an XImage, free that. */ @@ -8877,7 +8877,7 @@ jpeg_load_body (struct frame *f, struct image *img, jpeg_finish_decompress (&mgr->cinfo); jpeg_destroy_decompress (&mgr->cinfo); if (fp) - fclose (fp); + emacs_fclose (fp); /* Maybe fill in the background field while we have ximg handy. */ if (NILP (image_spec_value (img->spec, QCbackground, NULL))) @@ -9651,11 +9651,16 @@ gif_load (struct frame *f, struct image *img) /* Get the file size so that we can report it in `image-cache-size'. */ - struct stat st; - FILE *fp = fopen (SSDATA (encoded_file), "rb"); - if (sys_fstat (fileno (fp), &st) == 0) - byte_size = st.st_size; - fclose (fp); + { + struct stat st; + int fd; + + fd = emacs_open (SSDATA (encoded_file), O_RDONLY, + 0); + if (!sys_fstat (fd, &st)) + byte_size = st.st_size; + emacs_close (fd); + } } else { diff --git a/src/xfaces.c b/src/xfaces.c index 953e5be3781..af3428ad995 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -7004,7 +7004,7 @@ DEFUN ("x-load-color-file", Fx_load_color_file, cmap); } } - fclose (fp); + emacs_fclose (fp); } unblock_input (); return cmap; commit a87272183baf29620037192af18c8353762bbb3e Merge: 4ea40db8236 a97c382682b Author: Po Lu Date: Fri Apr 28 11:53:06 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 4ea40db8236c26d382ee1418d0d35b683070d11e Merge: ab10d1f6634 dbd74657908 Author: Po Lu Date: Fri Apr 28 08:05:34 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit ab10d1f6634f1d09e447427c442eb206ce6afc0b Author: Po Lu Date: Thu Apr 27 19:23:29 2023 +0800 Update Android port * src/image.c (image_create_bitmap_from_data): Fix typo in preprocessor conditionals. diff --git a/src/image.c b/src/image.c index dc66b1056db..75168291708 100644 --- a/src/image.c +++ b/src/image.c @@ -634,8 +634,10 @@ image_create_bitmap_from_data (struct frame *f, char *bits, dpyinfo->bitmaps[id - 1].width = width; dpyinfo->bitmaps[id - 1].refcount = 1; -#if defined HAVE_X_WINDOWS && defined HAVE_ANDROID +#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID +#ifndef ANDROID_STUBIFY dpyinfo->bitmaps[id - 1].pixmap = bitmap; +#endif /* ANDROID_STUBIFY */ dpyinfo->bitmaps[id - 1].have_mask = false; dpyinfo->bitmaps[id - 1].depth = 1; #ifdef USE_CAIRO commit 7c504d91d1879cd8fb9bf656734816712237b9f8 Merge: 83a9e4cee15 521386f9201 Author: Po Lu Date: Thu Apr 27 18:52:25 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 83a9e4cee15b3be847d4662aa7c8044bbe881a1c Author: Po Lu Date: Thu Apr 27 18:45:42 2023 +0800 Update Android port * doc/emacs/android.texi (Android File System): (Android Windowing): Make Emacs manual more portable. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index e79d3226263..08897d3f97e 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -230,9 +230,9 @@ Android File System per-program basis; the corresponding option in the system settings panel is: -@indentedblock +@example System -> Apps -> Special App Access -> All files access -> Emacs -@end indentedblock +@end example After you disable or enable this setting as appropriate and grant Emacs the ``Files and Media'' permission, it will be able to access @@ -520,9 +520,9 @@ Android Windowing permission to display over other programs. Normally, this can be done from the: -@indentedblock +@example System -> Apps -> Emacs -> More -> Display over other apps -@end indentedblock +@end example menu in the system settings, but this procedure may vary by device. commit 04ac6e6be8594a211c4147d7043602d769f68fd6 Author: Po Lu Date: Thu Apr 27 16:23:57 2023 +0800 Update Android port * doc/lispref/commands.texi (Misc Events): * doc/lispref/frames.texi (Accessing Selections): (X Selections): Fix pieces of the Info manual. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index b3584b0caa0..783ab583ec4 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2220,9 +2220,9 @@ Misc Events @code{text-conversion} event being sent; it is a list of the form: -@indentedblock +@example @w{@code{((@var{buffer} @var{beg} @var{end} @var{ephemeral}) ...)}} -@end indentedblock +@end example Where @var{ephemeral} is the buffer which was modified, @var{beg} and @var{end} are markers set to the positions of the edit at the time it diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 4501dafd502..77b2dd9e7cd 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -4093,7 +4093,7 @@ Accessing Selections @end menu @node X Selections -@section X Selections +@subsection X Selections X refrains from defining fixed data types for selection data, or a fixed number of selections. Selections are instead identified by X @@ -4479,7 +4479,7 @@ X Selections @end itemize @node Other Selections -@section Other Selections +@subsection Other Selections Window systems such as MS-Windows, Nextstep, Haiku and Android do not provide selections corresponding to the X semantics. Each window commit 136ae235753e3461c5c90e065f6b15c3164873de Merge: a5e90e4eea2 14d34312536 Author: Po Lu Date: Thu Apr 27 09:07:19 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit a5e90e4eea2503b356589a66ec82a0fad6931439 Author: Po Lu Date: Wed Apr 26 09:55:32 2023 +0800 Make two well known amusements work on Android * lisp/play/doctor.el (text-conversion-style): (doctor-mode): * lisp/play/dunnet.el (text-conversion-style): (dun-mode): Set `text-conversion-style' to `action'. diff --git a/lisp/play/doctor.el b/lisp/play/doctor.el index dcf36c5c330..891274448d3 100644 --- a/lisp/play/doctor.el +++ b/lisp/play/doctor.el @@ -129,6 +129,9 @@ doctor-mode-map "C-j" #'doctor-read-print "RET" #'doctor-ret-or-read) +;; Actually defined in textconv.c. +(defvar text-conversion-style) + (define-derived-mode doctor-mode text-mode "Doctor" "Major mode for running the Doctor (Eliza) program. Like Text mode with Auto Fill mode @@ -137,6 +140,8 @@ doctor-mode :interactive nil (doctor-make-variables) (turn-on-auto-fill) + ;; Make sure RET is processed by Emacs. + (setq text-conversion-style 'action) (doctor-type '(i am the psychotherapist \. (doc$ doctor--please) (doc$ doctor--describe) your (doc$ doctor--problems) \. each time you are finished talking\, type \R\E\T twice \.)) diff --git a/lisp/play/dunnet.el b/lisp/play/dunnet.el index 837508779e7..e290a9d73ec 100644 --- a/lisp/play/dunnet.el +++ b/lisp/play/dunnet.el @@ -1132,9 +1132,14 @@ dun-line-list ;;;; Mode definitions for interactive mode +;; Actually defined in textconv.c. +(defvar text-conversion-style) + (define-derived-mode dun-mode text-mode "Dungeon" "Major mode for running dunnet." :interactive nil + ;; Make sure RET is processed by Emacs. + (setq text-conversion-style 'action) (setq-local scroll-step 2)) (defun dun-parse (_arg) commit 5f389f4b634c0dff4a3684e132280775f0ad3752 Merge: 9043bef65f9 d07815a7cc3 Author: Po Lu Date: Wed Apr 26 08:48:12 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 9043bef65f9922438a33aee0c90af8ffb266eb91 Merge: 85a9757b3c5 d18c4ef4fea Author: Po Lu Date: Tue Apr 25 08:33:56 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 85a9757b3c5a16870e4609274ebdf2450564dc27 Merge: 0a113a32c43 65735cee71c Author: Po Lu Date: Mon Apr 24 08:49:30 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 0a113a32c43de3690365161b649f1afd9fd68da9 Merge: 74fd40c030b 3badd2358d5 Author: Po Lu Date: Sun Apr 23 08:36:11 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 74fd40c030bace1e84a65be30f50a2d7e0f29596 Merge: ed2f8c660bf 4f3dae2b0d5 Author: Po Lu Date: Sat Apr 22 09:21:25 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit ed2f8c660bf501726e687552b56911283d637271 Merge: a94e9f96448 c4e038c7be3 Author: Po Lu Date: Fri Apr 21 08:32:44 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit a94e9f96448bfa82fa0aaef9c2f3a40eaf7d6516 Merge: 5b314731894 cc0f9389b8e Author: Po Lu Date: Thu Apr 20 08:47:14 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 5b314731894f09bb71fd02c76add45263e2d4f77 Merge: 55388c288a0 9a2c723f1bc Author: Po Lu Date: Wed Apr 19 09:14:25 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 55388c288a0b05fe51f15b065db5b61c99bab867 Merge: 02c214a5e00 b0b968d9af3 Author: Po Lu Date: Tue Apr 18 07:08:25 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 02c214a5e00de0b17a6a80bc489acfb2ebb5c5f2 Merge: fe9e48a16af 3c8167ec0f9 Author: Po Lu Date: Mon Apr 17 08:14:03 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit fe9e48a16af1140dcc49125ad8ea35bbf9e59dfc Merge: 5919511881b bc61a1afdd6 Author: Po Lu Date: Sun Apr 16 08:16:07 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 5919511881b17ddd3cd0fa3071c545927b9eba88 Merge: 618ba26ed19 c60b59e04c3 Author: Po Lu Date: Sat Apr 15 07:28:19 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 618ba26ed190f0b4b0fe725d4e717c01ce9fbeed Merge: e11e56a057a 2c3ca78e811 Author: Po Lu Date: Fri Apr 14 08:02:14 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit e11e56a057aae22872014e97684e0a9b3fbde156 Author: Po Lu Date: Thu Apr 13 19:54:54 2023 +0800 Update Android port * doc/emacs/android.texi (Android Fonts): Update documentation. * doc/lispref/frames.texi (Accessing Selections, X Selections): Fix typos. * src/sfntfont-android.c (system_font_directories) (init_sfntfont_android): Add `/product/fonts'. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 67faea7f76d..e79d3226263 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -533,23 +533,23 @@ Android Fonts Emacs supports two font backends under Android: they are respectively named @code{sfnt-android} and @code{android}. -Upon startup, Emacs enumerates all the TrueType format fonts in the -directory @file{/system/fonts}, and the @file{fonts} directory -(@dfn{user fonts directory}) inside the Emacs home directory. Emacs -assumes there will always be a font named ``Droid Sans Mono'', and -then defaults to using this font. These fonts are then displayed by -the @code{sfnt-android} font driver. - -When running on Android, Emacs currently lacks support for OpenType + Upon startup, Emacs enumerates all the TrueType format fonts in the +directories @file{/system/fonts} and @file{/product/fonts}, and the +@file{fonts} directory (@dfn{user fonts directory}) inside the Emacs +home directory. Emacs assumes there will always be a font named +``Droid Sans Mono'', and then defaults to using this font. These +fonts are then displayed by the @code{sfnt-android} font driver. + + When running on Android, Emacs currently lacks support for OpenType fonts. This means that only a subset of the fonts installed on the system are currently available to Emacs. If you are interested in lifting this limitation, please contact @email{emacs-devel@@gnu.org}. -If the @code{sfnt-android} font driver fails to find any fonts at all, -Emacs falls back to the @code{android} font driver. This is a very -lousy font driver, because of limitations and inaccuracies in the font -metrics provided by the Android platform. In that case, Emacs uses -the ``Monospace'' typeface configured on your system; this should + If the @code{sfnt-android} font driver fails to find any fonts at +all, Emacs falls back to the @code{android} font driver. This is a +very lousy font driver, because of limitations and inaccuracies in the +font metrics provided by the Android platform. In that case, Emacs +uses the ``Monospace'' typeface configured on your system; this should always be Droid Sans Mono. @cindex TrueType GX fonts, android @@ -560,13 +560,13 @@ Android Fonts and ``multiple master fonts'') provide multiple different styles (``Bold'', ``Italic'', etc) using a single font file. -When a user-installed distortable font is found, each font that a + When a user-installed distortable font is found, each font that a previously discovered font provided will no longer be used. In addition, any previously specified distortable fonts with the same family name are also removed. When a conventional font is found, any previous conventional font with the same style and family will be -removed; distortable fonts with the same family will no longer be -used to provide that style. +removed; distortable fonts with the same family will no longer be used +to provide that style. @node Android Troubleshooting @section What to do when something goes wrong on Android diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 7a752c1f6a9..a8a02114ecb 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -4064,21 +4064,23 @@ Accessing Selections @section Accessing Selections @code{gui-get-selection} is able to retrieve multiple different -kinds of selection data. However, the exact data types which Emacs -understands is not precisely specified and differs depending on the -window system on which Emacs is running. +kinds of selection data from any number of selections. However, the +data types and selections that Emacs understands is not precisely +specified and differs depending on the window system on which Emacs is +running. At the same time, @code{gui-set-selection} hides a great deal of complexity behind its back, at least on some systems: its @var{data} argument need not be a string, but is actually given verbatim to system specific code. - Emacs implements selections most completely on the X Window System. -This is both an artifact of history (X was the first window system -supported by Emacs) and one of technical reasons: instead of using -selections only to transfer text and multimedia content between -clients, X uses selections as a general inter-client communication -system, leading to a great proliferation of selection data types. + Emacs's implementation of selections is most complete on the X +Window System. This is both an artifact of history (X was the first +window system supported by Emacs) and one of technical reasons: +instead of using selections only to transfer text and multimedia +content between clients, X uses selections as a general inter-client +communication system, leading to a great proliferation of selection +data types. Even more confusingly, X also supports another inter-client communication mechanism: the Inter-Client Exchange. However, ICE is @@ -4097,17 +4099,18 @@ X Selections fixed number of selections. Selections are instead identified by X ``atoms'', which are unique 29-bit identifiers issued by the X server for a corresponding name. In Emacs, you can simply write a symbol -with name of the atom, and Emacs will transparently request these +with the name of the atom, and Emacs will transparently request these identifiers where necessary. - When a program ``sets'' a selection under X, it actually takes -ownership of the selection---the X server will then deliver selection -requests to the program, which is obliged to respond to the requesting -client with the selection data. + When a program ``sets'' a selection under X, it actually makes +itself the ``owner'' of the selection---the X server will then deliver +selection requests to the program, which is obliged to respond to the +requesting client with the selection data. Similarly, a program does not ``get'' selection data from the X server. Instead, its selection requests are sent to the client with -the window which last took ownership over the selection. +the window which last took ownership over the selection, which then +replies with the requested data. Each selection request contains three parameters: diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c index 5e4c8fc6c9f..de2a9253b57 100644 --- a/src/sfntfont-android.c +++ b/src/sfntfont-android.c @@ -52,6 +52,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. static char *system_font_directories[] = { (char *) "/system/fonts", + (char *) "/product/fonts", /* This should be filled in by init_sfntfont_android. */ (char[PATH_MAX]) { }, }; @@ -772,7 +773,7 @@ init_sfntfont_android (void) /* Set up the user fonts directory. This directory is ``fonts'' in the Emacs files directory. */ - snprintf (system_font_directories[1], PATH_MAX, "%s/fonts", + snprintf (system_font_directories[2], PATH_MAX, "%s/fonts", android_get_home_directory ()); } commit 562b2fca7d824a01d759bc7ade0e54ddd8497a0b Merge: 91da696bbc4 861cf3a5c9d Author: Po Lu Date: Thu Apr 13 07:32:27 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 91da696bbc43c39efeaeb70a86410d3652bee14d Merge: 933b5b51ab1 56095046858 Author: Po Lu Date: Wed Apr 12 07:22:03 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 933b5b51ab1be789aeef0b25e12e2f033d90ee3a Merge: 857e2bcb664 9efa6d2cf28 Author: Po Lu Date: Tue Apr 11 07:57:31 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 857e2bcb664bbfa6df7101e8f314d7a44d5d7f56 Merge: 23e963b6f0d b5c5e923dba Author: Po Lu Date: Mon Apr 10 08:16:44 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 23e963b6f0d7c402d3d0679e4dd4288fba882f55 Merge: e1261fff85e e33c0a54915 Author: Po Lu Date: Sun Apr 9 09:07:40 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit e1261fff85ee1edc7e51715c9093946d16041f28 Merge: 3695ae4d635 9848ae17161 Author: Po Lu Date: Sat Apr 8 09:35:59 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 3695ae4d6359b12dddd1ca59a4f4d9f8a38fbd04 Author: Po Lu Date: Sat Apr 8 09:35:19 2023 +0800 Document selections on Android more thoroughly * doc/lispref/elisp.texi (Top): * doc/lispref/frames.texi (Frames): Add ``Accessing Selections'' to menu. (Accessing Selections, X Selections, Other Selections): New nodes. diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi index a1d7b51b609..72441c8d442 100644 --- a/doc/lispref/elisp.texi +++ b/doc/lispref/elisp.texi @@ -1139,6 +1139,7 @@ Top * Dialog Boxes:: Displaying a box to ask yes or no. * Pointer Shape:: Specifying the shape of the mouse pointer. * Window System Selections::Transferring text to and from other X clients. +* Accessing Selections:: The multiple different kinds of selections. * Yanking Media:: Yanking things that aren't plain text. * Drag and Drop:: Internals of Drag-and-Drop implementation. * Color Names:: Getting the definitions of color names. diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi index 9083b4b01c9..7a752c1f6a9 100644 --- a/doc/lispref/frames.texi +++ b/doc/lispref/frames.texi @@ -108,6 +108,7 @@ Frames * Dialog Boxes:: Displaying a box to ask yes or no. * Pointer Shape:: Specifying the shape of the mouse pointer. * Window System Selections:: Transferring text to and from other X clients. +* Accessing Selections:: The multiple different kinds of selections. * Yanking Media:: Yanking things that aren't plain text. * Drag and Drop:: Internals of Drag-and-Drop implementation. * Color Names:: Getting the definitions of color names. @@ -4059,6 +4060,539 @@ Window System Selections names of @code{gui-get-selection} and @code{gui-set-selection} before Emacs 25.1. +@node Accessing Selections +@section Accessing Selections + + @code{gui-get-selection} is able to retrieve multiple different +kinds of selection data. However, the exact data types which Emacs +understands is not precisely specified and differs depending on the +window system on which Emacs is running. + + At the same time, @code{gui-set-selection} hides a great deal of +complexity behind its back, at least on some systems: its @var{data} +argument need not be a string, but is actually given verbatim to +system specific code. + + Emacs implements selections most completely on the X Window System. +This is both an artifact of history (X was the first window system +supported by Emacs) and one of technical reasons: instead of using +selections only to transfer text and multimedia content between +clients, X uses selections as a general inter-client communication +system, leading to a great proliferation of selection data types. + + Even more confusingly, X also supports another inter-client +communication mechanism: the Inter-Client Exchange. However, ICE is +only used by Emacs to communicate with session managers, and is a +separate topic. + +@menu +* X Selections:: Selection data types (and more) on X. +* Other Selections:: How they work on other window systems. +@end menu + +@node X Selections +@section X Selections + + X refrains from defining fixed data types for selection data, or a +fixed number of selections. Selections are instead identified by X +``atoms'', which are unique 29-bit identifiers issued by the X server +for a corresponding name. In Emacs, you can simply write a symbol +with name of the atom, and Emacs will transparently request these +identifiers where necessary. + + When a program ``sets'' a selection under X, it actually takes +ownership of the selection---the X server will then deliver selection +requests to the program, which is obliged to respond to the requesting +client with the selection data. + + Similarly, a program does not ``get'' selection data from the X +server. Instead, its selection requests are sent to the client with +the window which last took ownership over the selection. + + Each selection request contains three parameters: + +@itemize @bullet +@item +The window which requested the selection; this is used to identify the +@c Not a typo: X spells ``requestor'' with an o. +requesting program, otherwise known as the @dfn{requestor}. + +@item +An atom identifying the ``target'' to which the owner should convert +the selection. It is easiest to think of the conversion target as the +kind of data that the requestor wants: in selection requests made by +Emacs, the target is determined by the @dfn{type} argument to +@code{gui-get-selection}. + +@item +A 32-bit timestamp containing the X server time at which the requestor +last obtained input. +@end itemize + + The selection owner responds by tranferring to the requestor a +series of bytes, 16 bit words, or 32 bit words, along with another +atom identifying the type of those words. After requesting a +selection, Emacs then applies its own interpretation of the data +format and data type to convert the data transferred by the selection +owner to a Lisp representation, which @code{gui-get-selection} +returns. + + By default, Emacs converts selection data consisting of any series +of bytes to a unibyte string containing those bytes, selection data +consisting of a single 16-bit or 32-bit word as an unsigned number, +and selection data consisting of multiple such words as a vector of +unsigned numbers. However, Emacs applies special treatment for +several selection data types: + +@table @code +@item INTEGER +16-bit or 32-bit words of this type are treated as signed integers, +instead of unsigned ones. If there are multiple words in the +selection data, a vector is returned; otherwise, the integer is +returned by itself. + +@item ATOM +32-bit words of this type are treated as X atoms, and returned (either +alone or as vectors) as Lisp symbols containing the names they +identify. Invalid atoms are returned as @code{nil}. + +@item COMPOUND_TEXT +@item UTF8_STRING +@item STRING +Unibyte strings returned for these data types will have a single +@code{foreign-selection} text property set to a symbol with the type +of the selection data. +@end table + + Each selection owner must return at least two selection targets: +@code{TARGETS}, which returns a number of atoms describing the +selection targets that the owner supports, and @code{MULTIPLE}, used +for internal purposes by X clients. A selection owner may support any +number of other targets, some of which may be standardized by the X +Consortium's +@url{http://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html, +Inter-Client Communication Conventions Manual}, while others, such as +@code{UTF8_STRING}, were supposed to be standardized by the XFree86 +Project, which unfortunately did not happen. + + Requests for a given selection target may, by convention, return +data in a specific type, or it may return data in one of several +types, whichever is most convenient for the selection owner; the +latter type of selection target is dubbed a @dfn{polymorphic target}. +A selection target may also return no data at all: by convention, the +selection owner performs some action a side effect upon responding to +a selection request with that target, and as such these targets are +referred to as @dfn{side-effect targets}. + + Here are some selection targets which behave in a reasonably +standard manner when used with the @code{CLIPBOARD}, @code{PRIMARY}, +or @code{SECONDARY} selections. + +@table @code +@item ADOBE_PORTABLE_DOCUMENT_FORMAT +This target returns data in Adobe System's ``Portable Document +Format'' format, as a string. + +@item APPLE_PICT +This target returns data in the ``PICT'' image format used on +Macintosh computers, as a string. + +@item BACKGROUND +@item BITMAP +@item COLORMAP +@item FOREGROUND +Together, these four targets return integer data necessary to make use +of a bitmap image stored on the X server: the pixel value of the +bitmap's background color, the X identifier of the bitmap, the +colormap inside which the background and foreground are allocated, and +the pixel value of the bitmap's foreground color. + +@item CHARACTER_POSITION +This target returns two unsigned 32-bit integers of type @code{SPAN} +describing the start and end positions of the selection data in the +text field containing it, in bytes. + +@item COMPOUND_TEXT +This target returns a string of type @code{COMPOUND_TEXT} in the X +Consortium's multi-byte text encoding system. + +@item DELETE +This target returns nothing, but as a side-effect deletes the +selection contents from any text field containing them. + +@item DRAWABLE +@item PIXMAP +This target returns a list of unsigned 32-bit integers, each of which +corresponds to an X server drawable or pixmap. + +@item ENCAPSULATED_POSTSCRIPT +@item _ADOBE_EPS +This target returns a string containing encapsulated Postscript code. + +@item FILE_NAME +This target returns a string containing one or more file names, +separated by NULL characters. + +@item HOST_NAME +This target returns a string containing the fully-qualified domain +name of the machine on which the selection owner is running. + +@item USER +This target returns a string containing the user name of the machine +on which the selection owner is running. + +@item LENGTH +This target returns an unsigned 32-bit or 16-bit integer containing +the length of the selection data. + +@item LINE_NUMBER +This target returns two unsigned 32-bit integers of type @code{SPAN} +describing the line numbers corresponding to the start and end +positions of the selection data in the text field containing it. + +@item MODULE +This target returns the name of any function containing the selection +data. It is mainly used by text editors. + +@item STRING +This target returns the selection data as a string of type +@code{STRING}, encoded in ISO Latin-1 format, with Unix newline +characters. + +@item C_STRING +This target returns the selection data as a ``C string''. This has +been interpreted as meaning the raw selection data in whatever +encoding used by the owner, either terminated with a NULL byte or not +at all, or an ASCII string which may or may not be terminated. + +@item UTF8_STRING +This returns the selection data as a string of type +@code{UTF8_STRING}, encoded in UTF-8, with unspecified EOL format. + +@item TIMESTAMP +This target returns the X server time at which the selection owner +took ownership over the selection as a 16-bit or 32-bit word of type +@code{CARDINAL}. + +@item TEXT +This polymorphic target returns selection data as a string, either +@code{COMPOUND_TEXT}, @code{STRING}, @code{C_STRING}, or +@code{UTF8_STRING}, whichever data type is convenient for the +selection owner. +@end table + + When a request for the targets @code{STRING}, @code{COMPOUND_TEXT}, +or @code{UTF8_STRING} is made using the function +@code{gui-get-selection}, and neither @code{selection-coding-system} +nor @code{next-selection-coding-system} are set, the returned strings +are additionally decoded using the appropriate coding system for those +data types: @code{iso-8859-1}, @code{compound-text-with-extensions} +and @code{utf-8} respectively. + + In addition to the targets specified above (and the many targets +used by various programs for their own purposes), several popular +programs and toolkits have decided to define selection data types of +their own, without consulting the appropriate X standards bodies. +These targets are usually named after MIME types, such as +@code{text/html} or @code{image/jpeg}, and have been known to contain: + +@itemize @bullet +@item +Unterminated, newline terminated, or NULL character terminated file +names of an image or text file. + +@item +Image or text data in the appropriate format. + +@item +@code{file://} URIs (or possibly newline or NUL terminated lists of +URIs) leading to files in the appropriate format. +@end itemize + + These selection targets were first used by Netscape, but are now +found in all kinds of programs, especially those based on recent +versions of the GTK+ or Qt toolkits. + + Emacs is also capable of acting as a selection owner. When +@code{gui-set-selection} is called, the selection data specified is +not transferred to the X server; instead, Emacs records it internally +and obtains ownership of the selection. + +@defvar selection-converter-alist + Alist of selection targets to ``selection converter'' functions. +When a selection request is received, Emacs looks up the selection +converter associated with the requested selection target. + + The selection converter is called with three arguments: the symbol +corresponding to the atom identifying the selection being requested, +the selection target that is being requested, and the value set with +@code{gui-set-selection}. The value which it returns is either a cons +of a symbol specifying the data type and a number, symbol, or a vector +of numbers or symbols, or its cdr by itself. + + If the value is the special symbol @code{NULL}, the data type is set +to @code{NULL}, and no data is returned to the requestor. + + If the value is a string, it must be a unibyte string; should no +data type be explicitly specified, the data is transferred to the +requestor with the type @code{STRING}. + + If the value is a symbol, its ``atom'' is retrieved, and it is +transferred to the requestor as a 32-bit value---if no data type was +specified, its type is @code{ATOM}. + + If the value is a number between @code{-32769} and @code{32768}, it +is transferred to the requestor as a 16 bit value---if no data type +was specified, its type is @code{INTEGER}. + + If the value is any other number, it is returned as a 32 bit value. +Even if the number returned is unsigned, the requestor will treat +words of type @code{INTEGER} as signed. To return an unsigned value, +specify the type @code{CARDINAL} instead. + + If the value is a vector of symbols or numbers, it is returned as a +list of multiple atoms or numbers. The data type returned by default +is determined by that of its first element. +@end defvar + + By default, Emacs is configured with selection converters for the +following selection targets: + +@table @code +@item TEXT +This selection converter returns selection data as: + +@itemize @bullet +@item +A string of type @code{C_STRING}, if the selection contents contain no +multibyte characters, or contains 8-bit characters with all 8 bits +set. + +@item +A string of type @code{STRING}, if the selection contents can be +represented as ISO-Latin-1 text. + +@item +A string of type @code{COMPOUND_TEXT}, if the selection contents can +be encoded in the X Consortium's Compound Text Encoding, and +@code{selection-coding-system} or @code{next-selection-coding-system} +is set to a coding system whose @code{:mime-charset} property is +@code{x-ctext}. + +@item +A string of type @code{UTF8_STRING} otherwise. +@end itemize + +@item COMPOUND_TEXT +This selection converter returns selection data as a string of type +@code{COMPOUND_TEXT}. + +@item STRING +This selection converter returns selection data as a string of type +@code{STRING}, encoded in ISO-Latin-1 format. + +@item UTF8_STRING +This selection converter returns selection data in UTF-8 format. + +@item text/plain +@item text/plain;charset=utf-8 +@item text/uri-list +@item text/x-xdnd-username +@item XmTRANSFER_SUCCESS +@item XmTRANSFER_FAILURE +@item FILE +@item _DT_NETFILE +These selection converters are used for internal purposes during +drag-and-drop operations and are not available for selections other +than @code{XdndSelection}. + +@item TARGETS +This selection converter returns a list of atoms, one for each +selection target understood by Emacs. + +@item MULTIPLE +This selection converter is implemented in C code and is used to +implement efficient transfer of selection requests which specify +multiple selection targets at the same time. + +@item LENGTH +This selection converter returns the length of the selection data, in +bytes. + +@item DELETE +This selection converter is used for internal purposes during +drag-and-drop operations. + +@item FILE_NAME +This selection converter returns the file name of the buffer +containing the selection data. + +@item CHARACTER_POSITION +This selection converter returns the character positions of each end +of the selection in the buffer containing the selection data. + +@item LINE_NUMBER +@item COLUMN_NUMBER +This selection converter returns the line and column numbers of each +end of the selection in the buffer containing the selection data. + +@item OWNER_OS +This selection converter returns the name of the operating system on +which Emacs is running. + +@item HOST_NAME +This selection converter returns the fully-qualified domain name of +the machine on which Emacs is running. + +@item USER +This selection converter returns the username of the user account +under which Emacs is running. + +@item CLASS +@item NAME +These selection converters return the resource class and name used by +Emacs. + +@item INTEGER +This selection converter returns an integer value verbatim. + +@item SAVE_TARGETS +@item _EMACS_INTERNAL +These selection converters are used for internal purposes. +@end table + + With the exception of @code{INTEGER}, all selection converters +expect the value given to @code{gui-set-selection} to be one of the +following: + +@itemize @bullet +@item +A string. + +@item +A list of the form @w{@code{(@var{beg} @var{end} @var{buf})}}, where +@var{beg} and @var{end} are two markers or overlays describing the +bounds of the selection data in the buffer @var{buf}. +@end itemize + +@node Other Selections +@section Other Selections + + Window systems such as MS-Windows, Nextstep, Haiku and Android do +not provide selections corresponding to the X semantics. Each window +system provides its own ad-hoc emulation of selections, none of which +make use of the ``selection converter'' mechanism described above. In +addition, only the @code{PRIMARY}, @code{CLIPBOARD}, and +@code{SECONDARY} selections are typically supported, alongside the +@code{XdndSelection} used for drag-and-drop operations. + + GTK itself exposes emulations of X selections to applications, but +those emulations are of varying completeness. While Emacs built with +PGTK will use the same selection interface as Emacs built with X, many +selection targets will not be useful. + + On MS-Windows, @code{gui-get-selection} accepts a single target, +@code{STRING}. The value returned is the selection data decoded +using @code{selection-coding-system}. + + @code{gui-set-selection} also only accepts strings, encodes them +in the selection coding system, and saves them to the clipboard. + + On Nextstep, Emacs only supports saving strings to selections. +However, requests for the following targets are accepted: + +@c FIXME: how is the text coding system determined, and do image/* or +@c application/* return image data or file names? +@itemize @bullet +@item text/plain +@item image/png +@item text/html +@item application/pdf +@item application/rtf +@item application/rtfd +@item STRING +@item text/plain +@item image/tiff +@end itemize + + On Haiku, Emacs supports the same selection values as on X. In +addition, Emacs fully implements the primary and secondary selections. +However, instead of taking ownership over the selection data, Emacs +transfers the selection data to the window server when +@code{gui-set-selection} is called. The Haiku window server expects +selection data to be provided in the form of a ``message'', containing +associations between data types and selection data. + +@defvar haiku-normal-selection-encoders +List of functions which act as selection encoders. When +@code{gui-set-selection} is called, each function in this list is +successively called with its @var{selection} and @var{value} +arguments. If the function returns non-@code{nil}, it should return a +list of the form @w{@code{(@var{key} @var{type} @var{value})}}, where +@var{key} is the name of the data type being transferred, @var{type} +is either a number identifying a data type (in which case @var{value} +should be a unibyte string that is directly transferred to the window +server), or a symbol identifying both a data type and how @var{value} +should be interpreted. +@end defvar + + Here are the meaningful values of @var{type}, and what they will +cause Emacs to interpret @var{value} as: + +@table @code +@item string +A unibyte string. The string is NULL-terminated after being placed in +the message. + +@item ref +A file name. The file is looked up and file system information +identifying the file is placed in the message. + +@item short +A 16-bit integer value. + +@item long +A 32-bit integer value. + +@item llong +A 64-bit integer value. + +@item byte +@item char +An unsigned byte between 0 and 255. + +@item size_t +A number between 0 and 1 minus two to the power of the word size of +the computer Emacs is running on. + +@item ssize_t +A number which fits in the C type @code{ssize_t}. + +@item point +A cons of two floats, specifying a coordinate on-screen. + +@item float +@item double +A single or double-precision floating point number in an unspecified +format. + +@item (haiku-numeric-enum MIME) +A unibyte string containing data in a certain MIME type. +@end table + + Under Haiku, @code{gui-get-selection} accepts either the targets +@code{TARGETS} and @code{TIMESTAMP}, where the former returns a vector +containing supported data types (much like on X), and the latter +returns the number of times the selection has been set, the targets +@code{STRING} and @code{UTF8_STRING}, which return text in ISO-Latin-1 +and UTF-8 format, or a MIME type, in which the data is returned +undecoded as a unibyte string. + + Under Android, @code{gui-get-selection} is restricted to returning +UTF-8 string data of the type @code{STRING}, or image and application +data associated with a MIME type. @code{gui-set-selection} will only +set string data, as on MS-Windows. + @node Yanking Media @section Yanking Media commit 6abed48e5c20559d63617a13fd379aaf5580594d Merge: 65987f85061 5e5f5b28e92 Author: Po Lu Date: Fri Apr 7 08:25:54 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 65987f85061a5f607d73f5dfe9446dacdd57da86 Merge: 3b07a4b3158 fa669c4b17c Author: Po Lu Date: Thu Apr 6 09:56:34 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 3b07a4b3158d024c6eb19ce0e7c67b799ae0d1fc Author: Po Lu Date: Thu Apr 6 09:56:23 2023 +0800 Implement `yank-media' on Android * doc/emacs/android.texi (Android Windowing): Update selection restrictions. * java/org/gnu/emacs/EmacsClipboard.java (EmacsClipboard): New functions `getClipboardTargets' and `getClipboardData'. * java/org/gnu/emacs/EmacsSdk11Clipboard.java (EmacsSdk11Clipboard, getClipboardTargets, getClipboardData): Implement. * java/org/gnu/emacs/EmacsSdk8Clipboard.java: Stub out new functions. * lisp/term/android-win.el (android-get-clipboard-1): Implement MIME type targets. * src/android.c (android_exception_check) (android_exception_check_1, android_exception_check_2): Fix punctuation in warning message. (android_exception_check_nonnull_1): New function. * src/android.h: Update prototypes. * src/androidselect.c (struct android_emacs_clipboard): New methods. (android_init_emacs_clipboard): Initialize new methods. (Fandroid_get_clipboard_targets, android_xfree_inside) (Fandroid_get_clipboard_data): New functions. (syms_of_androidselect): Define new subrs. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 97f72e98cee..67faea7f76d 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -476,7 +476,7 @@ Android Windowing Emacs does not implement all selection related features supported under the X Window System on Android. For example, only the @code{CLIPBOARD} and @code{PRIMARY} selections (@pxref{Cut and Paste}) -are supported, and plain text is the only supported data type. +are supported, and Emacs is only able to set selections to plain text. In addition, the Android system itself places certain restrictions on what selection data Emacs can access: diff --git a/java/org/gnu/emacs/EmacsClipboard.java b/java/org/gnu/emacs/EmacsClipboard.java index cd6bcebfe0e..5cd48af6e3a 100644 --- a/java/org/gnu/emacs/EmacsClipboard.java +++ b/java/org/gnu/emacs/EmacsClipboard.java @@ -31,6 +31,9 @@ public abstract class EmacsClipboard public abstract boolean clipboardExists (); public abstract byte[] getClipboard (); + public abstract byte[][] getClipboardTargets (); + public abstract long[] getClipboardData (byte[] target); + /* Create the correct kind of clipboard for this system. */ public static EmacsClipboard diff --git a/java/org/gnu/emacs/EmacsSdk11Clipboard.java b/java/org/gnu/emacs/EmacsSdk11Clipboard.java index a05184513cd..4959ec36eed 100644 --- a/java/org/gnu/emacs/EmacsSdk11Clipboard.java +++ b/java/org/gnu/emacs/EmacsSdk11Clipboard.java @@ -21,12 +21,20 @@ import android.content.ClipboardManager; import android.content.Context; +import android.content.ContentResolver; import android.content.ClipData; +import android.content.ClipDescription; + +import android.content.res.AssetFileDescriptor; + +import android.net.Uri; import android.util.Log; import android.os.Build; +import java.io.FileNotFoundException; +import java.io.IOException; import java.io.UnsupportedEncodingException; /* This class implements EmacsClipboard for Android 3.0 and later @@ -40,6 +48,7 @@ public final class EmacsSdk11Clipboard extends EmacsClipboard private boolean ownsClipboard; private int clipboardChangedCount; private int monitoredClipboardChangedCount; + private ContentResolver resolver; public EmacsSdk11Clipboard () @@ -51,6 +60,11 @@ public final class EmacsSdk11Clipboard extends EmacsClipboard if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) manager.addPrimaryClipChangedListener (this); + + /* Now obtain the content resolver used to open file + descriptors. */ + + resolver = EmacsService.SERVICE.getContentResolver (); } @Override @@ -157,4 +171,120 @@ public final class EmacsSdk11Clipboard extends EmacsClipboard return null; } + + /* Return an array of targets currently provided by the + clipboard, or NULL if there are none. */ + + @Override + public byte[][] + getClipboardTargets () + { + ClipData clip; + ClipDescription description; + byte[][] typeArray; + int i; + + /* N.B. that Android calls the clipboard the ``primary clip''; it + is not related to the X primary selection. */ + clip = manager.getPrimaryClip (); + description = clip.getDescription (); + i = description.getMimeTypeCount (); + typeArray = new byte[i][i]; + + try + { + for (i = 0; i < description.getMimeTypeCount (); ++i) + typeArray[i] = description.getMimeType (i).getBytes ("UTF-8"); + } + catch (UnsupportedEncodingException exception) + { + return null; + } + + return typeArray; + } + + /* Return the clipboard data for the given target, or NULL if it + does not exist. + + Value is normally an array of three longs: the file descriptor, + the start offset of the data, and its length; length may be + AssetFileDescriptor.UNKOWN_LENGTH, meaning that the data extends + from that offset to the end of the file. + + Do not use this function to open text targets; use `getClipboard' + for that instead, as it will handle selection data consisting + solely of a URI. */ + + @Override + public long[] + getClipboardData (byte[] target) + { + ClipData data; + String mimeType; + int fd; + AssetFileDescriptor assetFd; + Uri uri; + long[] value; + + /* Decode the target given by Emacs. */ + try + { + mimeType = new String (target, "UTF-8"); + } + catch (UnsupportedEncodingException exception) + { + return null; + } + + Log.d (TAG, "getClipboardData: "+ mimeType); + + /* Now obtain the clipboard data and the data corresponding to + that MIME type. */ + + data = manager.getPrimaryClip (); + + if (data.getItemCount () < 1) + return null; + + try + { + uri = data.getItemAt (0).getUri (); + + if (uri == null) + return null; + + Log.d (TAG, "getClipboardData: "+ uri); + + /* Now open the file descriptor. */ + assetFd = resolver.openTypedAssetFileDescriptor (uri, mimeType, + null); + + /* Duplicate the file descriptor. */ + fd = assetFd.getParcelFileDescriptor ().getFd (); + fd = EmacsNative.dup (fd); + + /* Return the relevant information. */ + value = new long[] { fd, assetFd.getStartOffset (), + assetFd.getLength (), }; + + /* Close the original offset. */ + assetFd.close (); + + Log.d (TAG, "getClipboardData: "+ value); + } + catch (FileNotFoundException e) + { + return null; + } + catch (IOException e) + { + return null; + } + + /* Don't return value if the file descriptor couldn't be + created. */ + + return fd != -1 ? value : null; + } }; diff --git a/java/org/gnu/emacs/EmacsSdk8Clipboard.java b/java/org/gnu/emacs/EmacsSdk8Clipboard.java index 5a40128b0ac..9622641810f 100644 --- a/java/org/gnu/emacs/EmacsSdk8Clipboard.java +++ b/java/org/gnu/emacs/EmacsSdk8Clipboard.java @@ -115,4 +115,33 @@ public final class EmacsSdk8Clipboard extends EmacsClipboard return null; } + + /* Return an array of targets currently provided by the + clipboard, or NULL if there are none. */ + + @Override + public byte[][] + getClipboardTargets () + { + return null; + } + + /* Return the clipboard data for the given target, or NULL if it + does not exist. + + Value is normally an array of three longs: the file descriptor, + the start offset of the data, and its length; length may be + AssetFileDescriptor.UNKOWN_LENGTH, meaning that the data extends + from that offset to the end of the file. + + Do not use this function to open text targets; use `getClipboard' + for that instead, as it will handle selection data consisting + solely of a URI. */ + + @Override + public long[] + getClipboardData (byte[] target) + { + return null; + } }; diff --git a/lisp/term/android-win.el b/lisp/term/android-win.el index c7610ae2ca3..d425ea401a9 100644 --- a/lisp/term/android-win.el +++ b/lisp/term/android-win.el @@ -70,6 +70,8 @@ handle-args-function (declare-function android-get-clipboard "androidselect.c") (declare-function android-set-clipboard "androidselect.c") (declare-function android-clipboard-owner-p "androidselect.c") +(declare-function android-get-clipboard-targets "androidselect.c") +(declare-function android-get-clipboard-data "androidselect.c") (defvar android-primary-selection nil "The last string placed in the primary selection. @@ -80,13 +82,25 @@ android-primary-selection (defun android-get-clipboard-1 (data-type) "Return the clipboard data. -DATA-TYPE is a selection conversion target; only STRING and -TARGETS are supported." +DATA-TYPE is a selection conversion target. `STRING' means to +return the contents of the clipboard as a string. `TARGETS' +means to return supported data types as a vector. + +Interpret any other symbol as a MIME type, and return its +corresponding data." (or (and (eq data-type 'STRING) (android-get-clipboard)) (and (eq data-type 'TARGETS) (android-clipboard-exists-p) - [TARGETS STRING]))) + (vconcat [TARGETS STRING] + (let ((i nil)) + (dolist (type (android-get-clipboard-targets)) + ;; Don't report plain text as a valid target. + (unless (equal type "text/plain") + (push (intern type) i))) + (nreverse i)))) + (and (symbolp data-type) + (android-get-clipboard-data (symbol-name data-type))))) (defun android-get-primary (data-type) "Return the last string placed in the primary selection, or nil. diff --git a/src/android.c b/src/android.c index 1da8bec316e..7852590acf4 100644 --- a/src/android.c +++ b/src/android.c @@ -5530,7 +5530,7 @@ android_exception_check (void) if ((*android_java_env)->ExceptionCheck (android_java_env)) { __android_log_print (ANDROID_LOG_WARN, __func__, - "Possible out of memory error." + "Possible out of memory error. " " The Java exception follows: "); /* Describe exactly what went wrong. */ (*android_java_env)->ExceptionDescribe (android_java_env); @@ -5549,7 +5549,7 @@ android_exception_check_1 (jobject object) if ((*android_java_env)->ExceptionCheck (android_java_env)) { __android_log_print (ANDROID_LOG_WARN, __func__, - "Possible out of memory error." + "Possible out of memory error. " " The Java exception follows: "); /* Describe exactly what went wrong. */ (*android_java_env)->ExceptionDescribe (android_java_env); @@ -5568,7 +5568,7 @@ android_exception_check_2 (jobject object, jobject object1) if ((*android_java_env)->ExceptionCheck (android_java_env)) { __android_log_print (ANDROID_LOG_WARN, __func__, - "Possible out of memory error." + "Possible out of memory error. " " The Java exception follows: "); /* Describe exactly what went wrong. */ (*android_java_env)->ExceptionDescribe (android_java_env); @@ -5600,6 +5600,27 @@ android_exception_check_nonnull (void *object, jobject object1) memory_full (0); } +/* Check for JNI problems based on the value of OBJECT. + + Signal out of memory if OBJECT is NULL. OBJECT1 and OBJECT2 mean + the same as in `android_exception_check_2'. */ + +void +android_exception_check_nonnull_1 (void *object, jobject object1, + jobject object2) +{ + if (object) + return; + + if (object1) + ANDROID_DELETE_LOCAL_REF (object1); + + if (object2) + ANDROID_DELETE_LOCAL_REF (object2); + + memory_full (0); +} + /* Native image transforms. */ diff --git a/src/android.h b/src/android.h index 03592bd955d..24666aaf989 100644 --- a/src/android.h +++ b/src/android.h @@ -88,6 +88,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. extern void android_exception_check_1 (jobject); extern void android_exception_check_2 (jobject, jobject); extern void android_exception_check_nonnull (void *, jobject); +extern void android_exception_check_nonnull_1 (void *, jobject, jobject); extern void android_get_keysym_name (int, char *, size_t); extern void android_wait_event (void); diff --git a/src/androidselect.c b/src/androidselect.c index dfbe0240941..54c712ca93b 100644 --- a/src/androidselect.c +++ b/src/androidselect.c @@ -19,6 +19,8 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include +#include +#include #include "lisp.h" #include "blockinput.h" @@ -27,12 +29,15 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include "androidterm.h" /* Selection support on Android is confined to copying and pasting of - plain text from the clipboard. There is no primary selection. + plain text and MIME data from the clipboard. There is no primary + selection. While newer versions of Android are supposed to have the necessary interfaces for transferring other kinds of selection data, doing so is too complicated, and involves registering ``content providers'' - and all kinds of other stuff. */ + and all kinds of other stuff; for this reason, Emacs does not + support setting the clipboard contents to anything other than plain + text. */ @@ -46,6 +51,8 @@ Copyright (C) 2023 Free Software Foundation, Inc. jmethodID clipboard_exists; jmethodID get_clipboard; jmethodID make_clipboard; + jmethodID get_clipboard_targets; + jmethodID get_clipboard_data; }; /* Methods associated with the EmacsClipboard class. */ @@ -86,6 +93,10 @@ #define FIND_METHOD(c_name, name, signature) \ FIND_METHOD (owns_clipboard, "ownsClipboard", "()I"); FIND_METHOD (clipboard_exists, "clipboardExists", "()Z"); FIND_METHOD (get_clipboard, "getClipboard", "()[B"); + FIND_METHOD (get_clipboard_targets, "getClipboardTargets", + "()[[B"); + FIND_METHOD (get_clipboard_data, "getClipboardData", + "([B)[J"); clipboard_class.make_clipboard = (*android_java_env)->GetStaticMethodID (android_java_env, @@ -244,6 +255,210 @@ DEFUN ("android-browse-url", Fandroid_browse_url, +/* MIME clipboard support. This provides support for reading MIME + data (but not text) from the clipboard. */ + +DEFUN ("android-get-clipboard-targets", Fandroid_get_clipboard_targets, + Sandroid_get_clipboard_targets, 0, 0, 0, + doc: /* Return a list of data types in the clipboard. +Value is a list of MIME types as strings, each defining a single extra +data type available from the clipboard. */) + (void) +{ + jarray bytes_array; + jbyteArray bytes; + jmethodID method; + size_t length, length1, i; + jbyte *data; + Lisp_Object targets, tem; + + if (!android_init_gui) + error ("No Android display connection!"); + + targets = Qnil; + block_input (); + method = clipboard_class.get_clipboard_targets; + bytes_array = (*android_java_env)->CallObjectMethod (android_java_env, + clipboard, method); + android_exception_check (); + + if (!bytes_array) + goto fail; + + length = (*android_java_env)->GetArrayLength (android_java_env, + bytes_array); + for (i = 0; i < length; ++i) + { + /* Retireve the MIME type. */ + bytes + = (*android_java_env)->GetObjectArrayElement (android_java_env, + bytes_array, i); + android_exception_check_nonnull (bytes, bytes_array); + + /* Cons it onto the list of targets. */ + length1 = (*android_java_env)->GetArrayLength (android_java_env, + bytes); + data = (*android_java_env)->GetByteArrayElements (android_java_env, + bytes, NULL); + android_exception_check_nonnull_1 (data, bytes, bytes_array); + + /* Decode the string. */ + tem = make_unibyte_string ((char *) data, length1); + tem = code_convert_string_norecord (tem, Qutf_8, Qnil); + targets = Fcons (tem, targets); + + /* Delete the retrieved data. */ + (*android_java_env)->ReleaseByteArrayElements (android_java_env, + bytes, data, + JNI_ABORT); + ANDROID_DELETE_LOCAL_REF (bytes); + } + unblock_input (); + + ANDROID_DELETE_LOCAL_REF (bytes_array); + return Fnreverse (targets); + + fail: + unblock_input (); + return Qnil; +} + +/* Free the memory inside PTR, a pointer to a char pointer. */ + +static void +android_xfree_inside (void *ptr) +{ + xfree (*(char **) ptr); +} + +DEFUN ("android-get-clipboard-data", Fandroid_get_clipboard_data, + Sandroid_get_clipboard_data, 1, 1, 0, + doc: /* Return the clipboard data of the given MIME TYPE. +Value is a unibyte string containing the entire contents of the +clipboard, after its owner has converted the data to the given +MIME type. Value is nil if the conversion fails, or if the data +is not present. + +Value is also nil if the clipboard data consists of a single URL which +does not have any corresponding data. In that case, use +`android-get-clipboard' instead. */) + (Lisp_Object type) +{ + jlongArray array; + jbyteArray bytes; + jmethodID method; + int fd; + ptrdiff_t rc; + jlong offset, length, *longs; + specpdl_ref ref; + char *buffer, *start; + + if (!android_init_gui) + error ("No Android display connection!"); + + /* Encode the string as UTF-8. */ + CHECK_STRING (type); + type = ENCODE_UTF_8 (type); + + /* Then give it to the selection code. */ + block_input (); + bytes = (*android_java_env)->NewByteArray (android_java_env, + SBYTES (type)); + (*android_java_env)->SetByteArrayRegion (android_java_env, bytes, + 0, SBYTES (type), + (jbyte *) SDATA (type)); + android_exception_check (); + + method = clipboard_class.get_clipboard_data; + array = (*android_java_env)->CallObjectMethod (android_java_env, + clipboard, method, + bytes); + android_exception_check_1 (bytes); + ANDROID_DELETE_LOCAL_REF (bytes); + + if (!array) + goto fail; + + longs = (*android_java_env)->GetLongArrayElements (android_java_env, + array, NULL); + android_exception_check_nonnull (longs, array); + + /* longs[0] is the file descriptor. + longs[1] is an offset to apply to the file. + longs[2] is either -1, or the number of bytes to read from the + file. */ + fd = longs[0]; + offset = longs[1]; + length = longs[2]; + + (*android_java_env)->ReleaseLongArrayElements (android_java_env, + array, longs, + JNI_ABORT); + ANDROID_DELETE_LOCAL_REF (array); + unblock_input (); + + /* Now begin reading from longs[0]. */ + ref = SPECPDL_INDEX (); + record_unwind_protect_int (close_file_unwind, fd); + + if (length != -1) + { + buffer = xmalloc (MIN (length, PTRDIFF_MAX)); + record_unwind_protect_ptr (xfree, buffer); + + rc = emacs_read_quit (fd, buffer, + MIN (length, PTRDIFF_MAX)); + + /* Return nil upon an IO problem. */ + if (rc < 0) + return unbind_to (ref, Qnil); + + /* Return the data as a unibyte string. */ + return unbind_to (ref, make_unibyte_string (buffer, rc)); + } + + /* Otherwise, read BUFSIZ bytes at a time. */ + buffer = xmalloc (BUFSIZ); + length = 0; + start = buffer; + + record_unwind_protect_ptr (android_xfree_inside, &buffer); + + /* Seek to the start of the data. */ + + if (offset) + { + if (lseek (fd, offset, SEEK_SET) < 0) + return unbind_to (ref, Qnil); + } + + while (true) + { + rc = emacs_read_quit (fd, start, BUFSIZ); + + if (!INT_ADD_OK (rc, length, &length) + || PTRDIFF_MAX - length < BUFSIZ) + memory_full (PTRDIFF_MAX); + + if (rc < 0) + return unbind_to (ref, Qnil); + + if (rc < BUFSIZ) + break; + + buffer = xrealloc (buffer, length + BUFSIZ); + start = buffer + length; + } + + return unbind_to (ref, make_unibyte_string (buffer, rc)); + + fail: + unblock_input (); + return Qnil; +} + + + void init_androidselect (void) { @@ -279,4 +494,6 @@ syms_of_androidselect (void) defsubr (&Sandroid_get_clipboard); defsubr (&Sandroid_clipboard_exists_p); defsubr (&Sandroid_browse_url); + defsubr (&Sandroid_get_clipboard_targets); + defsubr (&Sandroid_get_clipboard_data); } commit 458c6e5c9171f41f327ef88f4a4999db586f8e91 Merge: fb87f7a9050 30692f16b15 Author: Po Lu Date: Wed Apr 5 07:58:05 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit fb87f7a90507189794aa2dd789b15092fb86e077 Author: Po Lu Date: Tue Apr 4 18:35:11 2023 +0800 ; * lisp/subr.el (read-char-from-minibuffer): Fix typo. diff --git a/lisp/subr.el b/lisp/subr.el index 819aba631fa..3761ee6764b 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3471,8 +3471,8 @@ read-char-from-minibuffer ;; If text conversion is enabled in this buffer, then it will only ;; be disabled the next time `force-mode-line-update' happens. - (when (and (bound-and-true-p 'overriding-text-conversion-style) - (bound-and-true-p 'text-conversion-style)) + (when (and (bound-and-true-p overriding-text-conversion-style) + (bound-and-true-p text-conversion-style)) (force-mode-line-update)) (let* ((overriding-text-conversion-style nil) commit 6bdbb4cbfc2deb7d3a02e1428768e101f3dbd265 Author: Po Lu Date: Tue Apr 4 18:34:44 2023 +0800 Update Android port * lisp/subr.el (read-char-from-minibuffer): Don't use undefined variable. Reported by Robert Pluim. diff --git a/lisp/subr.el b/lisp/subr.el index 45953f07f13..819aba631fa 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3471,8 +3471,8 @@ read-char-from-minibuffer ;; If text conversion is enabled in this buffer, then it will only ;; be disabled the next time `force-mode-line-update' happens. - (when (and overriding-text-conversion-style - text-conversion-style) + (when (and (bound-and-true-p 'overriding-text-conversion-style) + (bound-and-true-p 'text-conversion-style)) (force-mode-line-update)) (let* ((overriding-text-conversion-style nil) commit 76a8347e43b6c57f032469cc14764aebd02e6ff3 Merge: 4a7facac1bb bd5c1d1cbbd Author: Po Lu Date: Tue Apr 4 07:49:48 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 4a7facac1bb67837866da3ca3c4573d176e29585 Merge: add40ccfb99 9a8d96da5c5 Author: Po Lu Date: Mon Apr 3 18:39:10 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit add40ccfb997e9e2576631804fc96df65ddfb156 Author: Po Lu Date: Mon Apr 3 18:24:02 2023 +0800 ; ; * src/androidselect.c (Fandroid_clipboard_exists_p): Add check. diff --git a/src/androidselect.c b/src/androidselect.c index f54a6d6f52c..dfbe0240941 100644 --- a/src/androidselect.c +++ b/src/androidselect.c @@ -208,6 +208,9 @@ DEFUN ("android-clipboard-exists-p", Fandroid_clipboard_exists_p, jboolean rc; jmethodID method; + if (!android_init_gui) + error ("No Android display connection"); + method = clipboard_class.clipboard_exists; rc = (*android_java_env)->CallBooleanMethod (android_java_env, clipboard, commit 0e0ea976cadb950258d7f3754f8fe581d092e36a Author: Po Lu Date: Mon Apr 3 10:23:33 2023 +0800 Update Android port * src/sfnt.c (sfnt_normalize_vector): Don't rely on undefined sign extension semantics. diff --git a/src/sfnt.c b/src/sfnt.c index f30093ba765..800adc19214 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -8241,8 +8241,8 @@ sfnt_normalize_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy, goto fail; /* Long division.. eek! */ - vector->x = (sfnt_div_fixed (vx * 1024, magnitude) >> 2); - vector->y = (sfnt_div_fixed (vy * 1024, magnitude) >> 2); + vector->x = (sfnt_div_fixed (vx * 1024, magnitude) / 4); + vector->y = (sfnt_div_fixed (vy * 1024, magnitude) / 4); } /* Compute a unit vector describing the direction of a line from the commit 8bca62d552a3ca5dc262f916055d9d443d360af8 Merge: 1d84b4b2865 c108132d3bb Author: Po Lu Date: Mon Apr 3 08:12:03 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 1d84b4b28659b5f2cc60872ce095e3887bed6fdd Merge: aeac57fbbd7 97e35b14987 Author: Po Lu Date: Sun Apr 2 09:11:00 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit aeac57fbbd7b0a1fcc27230f8e44374bd93b1602 Merge: ca53e2bfbc8 4bd1fc59664 Author: Po Lu Date: Sat Apr 1 14:26:45 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit ca53e2bfbc8a72fd372b3aa08520375345bede59 Merge: 05f8fe3ae30 c10c545ef26 Author: Po Lu Date: Sat Apr 1 14:25:00 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 05f8fe3ae30675d44121563edf9f368f9ace9d9d Merge: ab3ef576b4c 6523359dfe2 Author: Po Lu Date: Sat Apr 1 08:57:39 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit ab3ef576b4c529aad7461283cea9bda67c7c8bcd Merge: 956cb8ca65b 560c27a332c Author: Po Lu Date: Fri Mar 31 09:14:17 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 956cb8ca65b58f5e885d072c60aebfdf5a95784b Author: Po Lu Date: Thu Mar 30 13:04:24 2023 +0800 Tweak outline cache stuff * src/sfntfont.c: Adjust font cache size constants. diff --git a/src/sfntfont.c b/src/sfntfont.c index 960abe0d270..71399b890d2 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -1875,8 +1875,8 @@ sfntfont_match (struct frame *f, Lisp_Object font_spec) enum { - SFNT_OUTLINE_CACHE_SIZE = 128, - SFNT_RASTER_CACHE_SIZE = 100, + SFNT_OUTLINE_CACHE_SIZE = 256, + SFNT_RASTER_CACHE_SIZE = 128, }; /* Caching subsystem. Generating outlines from glyphs is expensive, commit 8bf461bd36920b438c6ec60c2ae7750e7f9c755b Author: Po Lu Date: Thu Mar 30 11:25:15 2023 +0800 ; * src/sfnt.c (GETINFO): Fix typo. diff --git a/src/sfnt.c b/src/sfnt.c index 564f5d883bd..f30093ba765 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -6511,7 +6511,8 @@ #define GETINFO() \ if (selector & 1) \ k |= 02; \ \ - if (selector & 8 && interpreter->n_axis) \ + if (selector & 8 \ + && interpreter->norm_coords) \ k |= 02000; \ \ PUSH_UNCHECKED (k); \ commit 8719ad534bb482039fca2b234d39750b49e80222 Author: Po Lu Date: Thu Mar 30 11:22:11 2023 +0800 ; * src/sfnt.h: Fix typo. diff --git a/src/sfnt.h b/src/sfnt.h index 58a6de060f4..365595fa37d 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -1924,8 +1924,8 @@ #define PROTOTYPE int, struct sfnt_offset_subtable * #define PROTOTYPE \ struct sfnt_maxp_table *, \ struct sfnt_cvt_table *, \ - struct sfnt_fvar_table *, \ struct sfnt_head_table *, \ + struct sfnt_fvar_table *, \ int, int extern struct sfnt_interpreter *sfnt_make_interpreter (PROTOTYPE); commit 89a30637b32edea461746023717f77bd87bb4b10 Author: Po Lu Date: Thu Mar 30 11:18:51 2023 +0800 Update Android port * src/sfnt.c (sfnt_make_interpreter): New argument `fvar'. Set axis count. (SCANCTRL): Implement selector bit 8. (GXAXIS): New instruction. (SFVTPV): Validate graphics state after changing freedom vector. (sfnt_line_to_vector): Implement `original'. (sfnt_move): Remove redundant division. (sfnt_interpret_run): Implement distortable font related GXAXIS instruction (0x91). (sfnt_vary_interpreter): Set naxis and norm_coords. (sfnt_make_test_interpreter, pushb_test_args, pushw_test_args) (sfnt_name_instruction, main): Adjust accordingly. * src/sfnt.h (struct sfnt_interpreter, PROTOTYPE): * src/sfntfont.c (sfntfont_setup_interpreter, sfntfont_open): Set up distortion information. diff --git a/src/sfnt.c b/src/sfnt.c index 4da0997751d..564f5d883bd 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -5445,10 +5445,10 @@ sfnt_init_graphics_state (struct sfnt_graphics_state *state) } /* Set up an interpreter to be used with a font. Use the resource - limits specified in the MAXP table, the values specified in the CVT - and HEAD tables, the pixel size PIXEL_SIZE, and the point size - POINT_SIZE. CVT may be NULL, in which case the interpreter will - not have access to a control value table. + limits specified in the MAXP table, the values specified in the + CVT, HEAD and FVAR tables, the pixel size PIXEL_SIZE, and the point + size POINT_SIZE. CVT may be NULL, in which case the interpreter + will not have access to a control value table. POINT_SIZE should be PIXEL_SIZE, converted to 1/72ths of an inch. @@ -5459,6 +5459,7 @@ sfnt_init_graphics_state (struct sfnt_graphics_state *state) sfnt_make_interpreter (struct sfnt_maxp_table *maxp, struct sfnt_cvt_table *cvt, struct sfnt_head_table *head, + struct sfnt_fvar_table *fvar, int pixel_size, int point_size) { size_t size, temp, i, storage_size, pad; @@ -5613,6 +5614,18 @@ sfnt_make_interpreter (struct sfnt_maxp_table *maxp, /* Fill in the current call depth. */ interpreter->call_depth = 0; + /* Clear variation axes. They will be set upon a call to + `sfnt_vary_interpreter'. */ + interpreter->n_axis = 0; + interpreter->norm_coords = NULL; + + /* Set n_axis now if a fvar table was provided. This way, GXAXIS + pushes the correct number of values even if no blend is + provided. */ + + if (fvar) + interpreter->n_axis = fvar->axis_count; + /* Return the interpreter. */ return interpreter; } @@ -6483,16 +6496,25 @@ #define SCANCTRL() \ interpreter->state.scan_control = value; \ } +/* Selector bit 8 is undocumented, but present in the Macintosh + rasterizer. 02000 is returned if there is a variation axis in + use. */ + #define GETINFO() \ { \ - uint32_t selector; \ + uint32_t selector, k; \ \ selector = POP (); \ \ + k = 0; \ + \ if (selector & 1) \ - PUSH_UNCHECKED (2) \ - else \ - PUSH_UNCHECKED (0) \ + k |= 02; \ + \ + if (selector & 8 && interpreter->n_axis) \ + k |= 02000; \ + \ + PUSH_UNCHECKED (k); \ } #define IDEF() \ @@ -6563,6 +6585,25 @@ #define INSTCTRL() \ |= (1 << s); \ } +/* GXAXIS is undocumented. It seems to return each axis in shortFrac + format. */ + +#define GXAXIS() \ + { \ + uint32_t v; \ + int i; \ + \ + for (i = 0; i < interpreter->n_axis; ++i) \ + { \ + if (interpreter->norm_coords) \ + v = interpreter->norm_coords[i] / 4; \ + else \ + v = 0; \ + \ + PUSH (v); \ + } \ + } + #define PUSHB() \ { \ int b, nbytes, IP; \ @@ -6943,6 +6984,8 @@ #define SFVTPV() \ { \ interpreter->state.freedom_vector \ = interpreter->state.projection_vector; \ + \ + sfnt_validate_gs (&interpreter->state); \ } #define ISECT() \ @@ -8231,6 +8274,16 @@ sfnt_line_to_vector (struct sfnt_interpreter *interpreter, sfnt_address_zp1 (interpreter, p1, &x1, &y1, &original_x1, &original_y1); + /* Use original coordinates if specified. */ + + if (original) + { + x2 = original_x2; + y2 = original_y2; + x1 = original_x1; + y1 = original_y1; + } + /* Calculate the vector between X2, Y2, and X1, Y1. */ a = sfnt_sub (x1, x2); b = sfnt_sub (y1, y2); @@ -9392,7 +9445,7 @@ sfnt_move (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y, size_t n, struct sfnt_interpreter *interpreter, sfnt_f26dot6 distance, unsigned char *flags) { - sfnt_f26dot6 versor; + sfnt_f26dot6 versor, k; sfnt_f2dot14 dot_product; size_t num; @@ -9412,12 +9465,13 @@ sfnt_move (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y, /* Move along X axis, converting the distance to the freedom vector. */ num = n; + k = sfnt_multiply_divide_signed (distance, + versor, + dot_product); while (num--) { - *x = sfnt_add (*x, sfnt_multiply_divide_signed (distance, - versor, - dot_product)); + *x = sfnt_add (*x, k); x++; if (flags) @@ -9432,12 +9486,13 @@ sfnt_move (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y, /* Move along X axis, converting the distance to the freedom vector. */ num = n; + k = sfnt_multiply_divide_signed (distance, + versor, + dot_product); while (num--) { - *y = sfnt_add (*y, sfnt_multiply_divide_signed (distance, - versor, - dot_product)); + *y = sfnt_add (*y, k); y++; if (flags) @@ -10747,6 +10802,10 @@ sfnt_interpret_run (struct sfnt_interpreter *interpreter, NOT_IMPLEMENTED (); break; + case 0x91: /* GXAXIS */ + GXAXIS (); + break; + default: if (opcode >= 0xE0) /* MIRP */ { @@ -14895,7 +14954,8 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, } /* Vary the specified INTERPRETER's control value table using the - variations in BLEND's CVT variations table. + variations in BLEND's CVT variations table, then record the blend's + normalized coordinates and axis count in the interpreter. The CVT table used to create INTERPRETER must be the same used to read BLEND->cvar. If not, behavior is undefined. */ @@ -14953,6 +15013,9 @@ sfnt_vary_interpreter (struct sfnt_interpreter *interpreter, interpreter->cvt[i] += delta; } } + + interpreter->n_axis = blend->fvar->axis_count; + interpreter->norm_coords = blend->norm_coords; } @@ -15443,7 +15506,7 @@ sfnt_make_test_interpreter (void) return sfnt_make_interpreter (&test_interpreter_profile, &test_interpreter_cvt, &test_interpreter_head, - 17, 17); + NULL, 17, 17); } struct sfnt_interpreter_test @@ -15938,7 +16001,7 @@ sfnt_check_instctrl (struct sfnt_interpreter *interpreter, static struct sfnt_generic_test_args pushb_test_args = { (uint32_t []) { 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, - 1U, }, + 1U, }, 9, true, 11, @@ -15947,7 +16010,7 @@ sfnt_check_instctrl (struct sfnt_interpreter *interpreter, static struct sfnt_generic_test_args pushw_test_args = { (uint32_t []) { 0x203U, 0x204U, 0x205U, 0x206U, 0x207U, 0x208U, - 0x909U, 0x909U, (uint32_t) -1, }, + 0x909U, 0x909U, (uint32_t) -1, }, 9, true, 20, @@ -18347,7 +18410,7 @@ sfnt_name_instruction (unsigned char opcode) "7 INS_$8F", "7 INS_$90", - "7 INS_$91", + "7 GXAXIS", "7 INS_$92", "7 INS_$93", "7 INS_$94", @@ -18924,8 +18987,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 36 -#define EASY_PPEM 36 +#define FANCY_PPEM 19 +#define EASY_PPEM 19 interpreter = NULL; head = sfnt_read_head_table (fd, font); @@ -19085,7 +19148,7 @@ #define EASY_PPEM 36 loca_short->num_offsets); } - interpreter = sfnt_make_interpreter (maxp, cvt, head, + interpreter = sfnt_make_interpreter (maxp, cvt, head, fvar, FANCY_PPEM, FANCY_PPEM); if (instance && gvar) sfnt_vary_interpreter (interpreter, &blend); @@ -19296,7 +19359,7 @@ #define EASY_PPEM 36 cvt ? cvt->num_elements : 0ul); interpreter = sfnt_make_interpreter (maxp, cvt, head, - FANCY_PPEM, + fvar, FANCY_PPEM, FANCY_PPEM); state = interpreter->state; diff --git a/src/sfnt.h b/src/sfnt.h index 30c82ad3795..58a6de060f4 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -1860,6 +1860,12 @@ #define PROTOTYPE \ /* What was the trap. */ const char *trap_reason; + /* Number of variation axes provided by this distortable font. */ + int n_axis; + + /* Normalized axis coordinates set for this distortable font. */ + sfnt_fixed *norm_coords; + #ifdef TEST /* If non-NULL, function called before each instruction is executed. */ @@ -1918,6 +1924,7 @@ #define PROTOTYPE int, struct sfnt_offset_subtable * #define PROTOTYPE \ struct sfnt_maxp_table *, \ struct sfnt_cvt_table *, \ + struct sfnt_fvar_table *, \ struct sfnt_head_table *, \ int, int diff --git a/src/sfntfont.c b/src/sfntfont.c index c9d48f640af..960abe0d270 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -2532,7 +2532,7 @@ sfntfont_probe_widths (struct sfnt_font_info *font_info) /* Initialize the instruction interpreter for INFO. Load the font and preprogram for the pixel size in INFO and its corresponding point - size POINT_SIZE. + size POINT_SIZE. Use the FVAR table in DESC. The font tables in INFO must already have been initialized. @@ -2541,6 +2541,7 @@ sfntfont_probe_widths (struct sfnt_font_info *font_info) static void sfntfont_setup_interpreter (struct sfnt_font_info *info, + struct sfnt_font_desc *desc, int point_size) { struct sfnt_cvt_table *cvt; @@ -2575,6 +2576,7 @@ sfntfont_setup_interpreter (struct sfnt_font_info *info, info->head. CVT can be NULL. */ interpreter = sfnt_make_interpreter (info->maxp, cvt, info->head, + desc->tables->fvar, info->font.pixel_size, point_size); @@ -3110,7 +3112,7 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, point_size = PIXEL_TO_POINT (pixel_size, (dpyinfo->resx * dpyinfo->resy / 2)); - sfntfont_setup_interpreter (font_info, point_size); + sfntfont_setup_interpreter (font_info, desc, point_size); /* If an instance was specified and the font is distortable, set up the blend. */ commit 804b76ba8db20d65106394f08e807bdc93c2c55d Merge: 219bfea874b bfa3500c3c6 Author: Po Lu Date: Thu Mar 30 09:14:02 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 219bfea874b237caa763a31f3e9261733fab8e68 Author: Po Lu Date: Wed Mar 29 20:14:43 2023 +0800 Improve rules for enumerating user fonts * doc/emacs/android.texi (Android Fonts): Document distortable font replacement rules. * src/sfntfont.c (sfnt_replace_fonts_p): New function. (sfnt_enum_font_1): Call it. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index c92700980de..97f72e98cee 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -534,10 +534,11 @@ Android Fonts named @code{sfnt-android} and @code{android}. Upon startup, Emacs enumerates all the TrueType format fonts in the -directory @file{/system/fonts}, and the @file{fonts} directory inside -the Emacs home directory. Emacs assumes there will always be a font -named ``Droid Sans Mono'', and then defaults to using this font. -These fonts are then displayed by the @code{sfnt-android} font driver. +directory @file{/system/fonts}, and the @file{fonts} directory +(@dfn{user fonts directory}) inside the Emacs home directory. Emacs +assumes there will always be a font named ``Droid Sans Mono'', and +then defaults to using this font. These fonts are then displayed by +the @code{sfnt-android} font driver. When running on Android, Emacs currently lacks support for OpenType fonts. This means that only a subset of the fonts installed on the @@ -551,6 +552,22 @@ Android Fonts the ``Monospace'' typeface configured on your system; this should always be Droid Sans Mono. +@cindex TrueType GX fonts, android +@cindex distortable fonts, android + + Like on X systems, Emacs supports distortable fonts under Android. +These fonts (also termed ``TrueType GX fonts'', ``variable fonts'', +and ``multiple master fonts'') provide multiple different styles +(``Bold'', ``Italic'', etc) using a single font file. + +When a user-installed distortable font is found, each font that a +previously discovered font provided will no longer be used. In +addition, any previously specified distortable fonts with the same +family name are also removed. When a conventional font is found, any +previous conventional font with the same style and family will be +removed; distortable fonts with the same family will no longer be +used to provide that style. + @node Android Troubleshooting @section What to do when something goes wrong on Android @cindex troubleshooting, android diff --git a/src/sfntfont.c b/src/sfntfont.c index 99ba6bb1001..c9d48f640af 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -827,6 +827,63 @@ sfnt_grok_registry (int fd, struct sfnt_font_desc *desc, xfree (subtables); } +/* Return whether or not the font description PREV conflicts with the + newer font description DESC, and should be removed from the list of + system fonts. + + If PREV is a variable font, potentially adjust its list of + instances. */ + +static bool +sfnt_replace_fonts_p (struct sfnt_font_desc *prev, + struct sfnt_font_desc *desc) +{ + int i, width, weight, slant, count_instance; + Lisp_Object tem; + bool family_equal_p; + + family_equal_p = !NILP (Fstring_equal (prev->family, + desc->family)); + + if ((!NILP (desc->instances) + || !NILP (Fstring_equal (prev->style, desc->style))) + && family_equal_p) + return true; + + if (NILP (prev->instances) || !family_equal_p) + return false; + + /* Look through instances in PREV to see if DESC provides the same + thing. */ + + count_instance = 0; + for (i = 0; i < ASIZE (prev->instances); ++i) + { + tem = AREF (prev->instances, i); + + if (NILP (tem)) + continue; + + width = XFIXNUM (AREF (tem, 2)); + weight = XFIXNUM (AREF (tem, 3)); + slant = XFIXNUM (AREF (tem, 4)); + + if (desc->width == width + && desc->weight == weight + && desc->slant == slant) + { + /* Remove this instance. */ + ASET (prev->instances, i, Qnil); + continue; + } + + count_instance++; + } + + /* Remove this desc if there are no more instances. */ + return count_instance < 1; +} + /* Enumerate the offset subtable SUBTABLES in the file FD, whose file name is FILE. OFFSET should be the offset of the subtable within the font file, and is recorded for future use. Value is 1 upon @@ -959,14 +1016,15 @@ sfnt_enum_font_1 (int fd, const char *file, desc->next = system_fonts; system_fonts = desc; - /* Remove any fonts which have the same style as this one. */ + /* Remove any fonts which have the same style as this one. For + distortable fonts, only remove overlapping styles, unless this is + also a distortable font. */ next = &system_fonts->next; prev = *next; for (; *next; prev = *next) { - if (!NILP (Fstring_equal (prev->style, desc->style)) - && !NILP (Fstring_equal (prev->family, desc->family))) + if (sfnt_replace_fonts_p (prev, desc)) { *next = prev->next; xfree (prev); commit 2686a55aa121b7e0bdd0d98c9b3e660df25c9bcf Author: Po Lu Date: Wed Mar 29 14:50:55 2023 +0800 Fix optimized move functions * src/sfnt.c (sfnt_move_x): (sfnt_move_y): (sfnt_move): Set N flags and don't forget to set N points too. diff --git a/src/sfnt.c b/src/sfnt.c index 11b632ca555..4da0997751d 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -9344,7 +9344,7 @@ sfnt_dual_project_onto_any_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy, } /* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom - vector. Set *FLAGS where appropriate and when non-NULL. + vector. Set N flags in *FLAGS where appropriate and when non-NULL. Assume both vectors are aligned to the X axis. */ @@ -9354,14 +9354,17 @@ sfnt_move_x (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y, sfnt_f26dot6 distance, unsigned char *flags) { while (n--) - *x = sfnt_add (*x, distance); + { + *x = sfnt_add (*x, distance); + x++; - if (flags) - *flags |= SFNT_POINT_TOUCHED_X; + if (flags) + *flags++ |= SFNT_POINT_TOUCHED_X; + } } /* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom - vector. Set *FLAGS where appropriate and when non-NULL. + vector. Set N flags in *FLAGS where appropriate and when non-NULL. Assume both vectors are aligned to the Y axis. */ @@ -9371,14 +9374,18 @@ sfnt_move_y (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y, sfnt_f26dot6 distance, unsigned char *flags) { while (n--) - *y = sfnt_add (*y, distance); + { + *y = sfnt_add (*y, distance); + y++; - if (flags) - *flags |= SFNT_POINT_TOUCHED_Y; + if (flags) + *flags++ |= SFNT_POINT_TOUCHED_Y; + } } /* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom - vector. Set *FLAGS where appropriate and when non-NULL. */ + vector. Set N flags in *FLAGS where appropriate and when + non-NULL. */ static void sfnt_move (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y, @@ -9412,10 +9419,10 @@ sfnt_move (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y, versor, dot_product)); x++; - } - if (flags) - *flags |= SFNT_POINT_TOUCHED_X; + if (flags) + *flags++ |= SFNT_POINT_TOUCHED_X; + } } versor = interpreter->state.freedom_vector.y; @@ -9432,10 +9439,10 @@ sfnt_move (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y, versor, dot_product)); y++; - } - if (flags) - *flags |= SFNT_POINT_TOUCHED_Y; + if (flags) + *flags++ |= SFNT_POINT_TOUCHED_Y; + } } } commit fa6ac5ed1ca938aee6e7efb63231c16e039054a2 Author: Po Lu Date: Wed Mar 29 13:45:18 2023 +0800 ; * src/sfnt.c (sfnt_read_avar_table): Fix sequencing problem. diff --git a/src/sfnt.c b/src/sfnt.c index 55739ced915..11b632ca555 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -13001,11 +13001,12 @@ sfnt_read_avar_table (int fd, struct sfnt_offset_subtable *subtable) /* Verify that words from here to buffer[1 + buffer[k] * 2], the next pairCount field, are within bounds. */ - if (k + 1 + buffer[k] * 2 > size / sizeof *buffer) + j = k + 1 + buffer[k] * 2; + if (j > size / sizeof *buffer) goto bail1; /* Move to the next pairCount field. */ - k += 1 + buffer[k] * 2; + k = j; } /* Resize avar to min_size and start filling in various commit 2dc037e4a80c2b8fe9411fcbd420524247b00b38 Merge: 279efc6cc2a f24aa0f46af Author: Po Lu Date: Wed Mar 29 13:24:34 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 279efc6cc2ab167eb0c791b3b138d249c49778d7 Author: Po Lu Date: Wed Mar 29 13:23:32 2023 +0800 Update Android port * src/sfntfont.c (sfntfont_setup_interpreter): Don't create interpreter for blatently broken fonts. diff --git a/src/sfntfont.c b/src/sfntfont.c index 40272247116..99ba6bb1001 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -2504,6 +2504,15 @@ sfntfont_setup_interpreter (struct sfnt_font_info *info, if (!fpgm && !prep) goto bail; + /* If the interpreter does not use the operand stack at all, it is + useless. In addition, some broken fonts specify some unnecessary + instructions in prep and set head->max_stack_elements to 0. + + Don't create the interpreter in that case. */ + + if (!info->maxp->max_stack_elements) + goto bail; + /* Now, create the interpreter using the limits in info->maxp and info->head. CVT can be NULL. */ commit 67da02444ee4ad0950bfe3b5ad531822e6818ff5 Author: Po Lu Date: Wed Mar 29 09:44:45 2023 +0800 Update Android port * src/sfntfont.c (sfntfont_open): Avoid specifying redundant blends. diff --git a/src/sfntfont.c b/src/sfntfont.c index 3476da6734e..40272247116 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -3068,6 +3068,22 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, sfnt_normalize_blend (&font_info->blend); + /* Test whether or not the instance is actually redundant, + as all of its axis are at their default values. If so, + free the instance. */ + + for (i = 0; i < desc->tables->fvar->axis_count; ++i) + { + if (font_info->blend.norm_coords[i]) + break; + } + + if (i == desc->tables->fvar->axis_count) + { + sfnt_free_blend (&font_info->blend); + goto cancel_blend; + } + /* If an interpreter was specified, distort it now. */ if (font_info->interpreter) @@ -3089,6 +3105,7 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, } } + cancel_blend: /* Calculate the xfld name. */ font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil); commit 13e1fb4c3f7f969f45ccbec1cfc001d76690d680 Merge: af6af86d08e 2002ac376c9 Author: Po Lu Date: Wed Mar 29 08:42:19 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit af6af86d08ec05b3a599af0a0c98ab1d2b81eaf3 Author: Po Lu Date: Wed Mar 29 08:33:43 2023 +0800 Update Android port * src/sfnt.c (sfnt_validate_gs): Fix validation of projection vector. diff --git a/src/sfnt.c b/src/sfnt.c index d2c726367cb..55739ced915 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -7567,7 +7567,7 @@ sfnt_move_zp1 (struct sfnt_interpreter *interpreter, uint32_t point, } /* Move N points starting from the specified POINT in the zone - addressed by INTERPRETER's ZP1 register by the given DISTANCE along + addressed by INTERPRETER's ZP2 register by the given DISTANCE along the freedom vector. No checking is done to ensure that POINT lies inside the zone, or @@ -9526,7 +9526,7 @@ sfnt_validate_gs (struct sfnt_graphics_state *gs) aligned to an axis. */ if (gs->freedom_vector.x == 040000 - && gs->projection_vector.y == 040000) + && gs->projection_vector.x == 040000) gs->move = sfnt_move_x; else if (gs->freedom_vector.y == 040000 && gs->projection_vector.y == 040000) commit f92413a79649ad65e132b450468c933bb1972642 Author: Po Lu Date: Tue Mar 28 19:21:06 2023 +0800 Update Android port * src/sfnt.c (sfnt_vary_compound_glyph): * src/sfntfont.c (sfntfont_get_glyph) (sfntfont_get_glyph_outline): Avoid clobbering offset size flag when varying compound glyph. diff --git a/src/sfnt.c b/src/sfnt.c index d2d3500a18d..d2c726367cb 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -14549,8 +14549,6 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, uint16_t *local_points, n_local_points; sfnt_fixed scale; ptrdiff_t data_offset; - bool *touched; - sfnt_fword *restrict original_x, *restrict original_y; struct sfnt_compound_glyph_component *component; gvar = blend->gvar; @@ -14628,12 +14626,6 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, intermediate_start = coords + gvar->axis_count; intermediate_end = coords + gvar->axis_count; - /* Allocate arrays of booleans and fwords to keep track of which - points have been touched. */ - touched = NULL; - original_x = NULL; - original_y = NULL; - while (ntuples--) { data = gvar->glyph_variation_data + offset + data_offset; @@ -14775,7 +14767,6 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, word = component->argument1.d; fword = sfnt_mul_fixed_round (dx[i], scale); - component->flags |= 01; component->argument1.d = word + fword; /* Vary the Y offset. */ @@ -14786,6 +14777,8 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, word = component->argument2.d; fword = sfnt_mul_fixed_round (dy[i], scale); + + /* Set the flag that says offsets are words. */ component->flags |= 01; component->argument2.d = word + fword; } @@ -14804,36 +14797,6 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* Deltas are only applied for each point number read. */ - if (!original_x) - { - if ((glyph->compound->num_components - * sizeof *touched) >= 1024 * 16) - touched = xmalloc (sizeof *touched - * glyph->compound->num_components); - else - touched = alloca (sizeof *touched - * glyph->compound->num_components); - - if ((sizeof *original_x * 2 - * glyph->compound->num_components) >= 1024 * 16) - original_x = xmalloc (sizeof *original_x * 2 - * glyph->compound->num_components); - else - original_x = alloca (sizeof *original_x * 2 - * glyph->compound->num_components); - - original_y = original_x + glyph->compound->num_components; - memcpy (original_x, glyph->simple->x_coordinates, - (sizeof *original_x - * glyph->compound->num_components)); - memcpy (original_y, glyph->simple->y_coordinates, - (sizeof *original_y - * glyph->compound->num_components)); - } - - memset (touched, 0, (sizeof *touched - * glyph->compound->num_components)); - for (i = 0; i < point_count; ++i) { /* Apply deltas to phantom points. */ @@ -14868,7 +14831,6 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, word = component->argument1.d; fword = sfnt_mul_fixed_round (dx[i], scale); - component->flags |= 01; component->argument1.d = word + fword; /* Vary the Y offset. */ @@ -14879,6 +14841,8 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, word = component->argument2.d; fword = sfnt_mul_fixed_round (dy[i], scale); + + /* Set the flag that says offsets are words. */ component->flags |= 01; component->argument2.d = word + fword; } @@ -14895,17 +14859,9 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* Return success. */ - if ((glyph->compound->num_components - * sizeof *touched) >= 1024 * 16) - xfree (touched); - if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) xfree (coords); - if ((sizeof *original_x * 2 - * glyph->compound->num_components) >= 1024 * 16) - xfree (original_x); - if (points != (uint16_t *) -1) xfree (points); @@ -14921,17 +14877,9 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, xfree (local_points); fail1: - if ((glyph->compound->num_components - * sizeof *touched) >= 1024 * 16) - xfree (touched); - if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) xfree (coords); - if ((sizeof *original_x * 2 - * glyph->compound->num_components) >= 1024 * 16) - xfree (original_x); - if (points != (uint16_t *) -1) xfree (points); diff --git a/src/sfntfont.c b/src/sfntfont.c index daf8f54c03c..3476da6734e 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -1886,18 +1886,14 @@ sfntfont_get_glyph (sfnt_glyph glyph_id, void *dcontext, tables->loca_short, tables->loca_long); - if (!tables->blend || !glyph) - return glyph; - - if ((glyph->simple - && sfnt_vary_simple_glyph (tables->blend, glyph_id, - glyph, &distortion)) - || (!glyph->simple - && sfnt_vary_compound_glyph (tables->blend, glyph_id, - glyph, &distortion))) + if (tables->blend && glyph) { - sfnt_free_glyph (glyph); - return NULL; + if (glyph->simple) + sfnt_vary_simple_glyph (tables->blend, glyph_id, glyph, + &distortion); + else + sfnt_vary_compound_glyph (tables->blend, glyph_id, glyph, + &distortion); } /* Note that the distortion is not relevant for compound glyphs. */ @@ -2022,14 +2018,11 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, return NULL; } } - else if (!glyph->simple) + else if (sfnt_vary_compound_glyph (blend, glyph_code, + glyph, &distortion)) { - if (sfnt_vary_compound_glyph (blend, glyph_code, - glyph, &distortion)) - { - sfnt_free_glyph (glyph); - return NULL; - } + sfnt_free_glyph (glyph); + return NULL; } } commit bf28a372ab6aad2d18e0417ac85a4aad14e4a9d7 Author: Po Lu Date: Tue Mar 28 16:34:50 2023 +0800 Update Android port * src/sfnt.c (sfnt_vary_simple_glyph, sfnt_vary_compound_glyph): Fix application of intermediate tuples. * src/sfntfont.c (sfntfont_open): Set xlfd name after applying distortion. diff --git a/src/sfnt.c b/src/sfnt.c index 2762f3df457..d2d3500a18d 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -14331,7 +14331,7 @@ sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* See whether or not the tuple applies to the current variation configuration, and how much to scale them by. */ - scale = sfnt_compute_tuple_scale (blend, index & 0x1000, + scale = sfnt_compute_tuple_scale (blend, index & 0x4000, coords, intermediate_start, intermediate_end); @@ -14705,7 +14705,7 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* See whether or not the tuple applies to the current variation configuration, and how much to scale them by. */ - scale = sfnt_compute_tuple_scale (blend, index & 0x1000, + scale = sfnt_compute_tuple_scale (blend, index & 0x4000, coords, intermediate_start, intermediate_end); diff --git a/src/sfntfont.c b/src/sfntfont.c index f9965ef13f1..daf8f54c03c 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -3045,9 +3045,6 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, font_info->font.underline_position = -1; font_info->font.underline_thickness = 0; - /* Calculate the xfld name. */ - font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil); - /* Now try to set up grid fitting for this font. */ dpyinfo = FRAME_DISPLAY_INFO (f); point_size = PIXEL_TO_POINT (pixel_size, (dpyinfo->resx @@ -3099,6 +3096,9 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, } } + /* Calculate the xfld name. */ + font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil); + #ifdef HAVE_HARFBUZZ /* HarfBuzz will potentially read font tables after the font has been opened by Emacs. Keep the font open, and record its offset commit 2826287b1a3d6661535d631732abd122d460b9fa Author: Po Lu Date: Tue Mar 28 13:39:10 2023 +0800 Correctly round lbearing values * src/sfnt.h (SFNT_ROUND_FIXED): * src/sfntfont.c (sfntfont_probe_widths): (sfntfont_measure_pcm): Round lbearing properly. diff --git a/src/sfnt.h b/src/sfnt.h index fb8cb80bae9..30c82ad3795 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -1325,9 +1325,8 @@ #define sfnt_coerce_fixed(fixed) ((sfnt_fixed) (fixed) / 65535.0) -#define SFNT_CEIL_FIXED(fixed) \ - (!((fixed) & 0177777) ? (fixed) \ - : ((fixed) + 0200000) & 037777600000) +#define SFNT_CEIL_FIXED(fixed) (((fixed) + 0177777) & 037777600000) +#define SFNT_FLOOR_FIXED(fixed) ((fixed) & 037777600000) diff --git a/src/sfntfont.c b/src/sfntfont.c index 9c0910d18ed..f9965ef13f1 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -2460,7 +2460,7 @@ sfntfont_probe_widths (struct sfnt_font_info *font_info) num_characters++; /* Add the advance to total_width. */ - total_width += metrics.advance / 65536; + total_width += SFNT_CEIL_FIXED (metrics.advance) / 65536; /* Update min_width if it hasn't been set yet or is wider. */ if (font_info->font.min_width == 1 @@ -3183,7 +3183,8 @@ sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, if (!outline) return 1; - pcm->lbearing = metrics.lbearing / 65536; + /* Round the left side bearing downwards. */ + pcm->lbearing = SFNT_FLOOR_FIXED (metrics.lbearing) / 65536; pcm->rbearing = SFNT_CEIL_FIXED (outline->xmax) / 65536; /* Round the advance, ascent and descent upwards. */ commit b3d100734b666bfe0541acfef7b8023a4edae455 Author: Po Lu Date: Tue Mar 28 12:03:47 2023 +0800 Update Android port * src/sfntfont.c (sfnt_open_tables): Fix typos in non-HarfBuzz code. diff --git a/src/sfntfont.c b/src/sfntfont.c index ee049b5c159..9c0910d18ed 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -2756,17 +2756,6 @@ sfnt_open_tables (struct sfnt_font_desc *desc) if (!tables->hmtx) goto bail5; -#ifdef HAVE_HARFBUZZ - /* Now copy over the subtable if necessary, as it is needed to read - extra font tables required by HarfBuzz. */ - tables->directory = subtable; - tables->fd = fd; -#else /* !HAVE_HARFBUZZ */ - /* Otherwise, close the fd and free the table directory. */ - xfree (subtable); - emacs_close (fd); -#endif /* HAVE_HARFBUZZ */ - /* Read instruction related font tables. These might not be present, which is OK, since instructing fonts is optional. */ tables->prep = sfnt_read_prep_table (fd, subtable); @@ -2782,6 +2771,17 @@ sfnt_open_tables (struct sfnt_font_desc *desc) tables->cvar = sfnt_read_cvar_table (fd, subtable, tables->fvar, tables->cvt); +#ifdef HAVE_HARFBUZZ + /* Now copy over the subtable if necessary, as it is needed to read + extra font tables required by HarfBuzz. */ + tables->directory = subtable; + tables->fd = fd; +#else /* !HAVE_HARFBUZZ */ + /* Otherwise, close the fd and free the table directory. */ + xfree (subtable); + emacs_close (fd); +#endif /* HAVE_HARFBUZZ */ + return tables; bail5: commit 64206ee3af9b59da9591aae400439bdea2cda09f Merge: 05f3f9c1c09 66b43944615 Author: Po Lu Date: Tue Mar 28 09:41:22 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 05f3f9c1c09c3e7a4d9c1d9ac16a34742a4124c1 Author: Po Lu Date: Tue Mar 28 09:41:01 2023 +0800 Update Android port * src/androidterm.c (android_draw_image_glyph_string): Restore potentially clobbered GC clipping. * src/sfnt.c (sfnt_large_integer_add, sfnt_multiply_divide_round) (sfnt_mul_fixed_round): New functions. (sfnt_build_glyph_outline): Take unscaled glyph metrics. (sfnt_prepare_raster, sfnt_vary_simple_glyph) (sfnt_vary_compound_glyph, sfnt_vary_interpreter): Use rounding multiplication to scale deltas. (main): Adjust tests. * src/sfntfont.c (sfntfont_get_glyph_outline) (sfntfont_probe_widths, sfntfont_open, sfntfont_measure_pcm) (sfntfont_draw): More minor fixes to variable fonts. diff --git a/src/androidterm.c b/src/androidterm.c index 1bc11e600f7..8ba7fb6a798 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -3327,6 +3327,7 @@ android_draw_image_glyph_string (struct glyph_string *s) /* Draw the foreground. */ android_draw_image_foreground (s); + android_set_glyph_string_clipping (s); /* If we must draw a relief around the image, do it. */ if (s->img->relief diff --git a/src/sfnt.c b/src/sfnt.c index f6730857f86..2762f3df457 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -3539,6 +3539,40 @@ sfnt_multiply_divide (unsigned int a, unsigned int b, unsigned int c) #endif } +#ifndef INT64_MAX + +/* Add the specified unsigned 32-bit N to the large integer + INTEGER. */ + +static void +sfnt_large_integer_add (struct sfnt_large_integer *integer, + uint32_t n) +{ + struct sfnt_large_integer number; + + number.low = integer->low + n; + number.high = integer->high + (number.low + < integer->low); + + *integer = number; +} + +/* Calculate (A * B) / C, rounding the result with a threshold of N. + Use a 64 bit temporary. */ + +static unsigned int +sfnt_multiply_divide_round (unsigned int a, unsigned int b, + unsigned int n, unsigned int c) +{ + struct sfnt_large_integer temp; + + sfnt_multiply_divide_1 (a, b, &temp); + sfnt_large_integer_add (&temp, n); + return sfnt_multiply_divide_2 (&temp, c); +} + +#endif /* INT64_MAX */ + /* The same as sfnt_multiply_divide, but handle signed values instead. */ @@ -3591,6 +3625,36 @@ sfnt_mul_fixed (sfnt_fixed x, sfnt_fixed y) #endif } +/* Multiply the two 16.16 fixed point numbers X and Y, with rounding + of the result. */ + +static sfnt_fixed +sfnt_mul_fixed_round (sfnt_fixed x, sfnt_fixed y) +{ +#ifdef INT64_MAX + int64_t product, round; + + product = (int64_t) x * (int64_t) y; + round = product < 0 ? -32768 : 32768; + + /* This can be done quickly with int64_t. */ + return (product + round) / (int64_t) 65536; +#else + int sign; + + sign = 1; + + if (x < 0) + sign = -sign; + + if (y < 0) + sign = -sign; + + return sfnt_multiply_divide_round (abs (x), abs (y), + 32768, 65536) * sign; +#endif +} + /* Set the pen size to the specified point and return. POINT will be scaled up to the pixel size. */ @@ -3766,7 +3830,7 @@ sfnt_curve_to_and_build (struct sfnt_point control, SCALE is a scale factor that converts between em space and device space. - Use the scaled glyph METRICS to determine the origin point of the + Use the unscaled glyph METRICS to determine the origin point of the outline. Call GET_GLYPH and FREE_GLYPH with the specified DCONTEXT to obtain @@ -3824,11 +3888,12 @@ sfnt_build_glyph_outline (struct sfnt_glyph *glyph, return NULL; } - /* Compute the origin position. */ - origin = outline->xmin - metrics->lbearing; - outline->origin - = (origin + sfnt_mul_fixed (glyph->origin_distortion, - build_outline_context.factor)); + /* Compute the origin position. Note that the original glyph xmin + is first used to calculate the origin point, and the origin + distortion is applied to it to get the distorted origin. */ + + origin = glyph->xmin - metrics->lbearing + glyph->origin_distortion; + outline->origin = sfnt_mul_fixed (origin, scale); return outline; } @@ -3881,10 +3946,10 @@ sfnt_prepare_raster (struct sfnt_raster *raster, { raster->width = (sfnt_ceil_fixed (outline->xmax) - - sfnt_floor_fixed (outline->xmin)) >> 16; + - sfnt_floor_fixed (outline->xmin)) / 65536; raster->height = (sfnt_ceil_fixed (outline->ymax) - - sfnt_floor_fixed (outline->ymin)) >> 16; + - sfnt_floor_fixed (outline->ymin)) / 65536; raster->refcount = 0; /* Align the raster to a SFNT_POLY_ALIGNMENT byte boundary. */ @@ -3896,8 +3961,8 @@ sfnt_prepare_raster (struct sfnt_raster *raster, However, variable fonts typically change this as variations are applied. */ raster->offx = sfnt_floor_fixed (outline->xmin - - outline->origin) >> 16; - raster->offy = sfnt_floor_fixed (outline->ymin) >> 16; + - outline->origin) / 65536; + raster->offy = sfnt_floor_fixed (outline->ymin) / 65536; } typedef void (*sfnt_edge_proc) (struct sfnt_edge *, size_t, @@ -5206,7 +5271,7 @@ sfnt_div_f26dot6 (sfnt_f26dot6 x, sfnt_f26dot6 y) #endif } -/* Multiply-round the specified two 26.6 fixed point numbers A and B. +/* Multiply the specified two 26.6 fixed point numbers A and B. Return the result, or an undefined value upon overflow. */ static sfnt_f26dot6 @@ -5263,26 +5328,6 @@ sfnt_mul_f2dot14 (sfnt_f2dot14 a, int32_t b) #endif } -#ifndef INT64_MAX - -/* Add the specified unsigned 32-bit N to the large integer - INTEGER. */ - -static void -sfnt_large_integer_add (struct sfnt_large_integer *integer, - uint32_t n) -{ - struct sfnt_large_integer number; - - number.low = integer->low + n; - number.high = integer->high + (number.low - < integer->low); - - *integer = number; -} - -#endif - /* Multiply the specified 26.6 fixed point number X by the specified 16.16 fixed point number Y with symmetric rounding. @@ -14343,15 +14388,15 @@ sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id, for (i = 0; i < glyph->simple->number_of_points; ++i) { - fword = sfnt_mul_fixed (dx[i], scale); + fword = sfnt_mul_fixed_round (dx[i], scale); glyph->simple->x_coordinates[i] += fword; - fword = sfnt_mul_fixed (dy[i], scale); + fword = sfnt_mul_fixed_round (dy[i], scale); glyph->simple->y_coordinates[i] += fword; } /* Apply the deltas for the two phantom points. */ - distortion->origin += sfnt_mul_fixed (dx[i++], scale); - distortion->advance += sfnt_mul_fixed (dx[i], scale); + distortion->origin += sfnt_mul_fixed_round (dx[i++], scale); + distortion->advance += sfnt_mul_fixed_round (dx[i], scale); break; default: @@ -14399,13 +14444,13 @@ sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id, if (glyph_points[i] == glyph->simple->number_of_points) { - distortion->origin += sfnt_mul_fixed (dx[i], scale); + distortion->origin += sfnt_mul_fixed_round (dx[i], scale); continue; } if (glyph_points[i] == glyph->simple->number_of_points + 1) { - distortion->advance += sfnt_mul_fixed (dx[i], scale); + distortion->advance += sfnt_mul_fixed_round (dx[i], scale); continue; } @@ -14413,9 +14458,9 @@ sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id, if (glyph_points[i] >= glyph->simple->number_of_points) continue; - fword = sfnt_mul_fixed (dx[i], scale); + fword = sfnt_mul_fixed_round (dx[i], scale); glyph->simple->x_coordinates[glyph_points[i]] += fword; - fword = sfnt_mul_fixed (dy[i], scale); + fword = sfnt_mul_fixed_round (dy[i], scale); glyph->simple->y_coordinates[glyph_points[i]] += fword; touched[glyph_points[i]] = true; } @@ -14729,7 +14774,7 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, else word = component->argument1.d; - fword = sfnt_mul_fixed (dx[i], scale); + fword = sfnt_mul_fixed_round (dx[i], scale); component->flags |= 01; component->argument1.d = word + fword; @@ -14740,14 +14785,14 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, else word = component->argument2.d; - fword = sfnt_mul_fixed (dy[i], scale); + fword = sfnt_mul_fixed_round (dy[i], scale); component->flags |= 01; component->argument2.d = word + fword; } /* Apply the deltas for the two phantom points. */ - distortion->origin += sfnt_mul_fixed (dx[i++], scale); - distortion->advance += sfnt_mul_fixed (dx[i], scale); + distortion->origin += sfnt_mul_fixed_round (dx[i++], scale); + distortion->advance += sfnt_mul_fixed_round (dx[i], scale); break; default: @@ -14795,13 +14840,13 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, if (glyph_points[i] == glyph->compound->num_components) { - distortion->origin += sfnt_mul_fixed (dx[i], scale); + distortion->origin += sfnt_mul_fixed_round (dx[i], scale); continue; } if (glyph_points[i] == glyph->compound->num_components + 1) { - distortion->advance += sfnt_mul_fixed (dx[i], scale); + distortion->advance += sfnt_mul_fixed_round (dx[i], scale); continue; } @@ -14822,7 +14867,7 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, else word = component->argument1.d; - fword = sfnt_mul_fixed (dx[i], scale); + fword = sfnt_mul_fixed_round (dx[i], scale); component->flags |= 01; component->argument1.d = word + fword; @@ -14833,7 +14878,7 @@ sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, else word = component->argument2.d; - fword = sfnt_mul_fixed (dy[i], scale); + fword = sfnt_mul_fixed_round (dy[i], scale); component->flags |= 01; component->argument2.d = word + fword; } @@ -14946,7 +14991,7 @@ sfnt_vary_interpreter (struct sfnt_interpreter *interpreter, then the tuple scale factor. */ delta = sfnt_mul_f26dot6_fixed (variation->deltas[j] * 64, interpreter->scale); - delta = sfnt_mul_fixed (delta, scale); + delta = sfnt_mul_fixed_round (delta, scale); /* Apply the delta to the control value table. */ interpreter->cvt[i] += delta; @@ -18923,8 +18968,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 40 -#define EASY_PPEM 40 +#define FANCY_PPEM 36 +#define EASY_PPEM 36 interpreter = NULL; head = sfnt_read_head_table (fd, font); @@ -19401,7 +19446,7 @@ #define EASY_PPEM 40 &dcontext)) printf ("decomposition failure\n"); - if (sfnt_lookup_glyph_metrics (code, EASY_PPEM, + if (sfnt_lookup_glyph_metrics (code, -1, &metrics, hmtx, hhea, head, maxp)) @@ -19424,6 +19469,10 @@ #define EASY_PPEM 40 if (outline) { + fprintf (stderr, "outline origin, rbearing: %" + PRIi32" %"PRIi32"\n", + outline->origin, + outline->ymax - outline->origin); sfnt_test_max = outline->ymax - outline->ymin; for (i = 0; i < outline->outline_used; i++) diff --git a/src/sfntfont.c b/src/sfntfont.c index 808c8b7c629..ee049b5c159 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -2081,11 +2081,6 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, } } - /* At this point, the glyph metrics are unscaled. Scale them up. - If INTERPRETER is set, use the scale placed within. */ - - sfnt_scale_metrics (&temp, scale); - if (!outline) { if (!interpreter) @@ -2102,6 +2097,11 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, &dcontext); } + /* At this point, the glyph metrics are unscaled. Scale them up. + If INTERPRETER is set, use the scale placed within. */ + + sfnt_scale_metrics (&temp, scale); + fail: xfree (glyph); @@ -2460,18 +2460,18 @@ sfntfont_probe_widths (struct sfnt_font_info *font_info) num_characters++; /* Add the advance to total_width. */ - total_width += metrics.advance >> 16; + total_width += metrics.advance / 65536; /* Update min_width if it hasn't been set yet or is wider. */ if (font_info->font.min_width == 1 - || font_info->font.min_width > metrics.advance >> 16) - font_info->font.min_width = metrics.advance >> 16; + || font_info->font.min_width > metrics.advance / 65536) + font_info->font.min_width = metrics.advance / 65536; /* If i is the space character, set the space width. Make sure to round this up. */ if (i == 32) font_info->font.space_width - = SFNT_CEIL_FIXED (metrics.advance) >> 16; + = SFNT_CEIL_FIXED (metrics.advance) / 65536; } /* Now, if characters were found, set average_width. */ @@ -3023,6 +3023,8 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, ASET (font_object, FONT_SPACING_INDEX, make_fixnum (desc->spacing)); + /* Set the font style. */ + FONT_SET_STYLE (font_object, FONT_WIDTH_INDEX, make_fixnum (desc->width)); FONT_SET_STYLE (font_object, FONT_WEIGHT_INDEX, @@ -3061,24 +3063,40 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, /* Make sure the instance is within range. */ && instance < desc->tables->fvar->instance_count) { - sfnt_init_blend (&font_info->blend, desc->tables->fvar, - desc->tables->gvar, desc->tables->avar, - desc->tables->cvar); + tem = AREF (desc->instances, instance); + + if (!NILP (tem)) + { + sfnt_init_blend (&font_info->blend, desc->tables->fvar, + desc->tables->gvar, desc->tables->avar, + desc->tables->cvar); + + /* Copy over the coordinates. */ + for (i = 0; i < desc->tables->fvar->axis_count; ++i) + font_info->blend.coords[i] + = desc->tables->fvar->instance[instance].coords[i]; - /* Copy over the coordinates. */ - for (i = 0; i < desc->tables->fvar->axis_count; ++i) - font_info->blend.coords[i] - = desc->tables->fvar->instance[instance].coords[i]; + sfnt_normalize_blend (&font_info->blend); - sfnt_normalize_blend (&font_info->blend); + /* If an interpreter was specified, distort it now. */ - /* If an interpreter was specified, distort it now. */ + if (font_info->interpreter) + sfnt_vary_interpreter (font_info->interpreter, + &font_info->blend); - if (font_info->interpreter) - sfnt_vary_interpreter (font_info->interpreter, - &font_info->blend); + font_info->instance = instance; - font_info->instance = instance; + /* Replace the style information with that of the + instance. */ + + FONT_SET_STYLE (font_object, FONT_WIDTH_INDEX, + AREF (tem, 2)); + FONT_SET_STYLE (font_object, FONT_WEIGHT_INDEX, + AREF (tem, 3)); + FONT_SET_STYLE (font_object, FONT_SLANT_INDEX, + AREF (tem, 4)); + ASET (font_object, FONT_ADSTYLE_INDEX, Qnil); + } } #ifdef HAVE_HARFBUZZ @@ -3165,13 +3183,13 @@ sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, if (!outline) return 1; - pcm->lbearing = metrics.lbearing >> 16; - pcm->rbearing = SFNT_CEIL_FIXED (outline->xmax) >> 16; + pcm->lbearing = metrics.lbearing / 65536; + pcm->rbearing = SFNT_CEIL_FIXED (outline->xmax) / 65536; /* Round the advance, ascent and descent upwards. */ - pcm->width = SFNT_CEIL_FIXED (metrics.advance) >> 16; - pcm->ascent = SFNT_CEIL_FIXED (outline->ymax) >> 16; - pcm->descent = SFNT_CEIL_FIXED (-outline->ymin) >> 16; + pcm->width = SFNT_CEIL_FIXED (metrics.advance) / 65536; + pcm->ascent = SFNT_CEIL_FIXED (outline->ymax) / 65536; + pcm->descent = SFNT_CEIL_FIXED (-outline->ymin) / 65536; sfntfont_dereference_outline (outline); return 0; @@ -3373,7 +3391,7 @@ sfntfont_draw (struct glyph_string *s, int from, int to, if (s->padding_p) current_x += 1; else - current_x += SFNT_CEIL_FIXED (metrics.advance) >> 16; + current_x += SFNT_CEIL_FIXED (metrics.advance) / 65536; } /* Call the window system function to put the glyphs to the commit ee9c0a482c86cab33ef149829ea02857bcdd9d8c Author: Po Lu Date: Mon Mar 27 18:22:23 2023 +0800 Update Android port * src/sfnt.c (sfnt_normalize_blend): Don't crash when axis variations are not present. diff --git a/src/sfnt.c b/src/sfnt.c index 99649698557..f6730857f86 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -13582,7 +13582,8 @@ sfnt_normalize_blend (struct sfnt_blend *blend) /* Now, apply axis variations, but only if the avar table has the right number of axes. */ - if (blend->fvar->axis_count == blend->avar->axis_count) + if (blend->avar && (blend->fvar->axis_count + == blend->avar->axis_count)) { for (i = 0; i < blend->fvar->axis_count; ++i) { commit 54d79f37ae59fbda0b40d3be66d0c9b3887734a7 Merge: dd4924ca90a e8790f42935 Author: Po Lu Date: Mon Mar 27 16:43:07 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit dd4924ca90a8ada26bb10e5df4557ae32c0d6403 Author: Po Lu Date: Mon Mar 27 16:42:52 2023 +0800 Update Android port * configure.ac (HAVE_OTF_GET_VARIATION_GLYPHS): Check for `hb_font_set_var_named_instance'. * src/sfnt.c (main): Update tests. * src/sfntfont-android.c (Fandroid_enumerate_fonts): Blacklist bad font. * src/sfntfont.c (struct sfnt_font_tables, struct sfnt_font_desc) (sfnt_decode_instance_name, sfnt_weight_descriptions) (sfnt_enum_font_1, sfntfont_list_1, sfntfont_desc_to_entity) (sfntfont_list, struct sfntfont_get_glyph_outline_dcontext) (sfntfont_get_glyph, sfntfont_get_glyph_outline) (struct sfnt_font_info, sfnt_close_tables, sfnt_open_tables) (sfntfont_open, sfntfont_measure_pcm, sfntfont_close) (sfntfont_draw, sfntfont_begin_hb_font, syms_of_sfntfont) (mark_sfntfont): Handle variable fonts correctly. diff --git a/configure.ac b/configure.ac index 1097be48e26..760188bf369 100644 --- a/configure.ac +++ b/configure.ac @@ -4586,14 +4586,19 @@ AC_DEFUN || test "$REALLY_ANDROID" = "yes"; then if test "${with_harfbuzz}" != "no"; then EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= $harfbuzz_required_ver]) - if test "$HAVE_HARFBUZZ" = "yes"; then + AS_IF([test "$HAVE_HARFBUZZ" = "yes"],[ AC_DEFINE([HAVE_HARFBUZZ], [1], [Define to 1 if using HarfBuzz.]) ### mingw32 and Cygwin-w32 don't use -lharfbuzz, since they load ### the library dynamically. - if test "${HAVE_W32}" = "yes"; then - HARFBUZZ_LIBS= - fi - fi + AS_IF([test "${HAVE_W32}" = "yes"], [HARFBUZZ_LIBS=]) + ## Now check for `hb_font_set_var_named_instance'. + OLD_CFLAGS=$CFLAGS + CFLAGS="$HARFBUZZ_CFLAGS $CFLAGS" + EMACS_CHECK_LIB([harfbuzz], [hb_font_set_var_named_instance], + [AC_DEFINE([HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE], [1], + [Define to 1 if `hb_font_set_var_named_instance' is present.])], + [], [$HARFBUZZ_LIBS], [#include ]) + CFLAGS=$OLD_CFLAGS]) fi fi diff --git a/src/sfnt.c b/src/sfnt.c index 9a1094a1ca9..99649698557 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -18922,8 +18922,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 15 -#define EASY_PPEM 15 +#define FANCY_PPEM 40 +#define EASY_PPEM 40 interpreter = NULL; head = sfnt_read_head_table (fd, font); diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c index 37f43465097..5e4c8fc6c9f 100644 --- a/src/sfntfont-android.c +++ b/src/sfntfont-android.c @@ -713,8 +713,11 @@ DEFUN ("android-enumerate-fonts", Fandroid_enumerate_fonts, /* If it contains (not ends with!) with .ttf or .ttc, then enumerate it. */ - if (strstr (dirent->d_name, ".ttf") - || strstr (dirent->d_name, ".ttc")) + if ((strstr (dirent->d_name, ".ttf") + || strstr (dirent->d_name, ".ttc")) + /* Ignore the non-variable Roboto font. */ + && (i != 0 || strcmp (dirent->d_name, + "RobotoStatic-Regular.ttf"))) { sprintf (name, "%s/%s", system_font_directories[i], dirent->d_name); diff --git a/src/sfntfont.c b/src/sfntfont.c index 346e145082a..808c8b7c629 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -69,6 +69,10 @@ Copyright (C) 2023 Free Software Foundation, Inc. struct sfnt_prep_table *prep; struct sfnt_fpgm_table *fpgm; struct sfnt_cvt_table *cvt; + struct sfnt_fvar_table *fvar; + struct sfnt_avar_table *avar; + struct sfnt_gvar_table *gvar; + struct sfnt_cvar_table *cvar; /* The selected character map. */ struct sfnt_cmap_encoding_subtable_data *cmap_data; @@ -118,6 +122,11 @@ Copyright (C) 2023 Free Software Foundation, Inc. /* Font registry that this font supports. */ Lisp_Object registry; + /* Vector of instances. Each element is another of the instance's + `style', `adstyle', and numeric width, weight, and slant. May be + nil. */ + Lisp_Object instances; + /* Numeric width, weight, slant and spacing. */ int width, weight, slant, spacing; @@ -360,6 +369,29 @@ sfnt_decode_foundry_name (struct sfnt_name_table *name) designer_rec.length); } +/* Decode the name of the specified font INSTANCE using the given NAME + table. Return the name of that instance, or nil upon failure. */ + +static Lisp_Object +sfnt_decode_instance_name (struct sfnt_instance *instance, + struct sfnt_name_table *name) +{ + struct sfnt_name_record name_rec; + unsigned char *name_data; + + name_data = sfnt_find_name (name, instance->name_id, + &name_rec); + + if (!name_data) + return Qnil; + + return sfnt_decode_font_string (name_data, + name_rec.platform_id, + name_rec.platform_specific_id, + name_rec.language_id, + name_rec.length); +} + struct sfnt_style_desc { /* The C string to match against. */ @@ -375,6 +407,7 @@ sfnt_decode_foundry_name (struct sfnt_name_table *name) { "thin", 0, }, { "extralight", 40, }, { "ultralight", 40, }, + { "light", 50, }, { "demilight", 55, }, { "semilight", 55, }, { "book", 75, }, @@ -809,7 +842,10 @@ sfnt_enum_font_1 (int fd, const char *file, struct sfnt_name_table *name; struct sfnt_meta_table *meta; struct sfnt_maxp_table *maxp; - Lisp_Object family, style; + struct sfnt_fvar_table *fvar; + struct sfnt_font_desc temp; + Lisp_Object family, style, instance, style1; + int i; /* Create the font desc and copy in the file name. */ desc = xzalloc (sizeof *desc + strlen (file) + 1); @@ -842,6 +878,10 @@ sfnt_enum_font_1 (int fd, const char *file, if (sfnt_decode_family_style (name, &family, &style)) goto bail4; + /* See if this is a distortable/variable/multiple master font (all + three terms mean the same time.) */ + fvar = sfnt_read_fvar_table (fd, subtables); + /* Set the family. */ desc->family = family; desc->designer = sfnt_decode_foundry_name (name); @@ -877,6 +917,43 @@ sfnt_enum_font_1 (int fd, const char *file, /* Figure out what registry this font is likely to support. */ sfnt_grok_registry (fd, desc, subtables); + if (fvar && fvar->instance_count) + { + /* If there is an fvar table with instances, then this is a font + which defines different axes along which the points in each + glyph can be changed. + + Instead of enumerating the font itself, enumerate each + instance within, which specifies how to configure each axis + to achieve a specified style. */ + + desc->instances = make_vector (fvar->instance_count, Qnil); + + for (i = 0; i < fvar->instance_count; ++i) + { + style1 = sfnt_decode_instance_name (&fvar->instance[i], + name); + + if (!style1) + continue; + + /* Now parse the style. */ + temp.adstyle = Qnil; + sfnt_parse_style (style1, &temp); + + /* Set each field of the vector. */ + instance = make_vector (5, Qnil); + ASET (instance, 0, style1); + ASET (instance, 1, temp.adstyle); + ASET (instance, 2, make_fixnum (temp.width)); + ASET (instance, 3, make_fixnum (temp.weight)); + ASET (instance, 4, make_fixnum (temp.slant)); + + /* Place the vector in desc->instances. */ + ASET (desc->instances, i, instance); + } + } + /* Set the style, link the desc onto system_fonts and return. */ desc->style = style; desc->next = system_fonts; @@ -898,6 +975,7 @@ sfnt_enum_font_1 (int fd, const char *file, next = &prev->next; } + xfree (fvar); xfree (meta); xfree (maxp); xfree (name); @@ -1346,15 +1424,23 @@ sfntfont_registries_compatible_p (Lisp_Object a, Lisp_Object b) } /* Return whether or not the font description DESC satisfactorily - matches the font specification FONT_SPEC. */ + matches the font specification FONT_SPEC. -static bool -sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec) + Value is 0 if there is no match, -1 if there is a match against + DESC itself, and the number of matching instances if the style + matches one or more instances defined in in DESC. Return the index + of each matching instance in INSTANCES; it should be SIZE big. */ + +static int +sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec, + int *instances, int size) { Lisp_Object tem, extra, tail; struct sfnt_cmap_encoding_subtable_data *cmap; size_t i; struct sfnt_cmap_encoding_subtable subtable; + int instance, num_instance; + Lisp_Object item; /* cmap and subtable are caches for sfntfont_lookup_char. */ @@ -1388,7 +1474,9 @@ sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec) if (!NILP (tem) && NILP (Fstring_equal (SYMBOL_NAME (tem), desc->family))) - return false; + return 0; + + instance = -1; /* If a registry is set and wrong, then reject the font desc immediately. This detects 50% of mismatches from fontset.c. @@ -1399,33 +1487,82 @@ sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec) tem = AREF (spec, FONT_REGISTRY_INDEX); if (!NILP (tem) && !NILP (desc->registry) && !sfntfont_registries_compatible_p (tem, desc->registry)) - return false; + return 0; - /* Check that the adstyle specified matches. */ + /* Check the style. If DESC is a fixed font, just check once. + Otherwise, check each instance. */ - tem = AREF (spec, FONT_ADSTYLE_INDEX); - if (!NILP (tem) && NILP (Fequal (tem, desc->adstyle))) - return false; + if (NILP (desc->instances)) + { + tem = AREF (spec, FONT_ADSTYLE_INDEX); + if (!NILP (tem) && NILP (Fequal (tem, desc->adstyle))) + return 0; - /* Check the style. */ + if (FONT_WIDTH_NUMERIC (spec) != -1 + && FONT_WIDTH_NUMERIC (spec) != desc->width) + return 0; - if (FONT_WIDTH_NUMERIC (spec) != -1 - && FONT_WIDTH_NUMERIC (spec) != desc->width) - return false; + if (FONT_WEIGHT_NUMERIC (spec) != -1 + && FONT_WEIGHT_NUMERIC (spec) != desc->weight) + return 0; - if (FONT_WEIGHT_NUMERIC (spec) != -1 - && FONT_WEIGHT_NUMERIC (spec) != desc->weight) - return false; + if (FONT_SLANT_NUMERIC (spec) != -1 + && FONT_SLANT_NUMERIC (spec) != desc->slant) + return 0; + } + else + { + num_instance = 0; - if (FONT_SLANT_NUMERIC (spec) != -1 - && FONT_SLANT_NUMERIC (spec) != desc->slant) - return false; + /* Find the indices of instances in this distortable font which + match the given font spec. */ + + for (i = 0; i < ASIZE (desc->instances); ++i) + { + item = AREF (desc->instances, i); + + if (NILP (item)) + continue; + + /* Check that the adstyle specified matches. */ + + tem = AREF (spec, FONT_ADSTYLE_INDEX); + if (!NILP (tem) && NILP (Fequal (tem, AREF (item, 1)))) + continue; + + /* Check the style. */ + + if (FONT_WIDTH_NUMERIC (spec) != -1 + && (FONT_WIDTH_NUMERIC (spec) + != XFIXNUM (AREF (item, 2)))) + continue; + + if (FONT_WEIGHT_NUMERIC (spec) != -1 + && (FONT_WEIGHT_NUMERIC (spec) + != XFIXNUM (AREF (item, 3)))) + continue; + + if (FONT_SLANT_NUMERIC (spec) != -1 + && (FONT_SLANT_NUMERIC (spec) + != XFIXNUM (AREF (item, 4)))) + continue; + + if (num_instance == size) + break; + + /* A matching instance has been found. Set its index, then + go back to the rest of the font matching. */ + instances[num_instance++] = i; + } + + instance = num_instance; + } /* Handle extras. */ extra = AREF (spec, FONT_EXTRA_INDEX); if (NILP (extra)) - return true; + return instance; tem = assq_no_quit (QCscript, extra); cmap = NULL; @@ -1490,12 +1627,12 @@ sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec) desc->subtable = subtable; xfree (cmap); - return true; + return instance; fail: /* The cmap might've been read in and require deallocation. */ xfree (cmap); - return false; + return 0; } /* Type of font entities and font objects created. */ @@ -1546,12 +1683,14 @@ sfntfont_registry_for_desc (struct sfnt_font_desc *desc) } /* Return a font-entity that represents the font descriptor (unopened - font) DESC. */ + font) DESC. If INSTANCE is more than or equal to 1, then it is the + index of the instance in DESC that should be opened plus 1; in that + case, DESC must be a distortable font. */ static Lisp_Object -sfntfont_desc_to_entity (struct sfnt_font_desc *desc) +sfntfont_desc_to_entity (struct sfnt_font_desc *desc, int instance) { - Lisp_Object entity; + Lisp_Object entity, vector; entity = font_make_entity (); @@ -1572,19 +1711,40 @@ sfntfont_desc_to_entity (struct sfnt_font_desc *desc) ASET (entity, FONT_SPACING_INDEX, make_fixnum (desc->spacing)); - FONT_SET_STYLE (entity, FONT_WIDTH_INDEX, - make_fixnum (desc->width)); - FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX, - make_fixnum (desc->weight)); - FONT_SET_STYLE (entity, FONT_SLANT_INDEX, - make_fixnum (desc->slant)); + if (instance >= 1) + { + if (NILP (desc->instances) + || instance > ASIZE (desc->instances)) + emacs_abort (); - ASET (entity, FONT_ADSTYLE_INDEX, Qnil); + vector = AREF (desc->instances, instance - 1); + FONT_SET_STYLE (entity, FONT_WIDTH_INDEX, + AREF (vector, 2)); + FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX, + AREF (vector, 3)); + FONT_SET_STYLE (entity, FONT_SLANT_INDEX, + AREF (vector, 4)); + ASET (entity, FONT_ADSTYLE_INDEX, AREF (vector, 1)); + } + else + { + FONT_SET_STYLE (entity, FONT_WIDTH_INDEX, + make_fixnum (desc->width)); + FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX, + make_fixnum (desc->weight)); + FONT_SET_STYLE (entity, FONT_SLANT_INDEX, + make_fixnum (desc->slant)); + ASET (entity, FONT_ADSTYLE_INDEX, desc->adstyle); + } /* Set FONT_EXTRA_INDEX to a pointer to the font description. Font descriptions are never supposed to be freed. */ + ASET (entity, FONT_EXTRA_INDEX, - list1 (Fcons (Qfont_entity, make_mint_ptr (desc)))); + (instance >= 1 + ? list2 (Fcons (Qfont_entity, make_mint_ptr (desc)), + Fcons (Qfont_instance, make_fixnum (instance - 1))) + : list1 (Fcons (Qfont_entity, make_mint_ptr (desc))))); return entity; } @@ -1597,6 +1757,7 @@ sfntfont_list (struct frame *f, Lisp_Object font_spec) { Lisp_Object matching, tem; struct sfnt_font_desc *desc; + int i, rc, instances[100]; matching = Qnil; @@ -1612,10 +1773,24 @@ sfntfont_list (struct frame *f, Lisp_Object font_spec) } /* Loop through known system fonts and add them one-by-one. */ + for (desc = system_fonts; desc; desc = desc->next) { - if (sfntfont_list_1 (desc, font_spec)) - matching = Fcons (sfntfont_desc_to_entity (desc), matching); + rc = sfntfont_list_1 (desc, font_spec, instances, + ARRAYELTS (instances)); + + if (rc < 0) + matching = Fcons (sfntfont_desc_to_entity (desc, 0), + matching); + else if (rc) + { + /* Add each matching instance. */ + + for (i = 0; i < rc; ++i) + matching = Fcons (sfntfont_desc_to_entity (desc, + instances[i] + 1), + matching); + } } unblock_input (); @@ -1688,23 +1863,45 @@ sfntfont_match (struct frame *f, Lisp_Object font_spec) /* glyf table. */ struct sfnt_glyf_table *glyf; + + /* Variation settings, or NULL. */ + struct sfnt_blend *blend; }; -/* Return the glyph identified by GLYPH from the glyf and loca table - specified in DCONTEXT. Set *NEED_FREE to true. */ +/* Return the glyph identified by GLYPH_ID from the glyf and loca + table specified in DCONTEXT. Set *NEED_FREE to true. */ static struct sfnt_glyph * -sfntfont_get_glyph (sfnt_glyph glyph, void *dcontext, +sfntfont_get_glyph (sfnt_glyph glyph_id, void *dcontext, bool *need_free) { struct sfntfont_get_glyph_outline_dcontext *tables; + struct sfnt_glyph *glyph; + struct sfnt_metrics_distortion distortion; tables = dcontext; *need_free = true; - return sfnt_read_glyph (glyph, tables->glyf, - tables->loca_short, - tables->loca_long); + glyph = sfnt_read_glyph (glyph_id, tables->glyf, + tables->loca_short, + tables->loca_long); + + if (!tables->blend || !glyph) + return glyph; + + if ((glyph->simple + && sfnt_vary_simple_glyph (tables->blend, glyph_id, + glyph, &distortion)) + || (!glyph->simple + && sfnt_vary_compound_glyph (tables->blend, glyph_id, + glyph, &distortion))) + { + sfnt_free_glyph (glyph); + return NULL; + } + + /* Note that the distortion is not relevant for compound glyphs. */ + return glyph; } /* Free the glyph identified by GLYPH. */ @@ -1734,6 +1931,8 @@ sfntfont_dereference_outline (struct sfnt_glyph_outline *outline) HEAD. Keep *CACHE_SIZE updated with the number of elements in the cache. + Distort the glyph using BLEND if INDEX is not -1. + Use the offset information in the long or short loca tables LOCA_LONG and LOCA_SHORT, whichever is set. @@ -1754,6 +1953,8 @@ sfntfont_dereference_outline (struct sfnt_glyph_outline *outline) sfntfont_get_glyph_outline (sfnt_glyph glyph_code, struct sfnt_outline_cache *cache, sfnt_fixed scale, int *cache_size, + struct sfnt_blend *blend, + int index, struct sfnt_glyf_table *glyf, struct sfnt_head_table *head, struct sfnt_hmtx_table *hmtx, @@ -1772,8 +1973,10 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, struct sfnt_instructed_outline *value; const char *error; struct sfnt_glyph_metrics temp; + struct sfnt_metrics_distortion distortion; start = cache->next; + distortion.advance = 0; /* See if the outline is already cached. */ for (; start != cache; start = start->next) @@ -1806,6 +2009,30 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, if (!glyph) return NULL; + /* Distort the glyph if necessary. */ + + if (index != -1) + { + if (glyph->simple) + { + if (sfnt_vary_simple_glyph (blend, glyph_code, + glyph, &distortion)) + { + sfnt_free_glyph (glyph); + return NULL; + } + } + else if (!glyph->simple) + { + if (sfnt_vary_compound_glyph (blend, glyph_code, + glyph, &distortion)) + { + sfnt_free_glyph (glyph); + return NULL; + } + } + } + /* Try to instruct the glyph if INTERPRETER is specified. */ outline = NULL; @@ -1813,6 +2040,7 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, dcontext.loca_long = loca_long; dcontext.loca_short = loca_short; dcontext.glyf = glyf; + dcontext.blend = (index != -1 ? blend : NULL); /* Now load the glyph's unscaled metrics into TEMP. */ @@ -1820,6 +2048,9 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, head, maxp)) goto fail; + /* Add the advance width distortion. */ + temp.advance += distortion.advance; + if (interpreter) { if (glyph->simple) @@ -1878,6 +2109,13 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, if (!outline) return NULL; + if (index != -1) + /* Finally, adjust the left side bearing of the glyph metrics by + the origin point of the outline, should a distortion have been + applied. The left side bearing is the distance from the origin + point to the left most point on the X axis. */ + temp.lbearing = outline->xmin - outline->origin; + start = xmalloc (sizeof *start); start->glyph = glyph_code; start->outline = outline; @@ -2125,6 +2363,13 @@ sfntfont_free_raster_cache (struct sfnt_raster_cache *cache) /* Factor used to convert from em space to pixel space. */ sfnt_fixed scale; + /* The blend (configuration of this multiple master font). */ + struct sfnt_blend blend; + + /* The index of the named instance used to initialize BLEND. + -1 if BLEND is not initialized. */ + int instance; + #ifdef HAVE_MMAP /* Whether or not the glyph table has been mmapped. */ bool glyf_table_mapped; @@ -2358,6 +2603,10 @@ sfnt_close_tables (struct sfnt_font_tables *tables) xfree (tables->prep); xfree (tables->fpgm); xfree (tables->cvt); + xfree (tables->fvar); + xfree (tables->avar); + xfree (tables->gvar); + xfree (tables->cvar); xfree (tables->cmap_data); if (tables->uvs) @@ -2524,6 +2773,15 @@ sfnt_open_tables (struct sfnt_font_desc *desc) tables->fpgm = sfnt_read_fpgm_table (fd, subtable); tables->cvt = sfnt_read_cvt_table (fd, subtable); + /* Read distortion related tables. These might not be present. */ + tables->fvar = sfnt_read_fvar_table (fd, subtable); + tables->avar = sfnt_read_avar_table (fd, subtable); + tables->gvar = sfnt_read_gvar_table (fd, subtable); + + if (tables->cvt && tables->fvar) + tables->cvar = sfnt_read_cvar_table (fd, subtable, tables->fvar, + tables->cvt); + return tables; bail5: @@ -2614,9 +2872,10 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, struct sfnt_font_desc *desc; Lisp_Object font_object; struct charset *charset; - int point_size; + int point_size, instance, i; Display_Info *dpyinfo; struct sfnt_font_tables *tables; + Lisp_Object tem; if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0) pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)); @@ -2633,10 +2892,18 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, /* Now find the font description corresponding to FONT_ENTITY. */ - if (NILP (AREF (font_entity, FONT_EXTRA_INDEX))) + tem = AREF (font_entity, FONT_EXTRA_INDEX); + if (NILP (tem)) return Qnil; - desc = xmint_pointer (XCDR (XCAR (AREF (font_entity, FONT_EXTRA_INDEX)))); + desc = xmint_pointer (XCDR (XCAR (tem))); + + /* Finally, see if a specific instance is associated with + FONT_ENTITY. */ + + instance = -1; + if (!NILP (XCDR (tem))) + instance = XFIXNUM (XCDR (XCAR (XCDR (tem)))); /* Build the font object. */ font_object = font_make_object (VECSIZE (struct sfnt_font_info), @@ -2669,6 +2936,8 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, font_info->raster_cache_size = 0; font_info->interpreter = NULL; font_info->scale = 0; + font_info->instance = -1; + font_info->blend.coords = NULL; #ifdef HAVE_MMAP font_info->glyf_table_mapped = false; #endif /* HAVE_MMAP */ @@ -2784,6 +3053,34 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, / 2)); sfntfont_setup_interpreter (font_info, point_size); + /* If an instance was specified and the font is distortable, set up + the blend. */ + + if (instance != -1 + && desc->tables->fvar && desc->tables->gvar + /* Make sure the instance is within range. */ + && instance < desc->tables->fvar->instance_count) + { + sfnt_init_blend (&font_info->blend, desc->tables->fvar, + desc->tables->gvar, desc->tables->avar, + desc->tables->cvar); + + /* Copy over the coordinates. */ + for (i = 0; i < desc->tables->fvar->axis_count; ++i) + font_info->blend.coords[i] + = desc->tables->fvar->instance[instance].coords[i]; + + sfnt_normalize_blend (&font_info->blend); + + /* If an interpreter was specified, distort it now. */ + + if (font_info->interpreter) + sfnt_vary_interpreter (font_info->interpreter, + &font_info->blend); + + font_info->instance = instance; + } + #ifdef HAVE_HARFBUZZ /* HarfBuzz will potentially read font tables after the font has been opened by Emacs. Keep the font open, and record its offset @@ -2855,6 +3152,8 @@ sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, outline = sfntfont_get_glyph_outline (glyph, &font->outline_cache, font->scale, &font->outline_cache_size, + &font->blend, + font->instance, font->glyf, font->head, font->hmtx, font->hhea, font->maxp, @@ -2960,6 +3259,11 @@ sfntfont_close (struct font *font) info->interpreter = NULL; info->uvs = NULL; + /* Deinitialize the blend. */ + if (info->instance != -1 && info->blend.coords) + sfnt_free_blend (&info->blend); + info->instance = -1; + #ifdef HAVE_MMAP /* Unlink INFO. */ @@ -3035,6 +3339,8 @@ sfntfont_draw (struct glyph_string *s, int from, int to, &info->outline_cache, info->scale, &info->outline_cache_size, + &info->blend, + info->instance, info->glyf, info->head, info->hmtx, info->hhea, info->maxp, @@ -3362,6 +3668,13 @@ sfntfont_begin_hb_font (struct font *font, double *position_unit) hb_font_set_scale (info->hb_font, factor * 64, factor * 64); hb_font_set_ppem (info->hb_font, factor, factor); +#ifdef HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE + /* Set the instance if this is a distortable font. */ + if (info->instance != -1) + hb_font_set_var_named_instance (info->hb_font, + info->instance); +#endif /* HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE */ + /* This is needed for HarfBuzz before 2.0.0; it is the default in later versions. */ hb_ot_font_set_funcs (info->hb_font); @@ -3396,6 +3709,7 @@ syms_of_sfntfont (void) DEFSYM (Qzh, "zh"); DEFSYM (Qja, "ja"); DEFSYM (Qko, "ko"); + DEFSYM (Qfont_instance, "font-instance"); /* Char-table purpose. */ DEFSYM (Qfont_lookup_cache, "font-lookup-cache"); @@ -3427,6 +3741,7 @@ mark_sfntfont (void) mark_object (desc->family); mark_object (desc->style); mark_object (desc->adstyle); + mark_object (desc->instances); mark_object (desc->languages); mark_object (desc->registry); mark_object (desc->char_cache); commit 18b34e9ca01deff8e0bde4b1e53293f27712a149 Author: Po Lu Date: Mon Mar 27 11:24:05 2023 +0800 Refactor sfntfont.c * src/sfnt.c (sfnt_build_glyph_outline): Take scale, not head and pixel size. (sfnt_scale_metrics_to_pixel_size): Delete function. (sfnt_get_scale): New function. (main): Update tests. * src/sfnt.h (PROTOTYPE): Update prototypes. * src/sfntfont.c (struct sfnt_outline_cache) (sfntfont_get_glyph_outline, struct sfnt_font_info) (sfntfont_open): Save scale in font information and use it. (sfntfont_measure_instructed_pcm): Delete function. (sfntfont_measure_pcm): Make this the only ``measure pcm'' function. (sfntfont_draw): Rely on sfntfont_get_glyph_outline for the scale. diff --git a/src/sfnt.c b/src/sfnt.c index 8e7a30e3b05..9a1094a1ca9 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -3760,21 +3760,21 @@ sfnt_curve_to_and_build (struct sfnt_point control, } /* Non-reentrantly build the outline for the specified GLYPH at the - given pixel size. Return the outline data with a refcount of 0 + given scale factor. Return the outline data with a refcount of 0 upon success, or NULL upon failure. + SCALE is a scale factor that converts between em space and device + space. + Use the scaled glyph METRICS to determine the origin point of the outline. Call GET_GLYPH and FREE_GLYPH with the specified DCONTEXT to obtain - glyphs for compound glyph subcomponents. - - HEAD should be the `head' table of the font. */ + glyphs for compound glyph subcomponents. */ TEST_STATIC struct sfnt_glyph_outline * sfnt_build_glyph_outline (struct sfnt_glyph *glyph, - struct sfnt_head_table *head, - int pixel_size, + sfnt_fixed scale, struct sfnt_glyph_metrics *metrics, sfnt_get_glyph_proc get_glyph, sfnt_free_glyph_proc free_glyph, @@ -3805,19 +3805,8 @@ sfnt_build_glyph_outline (struct sfnt_glyph *glyph, outline->xmax = 0; outline->ymax = 0; - /* Figure out how to convert from font unit-space to pixel space. - To turn one unit to its corresponding pixel size given a ppem of - 1, the unit must be divided by head->units_per_em. Then, it must - be multipled by the ppem. So, - - PIXEL = UNIT / UPEM * PPEM - - which means: - - PIXEL = UNIT * PPEM / UPEM */ - - build_outline_context.factor - = sfnt_div_fixed (pixel_size, head->units_per_em); + /* Set the scale factor. */ + build_outline_context.factor = scale; /* Decompose the outline. */ rc = sfnt_decompose_glyph (glyph, sfnt_move_to_and_build, @@ -4536,21 +4525,24 @@ sfnt_scale_metrics (struct sfnt_glyph_metrics *metrics, = sfnt_mul_fixed (metrics->advance * 65536, factor); } -/* Like `sfnt_scale_metrics', except it scales the specified metrics - by a factor calculated using the given PPEM and HEAD table's UPEM - value. */ +/* Calculate the factor used to convert em space to device space for a + font with the specified HEAD table and PPEM value. */ -MAYBE_UNUSED TEST_STATIC void -sfnt_scale_metrics_to_pixel_size (struct sfnt_glyph_metrics *metrics, - int ppem, - struct sfnt_head_table *head) +MAYBE_UNUSED TEST_STATIC sfnt_fixed +sfnt_get_scale (struct sfnt_head_table *head, int ppem) { - sfnt_fixed factor; + /* Figure out how to convert from font unit-space to pixel space. + To turn one unit to its corresponding pixel size given a ppem of + 1, the unit must be divided by head->units_per_em. Then, it must + be multipled by the ppem. So, + + PIXEL = UNIT / UPEM * PPEM + + which means: + + PIXEL = UNIT * PPEM / UPEM */ - /* Now calculate the factor scale lbearing and advance up to the - given PPEM size. */ - factor = sfnt_div_fixed (ppem, head->units_per_em); - sfnt_scale_metrics (metrics, factor); + return sfnt_div_fixed (ppem, head->units_per_em); } @@ -19419,8 +19411,7 @@ #define EASY_PPEM 15 /* Time this important bit. */ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start); - outline = sfnt_build_glyph_outline (glyph, head, - EASY_PPEM, + outline = sfnt_build_glyph_outline (glyph, scale, &metrics, sfnt_test_get_glyph, sfnt_test_free_glyph, diff --git a/src/sfnt.h b/src/sfnt.h index 84e51ff6766..fb8cb80bae9 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -1370,8 +1370,7 @@ #define PROTOTYPE int, struct sfnt_offset_subtable * #define PROTOTYPE \ struct sfnt_glyph *, \ - struct sfnt_head_table *, \ - int, \ + sfnt_fixed, \ struct sfnt_glyph_metrics *, \ sfnt_get_glyph_proc, \ sfnt_free_glyph_proc, \ @@ -1403,8 +1402,7 @@ #define PROTOTYPE \ extern void sfnt_scale_metrics (struct sfnt_glyph_metrics *, sfnt_fixed); -extern void sfnt_scale_metrics_to_pixel_size (struct sfnt_glyph_metrics *, - int, struct sfnt_head_table *); +extern sfnt_fixed sfnt_get_scale (struct sfnt_head_table *, int); #define PROTOTYPE int, struct sfnt_offset_subtable * extern struct sfnt_name_table *sfnt_read_name_table (PROTOTYPE); diff --git a/src/sfntfont.c b/src/sfntfont.c index 09aa89a9cdd..346e145082a 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -1648,7 +1648,10 @@ sfntfont_match (struct frame *f, Lisp_Object font_spec) /* Caching subsystem. Generating outlines from glyphs is expensive, and so is rasterizing them, so two caches are maintained for both - glyph outlines and rasters. */ + glyph outlines and rasters. + + Computing metrics also requires some expensive processing if the + glyph has instructions or distortions. */ struct sfnt_outline_cache { @@ -1658,6 +1661,9 @@ sfntfont_match (struct frame *f, Lisp_Object font_spec) /* Pointer to outline. */ struct sfnt_glyph_outline *outline; + /* Reference to glyph metrics. */ + struct sfnt_glyph_metrics metrics; + /* What glyph this caches. */ sfnt_glyph glyph; }; @@ -1724,20 +1730,21 @@ sfntfont_dereference_outline (struct sfnt_glyph_outline *outline) } /* Get the outline corresponding to the specified GLYPH_CODE in CACHE. - Use the pixel size PIXEL_SIZE, the glyf table GLYF, and the head - table HEAD. Keep *CACHE_SIZE updated with the number of elements - in the cache. + Use the scale factor SCALE, the glyf table GLYF, and the head table + HEAD. Keep *CACHE_SIZE updated with the number of elements in the + cache. Use the offset information in the long or short loca tables LOCA_LONG and LOCA_SHORT, whichever is set. - Use the specified HMTX, HHEA and MAXP tables when instructing + Use the specified HMTX, HEAD, HHEA and MAXP tables when instructing compound glyphs. - If INTERPRETER is non-NULL, then possibly use the unscaled glyph - metrics in METRICS and the interpreter STATE to instruct the glyph. - Otherwise, METRICS must contain scaled glyph metrics used to - compute the origin point of the outline. + If INTERPRETER is non-NULL, then possibly use it and the + interpreter graphics STATE to instruct the glyph. + + If METRICS is non-NULL, return the scaled glyph metrics after + variation and instructing. Return the outline with an incremented reference count and enter the generated outline into CACHE upon success, possibly discarding @@ -1746,7 +1753,7 @@ sfntfont_dereference_outline (struct sfnt_glyph_outline *outline) static struct sfnt_glyph_outline * sfntfont_get_glyph_outline (sfnt_glyph glyph_code, struct sfnt_outline_cache *cache, - int pixel_size, int *cache_size, + sfnt_fixed scale, int *cache_size, struct sfnt_glyf_table *glyf, struct sfnt_head_table *head, struct sfnt_hmtx_table *hmtx, @@ -1785,6 +1792,9 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, start->last->next = start; start->outline->refcount++; + if (metrics) + *metrics = start->metrics; + return start->outline; } } @@ -1804,6 +1814,12 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, dcontext.loca_short = loca_short; dcontext.glyf = glyf; + /* Now load the glyph's unscaled metrics into TEMP. */ + + if (sfnt_lookup_glyph_metrics (glyph_code, -1, &temp, hmtx, hhea, + head, maxp)) + goto fail; + if (interpreter) { if (glyph->simple) @@ -1813,7 +1829,7 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, interpreter->state = *state; error = sfnt_interpret_simple_glyph (glyph, interpreter, - metrics, &value); + &temp, &value); } else /* Restoring the interpreter state is done by @@ -1824,7 +1840,7 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, sfntfont_get_glyph, sfntfont_free_glyph, hmtx, hhea, maxp, - metrics, &dcontext, + &temp, &dcontext, &value); if (!error) @@ -1834,32 +1850,29 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, } } + /* At this point, the glyph metrics are unscaled. Scale them up. + If INTERPRETER is set, use the scale placed within. */ + + sfnt_scale_metrics (&temp, scale); + if (!outline) { - /* If INTERPRETER is NULL, METRICS contains scaled metrics. */ - if (!interpreter) - outline = sfnt_build_glyph_outline (glyph, head, pixel_size, - metrics, + outline = sfnt_build_glyph_outline (glyph, scale, + &temp, sfntfont_get_glyph, sfntfont_free_glyph, &dcontext); else - { - /* But otherwise, they are unscaled, and must be scaled - before being used. */ - - temp = *metrics; - sfnt_scale_metrics_to_pixel_size (&temp, pixel_size, - head); - outline = sfnt_build_glyph_outline (glyph, head, pixel_size, - &temp, - sfntfont_get_glyph, - sfntfont_free_glyph, - &dcontext); - } + outline = sfnt_build_glyph_outline (glyph, scale, + &temp, + sfntfont_get_glyph, + sfntfont_free_glyph, + &dcontext); } + fail: + xfree (glyph); if (!outline) @@ -1868,6 +1881,7 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, start = xmalloc (sizeof *start); start->glyph = glyph_code; start->outline = outline; + start->metrics = temp; /* One reference goes to the cache. The second reference goes to the caller. */ @@ -1898,7 +1912,11 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, (*cache_size)--; } - /* Return the cached outline. */ + /* Return the cached outline and metrics. */ + + if (metrics) + *metrics = temp; + return outline; } @@ -2104,6 +2122,9 @@ sfntfont_free_raster_cache (struct sfnt_raster_cache *cache) programs. */ struct sfnt_graphics_state state; + /* Factor used to convert from em space to pixel space. */ + sfnt_fixed scale; + #ifdef HAVE_MMAP /* Whether or not the glyph table has been mmapped. */ bool glyf_table_mapped; @@ -2647,6 +2668,7 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, font_info->raster_cache.last = &font_info->raster_cache; font_info->raster_cache_size = 0; font_info->interpreter = NULL; + font_info->scale = 0; #ifdef HAVE_MMAP font_info->glyf_table_mapped = false; #endif /* HAVE_MMAP */ @@ -2682,6 +2704,9 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, font_info->cmap_subtable = tables->cmap_subtable; font_info->uvs = tables->uvs; + /* Calculate the font's scaling factor. */ + font_info->scale = sfnt_get_scale (font_info->head, pixel_size); + /* Fill in font data. */ font = &font_info->font; font->pixel_size = pixel_size; @@ -2819,21 +2844,16 @@ sfntfont_encode_char (struct font *font, int c) Value is 0 upon success, 1 otherwise. */ static int -sfntfont_measure_instructed_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, - struct font_metrics *pcm) +sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, + struct font_metrics *pcm) { struct sfnt_glyph_metrics metrics; struct sfnt_glyph_outline *outline; - /* Ask for unscaled metrics. */ - if (sfnt_lookup_glyph_metrics (glyph, -1, &metrics, font->hmtx, - font->hhea, font->head, font->maxp)) - return 1; - /* Now get the glyph outline, which is required to obtain the rsb, ascent and descent. */ outline = sfntfont_get_glyph_outline (glyph, &font->outline_cache, - font->font.pixel_size, + font->scale, &font->outline_cache_size, font->glyf, font->head, font->hmtx, font->hhea, @@ -2846,57 +2866,6 @@ sfntfont_measure_instructed_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, if (!outline) return 1; - /* Scale the metrics by the interpreter's scale. */ - sfnt_scale_metrics (&metrics, font->interpreter->scale); - - pcm->lbearing = metrics.lbearing >> 16; - pcm->rbearing = SFNT_CEIL_FIXED (outline->xmax) >> 16; - - /* Round the advance, ascent and descent upwards. */ - pcm->width = SFNT_CEIL_FIXED (metrics.advance) >> 16; - pcm->ascent = SFNT_CEIL_FIXED (outline->ymax) >> 16; - pcm->descent = SFNT_CEIL_FIXED (-outline->ymin) >> 16; - - sfntfont_dereference_outline (outline); - return 0; -} - -/* Measure the single glyph GLYPH in the font FONT and return its - metrics in *PCM. Value is 0 upon success, 1 otherwise. */ - -static int -sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, - struct font_metrics *pcm) -{ - struct sfnt_glyph_metrics metrics; - struct sfnt_glyph_outline *outline; - - if (font->interpreter) - /* Use a function which instructs the glyph. */ - return sfntfont_measure_instructed_pcm (font, glyph, pcm); - - /* Get the glyph metrics first. */ - if (sfnt_lookup_glyph_metrics (glyph, font->font.pixel_size, - &metrics, font->hmtx, font->hhea, - font->head, font->maxp)) - return 1; - - /* Now get the glyph outline, which is required to obtain the rsb, - ascent and descent. */ - outline = sfntfont_get_glyph_outline (glyph, &font->outline_cache, - font->font.pixel_size, - &font->outline_cache_size, - font->glyf, font->head, - font->hmtx, font->hhea, - font->maxp, - font->loca_short, - font->loca_long, NULL, - &metrics, NULL); - - if (!outline) - return 1; - - /* How to round lbearing and rbearing? */ pcm->lbearing = metrics.lbearing >> 16; pcm->rbearing = SFNT_CEIL_FIXED (outline->xmax) >> 16; @@ -3049,15 +3018,10 @@ sfntfont_draw (struct glyph_string *s, int from, int to, struct font *font; struct sfnt_font_info *info; struct sfnt_glyph_metrics metrics; - int pixel_size; length = to - from; font = s->font; info = (struct sfnt_font_info *) font; - pixel_size = font->pixel_size; - - if (info->interpreter) - pixel_size = -1; rasters = alloca (length * sizeof *rasters); x_coords = alloca (length * sizeof *x_coords); @@ -3066,21 +3030,10 @@ sfntfont_draw (struct glyph_string *s, int from, int to, /* Get rasters and outlines for them. */ for (i = from; i < to; ++i) { - /* Look up the metrics for this glyph. The metrics are unscaled - if INFO->interpreter is set. */ - if (sfnt_lookup_glyph_metrics (s->char2b[i], pixel_size, - &metrics, info->hmtx, info->hhea, - info->head, info->maxp)) - { - rasters[i - from] = NULL; - x_coords[i - from] = 0; - continue; - } - /* Look up the outline. */ outline = sfntfont_get_glyph_outline (s->char2b[i], &info->outline_cache, - font->pixel_size, + info->scale, &info->outline_cache_size, info->glyf, info->head, info->hmtx, info->hhea, @@ -3098,10 +3051,6 @@ sfntfont_draw (struct glyph_string *s, int from, int to, continue; } - /* Scale the metrics if info->interpreter is set. */ - if (info->interpreter) - sfnt_scale_metrics (&metrics, info->interpreter->scale); - /* Rasterize the outline. */ rasters[i - from] = sfntfont_get_glyph_raster (s->char2b[i], &info->raster_cache, commit 67a325243c6942ff367b24211999554690c328d2 Author: Po Lu Date: Mon Mar 27 09:58:42 2023 +0800 Refactor sfntfont.c * src/sfntfont.c (struct sfnt_font_tables): New structure. (struct sfnt_font_desc): New field `tables'. (struct sfnt_font_info): New field `desc'. (sfntfont_setup_interpreter): Drop fd arguments and don't try to load interpreter tables. (sfnt_open_tables, sfnt_close_tables): New functions. (sfnt_reference_font_tables, sfnt_dereference_font_tables): New functions. (sfntfont_open, sfntfont_close): Implement in terms of those functions in order to share tables. diff --git a/src/sfntfont.c b/src/sfntfont.c index e4579d62154..09aa89a9cdd 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -51,6 +51,48 @@ Copyright (C) 2023 Free Software Foundation, Inc. +/* Tables associated with each font, be it distortable or not. This + allows different font objects sharing the same underlying font file + to share tables. */ + +struct sfnt_font_tables +{ + /* Various tables required to use the font. */ + struct sfnt_cmap_table *cmap; + struct sfnt_hhea_table *hhea; + struct sfnt_maxp_table *maxp; + struct sfnt_head_table *head; + struct sfnt_hmtx_table *hmtx; + struct sfnt_glyf_table *glyf; + struct sfnt_loca_table_short *loca_short; + struct sfnt_loca_table_long *loca_long; + struct sfnt_prep_table *prep; + struct sfnt_fpgm_table *fpgm; + struct sfnt_cvt_table *cvt; + + /* The selected character map. */ + struct sfnt_cmap_encoding_subtable_data *cmap_data; + + /* Data identifying that character map. */ + struct sfnt_cmap_encoding_subtable cmap_subtable; + + /* The UVS context. */ + struct sfnt_uvs_context *uvs; + +#ifdef HAVE_MMAP + /* Whether or not the glyph table has been mmapped. */ + bool glyf_table_mapped; +#endif /* HAVE_MMAP */ + +#ifdef HAVE_HARFBUZZ + /* File descriptor associated with this font. */ + int fd; + + /* The table directory of the font file. */ + struct sfnt_offset_subtable *directory; +#endif /* HAVE_HARFBUZZ */ +}; + /* Description of a font that hasn't been opened. */ struct sfnt_font_desc @@ -99,6 +141,12 @@ Copyright (C) 2023 Free Software Foundation, Inc. /* The number of glyphs in this font. Used to catch invalid cmap tables. This is actually the number of glyphs - 1. */ int num_glyphs; + + /* The number of references to the font tables below. */ + int refcount; + + /* List of font tables. */ + struct sfnt_font_tables *tables; }; /* List of fonts. */ @@ -1808,7 +1856,7 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, &temp, sfntfont_get_glyph, sfntfont_free_glyph, - &dcontext); + &dcontext); } } @@ -2011,6 +2059,10 @@ sfntfont_free_raster_cache (struct sfnt_raster_cache *cache) struct sfnt_font_info *next; #endif /* HAVE_MMAP */ + /* The font description used to create this font. Used to + dereference tables associated with this font. */ + struct sfnt_font_desc *desc; + /* Various tables required to use the font. */ struct sfnt_cmap_table *cmap; struct sfnt_hhea_table *hhea; @@ -2161,20 +2213,17 @@ sfntfont_probe_widths (struct sfnt_font_info *font_info) font_info->font.average_width = total_width / num_characters; } -/* Initialize the instruction interpreter for INFO, whose file and - offset subtable should be respectively FD and SUBTABLE. Load the - font and preprogram for the pixel size in INFO and its - corresponding point size POINT_SIZE. +/* Initialize the instruction interpreter for INFO. Load the font and + preprogram for the pixel size in INFO and its corresponding point + size POINT_SIZE. The font tables in INFO must already have been initialized. - Set INFO->interpreter, INFO->cvt, INFO->prep, INFO->fpgm and - INFO->state upon success, and leave those fields intact + Set INFO->interpreter upon success, and leave that field intact otherwise. */ static void -sfntfont_setup_interpreter (int fd, struct sfnt_font_info *info, - struct sfnt_offset_subtable *subtable, +sfntfont_setup_interpreter (struct sfnt_font_info *info, int point_size) { struct sfnt_cvt_table *cvt; @@ -2184,12 +2233,11 @@ sfntfont_setup_interpreter (int fd, struct sfnt_font_info *info, const char *error; struct sfnt_graphics_state state; - /* Try to read the control value program, cvt, and font program - tables. */ + /* Load the cvt, fpgm and prep already read. */ - cvt = sfnt_read_cvt_table (fd, subtable); - fpgm = sfnt_read_fpgm_table (fd, subtable); - prep = sfnt_read_prep_table (fd, subtable); + cvt = info->cvt ; + fpgm = info->fpgm; + prep = info->prep; /* If both fpgm and prep are NULL, this font likely has no instructions, so don't bother setting up the interpreter. */ @@ -2199,6 +2247,7 @@ sfntfont_setup_interpreter (int fd, struct sfnt_font_info *info, /* Now, create the interpreter using the limits in info->maxp and info->head. CVT can be NULL. */ + interpreter = sfnt_make_interpreter (info->maxp, cvt, info->head, info->font.pixel_size, point_size); @@ -2256,92 +2305,72 @@ sfntfont_setup_interpreter (int fd, struct sfnt_font_info *info, bail1: xfree (interpreter); bail: - xfree (cvt); - xfree (fpgm); - xfree (prep); + return; } -/* Open the font corresponding to the font-entity FONT_ENTITY. Return - nil upon failure, else the opened font-object. */ +/* Free each of the tables opened by `sfnt_open_tables', and possibly + file descriptors as well. Then, free TABLES itself. */ -Lisp_Object -sfntfont_open (struct frame *f, Lisp_Object font_entity, - int pixel_size) +static void +sfnt_close_tables (struct sfnt_font_tables *tables) { - struct sfnt_font_info *font_info; - struct font *font; - struct sfnt_font_desc *desc; - Lisp_Object font_object; - int fd, i; -#ifdef HAVE_MMAP int rc; -#endif /* HAVE_MMAP */ - struct sfnt_offset_subtable *subtable; - struct sfnt_cmap_encoding_subtable *subtables; - struct sfnt_cmap_encoding_subtable_data **data; - struct charset *charset; - int point_size; - Display_Info *dpyinfo; - struct sfnt_cmap_format_14 *format14; - if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0) - pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)); - else if (pixel_size == 0) + xfree (tables->cmap); + xfree (tables->hhea); + xfree (tables->maxp); + xfree (tables->head); + xfree (tables->hmtx); +#ifdef HAVE_MMAP + if (tables->glyf_table_mapped) { - /* This bit was copied from xfont.c. The values might need - adjustment. */ + rc = sfnt_unmap_glyf_table (tables->glyf); - if (FRAME_FONT (f)) - pixel_size = FRAME_FONT (f)->pixel_size; - else - pixel_size = 12; + if (rc) + emacs_abort (); } + else +#endif /* HAVE_MMAP */ + xfree (tables->glyf); + xfree (tables->loca_short); + xfree (tables->loca_long); + xfree (tables->prep); + xfree (tables->fpgm); + xfree (tables->cvt); + xfree (tables->cmap_data); - /* Now find the font description corresponding to FONT_ENTITY. */ - - if (NILP (AREF (font_entity, FONT_EXTRA_INDEX))) - return Qnil; + if (tables->uvs) + sfnt_free_uvs_context (tables->uvs); - desc = xmint_pointer (XCDR (XCAR (AREF (font_entity, FONT_EXTRA_INDEX)))); +#ifdef HAVE_HARFBUZZ + /* Close the font file. */ - /* Build the font object. */ - font_object = font_make_object (VECSIZE (struct sfnt_font_info), - font_entity, pixel_size); - font_info = (struct sfnt_font_info *) XFONT_OBJECT (font_object); + if (tables->fd != -1) + { + emacs_close (tables->fd); + tables->fd = -1; + } - block_input (); + /* Free its table directory. */ + xfree (tables->directory); + tables->directory = NULL; +#endif +} - /* Initialize all the font driver specific data. */ +/* Open font tables associated with the specified font description + DESC. Return the font tables, or NULL upon failure. */ - font_info->cmap = NULL; - font_info->hhea = NULL; - font_info->maxp = NULL; - font_info->head = NULL; - font_info->glyf = NULL; - font_info->hmtx = NULL; - font_info->loca_short = NULL; - font_info->loca_long = NULL; - font_info->cmap_data = NULL; - font_info->prep = NULL; - font_info->fpgm = NULL; - font_info->cvt = NULL; - font_info->uvs = NULL; +static struct sfnt_font_tables * +sfnt_open_tables (struct sfnt_font_desc *desc) +{ + struct sfnt_font_tables *tables; + struct sfnt_offset_subtable *subtable; + int fd, i, rc; + struct sfnt_cmap_encoding_subtable *subtables; + struct sfnt_cmap_encoding_subtable_data **data; + struct sfnt_cmap_format_14 *format14; - font_info->outline_cache.next = &font_info->outline_cache; - font_info->outline_cache.last = &font_info->outline_cache; - font_info->outline_cache_size = 0; - font_info->raster_cache.next = &font_info->raster_cache; - font_info->raster_cache.last = &font_info->raster_cache; - font_info->raster_cache_size = 0; - font_info->interpreter = NULL; -#ifdef HAVE_MMAP - font_info->glyf_table_mapped = false; -#endif /* HAVE_MMAP */ -#ifdef HAVE_HARFBUZZ - font_info->hb_font = NULL; - font_info->fd = -1; - font_info->directory = NULL; -#endif /* HAVE_HARFBUZZ */ + tables = xzalloc (sizeof *tables); /* Open the font. */ fd = emacs_open (desc->path, O_RDONLY, 0); @@ -2349,7 +2378,7 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, if (fd == -1) goto bail; - /* Seek to the offset specified. */ + /* Seek to the offset specified to the table directory. */ if (desc->offset && lseek (fd, desc->offset, SEEK_SET) != desc->offset) @@ -2365,17 +2394,16 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, mostly on devices with flash memory, so the order in which they are read is insignificant. */ - /* Select a character map table. */ - font_info->cmap = sfnt_read_cmap_table (fd, subtable, &subtables, - &data); - if (!font_info->cmap) + tables->cmap = sfnt_read_cmap_table (fd, subtable, &subtables, + &data); + if (!tables->cmap) goto bail2; format14 = NULL; - font_info->cmap_data - = sfntfont_select_cmap (font_info->cmap, + tables->cmap_data + = sfntfont_select_cmap (tables->cmap, subtables, data, - &font_info->cmap_subtable, + &tables->cmap_subtable, &format14); if (format14) @@ -2385,13 +2413,13 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, font, and a list of ``non-default'' mappings between base characters and variation glyph IDs. */ - font_info->uvs = sfnt_create_uvs_context (format14, fd); + tables->uvs = sfnt_create_uvs_context (format14, fd); xfree (format14); } - for (i = 0; i < font_info->cmap->num_subtables; ++i) + for (i = 0; i < tables->cmap->num_subtables; ++i) { - if (data[i] != font_info->cmap_data + if (data[i] != tables->cmap_data /* format14 has already been freed. */ && data[i] != (struct sfnt_cmap_encoding_subtable_data *) format14) xfree (data[i]); @@ -2400,66 +2428,260 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, xfree (subtables); xfree (data); - if (!font_info->cmap_data) + if (!tables->cmap_data) goto bail3; /* Read the hhea, maxp, glyf, and head tables. */ - font_info->hhea = sfnt_read_hhea_table (fd, subtable); - font_info->maxp = sfnt_read_maxp_table (fd, subtable); + tables->hhea = sfnt_read_hhea_table (fd, subtable); + tables->maxp = sfnt_read_maxp_table (fd, subtable); #ifdef HAVE_MMAP /* First try to map the glyf table. If that fails, then read the glyf table. */ - font_info->glyf = sfnt_map_glyf_table (fd, subtable); + tables->glyf = sfnt_map_glyf_table (fd, subtable); /* Next, if this fails, read the glyf table. */ - if (!font_info->glyf) + if (!tables->glyf) #endif /* HAVE_MMAP */ - font_info->glyf = sfnt_read_glyf_table (fd, subtable); + tables->glyf = sfnt_read_glyf_table (fd, subtable); #ifdef HAVE_MMAP else - font_info->glyf_table_mapped = true; + tables->glyf_table_mapped = true; #endif /* HAVE_MMAP */ - font_info->head = sfnt_read_head_table (fd, subtable); + tables->head = sfnt_read_head_table (fd, subtable); /* If any of those tables couldn't be read, bail. */ - if (!font_info->hhea || !font_info->maxp || !font_info->glyf - || !font_info->head) + if (!tables->hhea || !tables->maxp || !tables->glyf + || !tables->head) goto bail4; /* Now figure out which kind of loca table must be read based on head->index_to_loc_format. */ - font_info->loca_short = NULL; - font_info->loca_long = NULL; - if (font_info->head->index_to_loc_format) + if (tables->head->index_to_loc_format) { - font_info->loca_long + tables->loca_long = sfnt_read_loca_table_long (fd, subtable); - if (!font_info->loca_long) + if (!tables->loca_long) goto bail4; } else { - font_info->loca_short + tables->loca_short = sfnt_read_loca_table_short (fd, subtable); - if (!font_info->loca_short) + if (!tables->loca_short) goto bail4; } /* Read the horizontal metrics table. */ - font_info->hmtx = sfnt_read_hmtx_table (fd, subtable, - font_info->hhea, - font_info->maxp); - if (!font_info->hmtx) + tables->hmtx = sfnt_read_hmtx_table (fd, subtable, + tables->hhea, + tables->maxp); + if (!tables->hmtx) goto bail5; +#ifdef HAVE_HARFBUZZ + /* Now copy over the subtable if necessary, as it is needed to read + extra font tables required by HarfBuzz. */ + tables->directory = subtable; + tables->fd = fd; +#else /* !HAVE_HARFBUZZ */ + /* Otherwise, close the fd and free the table directory. */ + xfree (subtable); + emacs_close (fd); +#endif /* HAVE_HARFBUZZ */ + + /* Read instruction related font tables. These might not be + present, which is OK, since instructing fonts is optional. */ + tables->prep = sfnt_read_prep_table (fd, subtable); + tables->fpgm = sfnt_read_fpgm_table (fd, subtable); + tables->cvt = sfnt_read_cvt_table (fd, subtable); + + return tables; + + bail5: + xfree (tables->loca_long); + xfree (tables->loca_short); + bail4: + xfree (tables->hhea); + xfree (tables->maxp); + +#ifdef HAVE_MMAP + if (tables->glyf_table_mapped) + { + rc = sfnt_unmap_glyf_table (tables->glyf); + + if (rc) + emacs_abort (); + } + else +#endif /* HAVE_MMAP */ + xfree (tables->glyf); + + xfree (tables->head); + + /* This comes under bail4 due to a peculiarity of how the four + tables above are validated. */ + xfree (tables->cmap_data); + bail3: + if (tables->uvs) + sfnt_free_uvs_context (tables->uvs); + + xfree (tables->cmap); + bail2: + xfree (subtable); + bail1: + emacs_close (fd); + bail: + xfree (tables); + return NULL; +} + +/* Open or reference font tables corresponding to the specified font + DESC. Return NULL upon failure. */ + +static struct sfnt_font_tables * +sfnt_reference_font_tables (struct sfnt_font_desc *desc) +{ + if (desc->refcount) + { + desc->refcount++; + return desc->tables; + } + + desc->tables = sfnt_open_tables (desc); + + if (!desc->tables) + return NULL; + + desc->refcount++; + return desc->tables; +} + +/* Dereference font tables corresponding to the specified font + DESC. */ + +static void +sfnt_dereference_font_tables (struct sfnt_font_desc *desc) +{ + if (!desc->refcount) + emacs_abort (); + + if (--desc->refcount) + return; + + sfnt_close_tables (desc->tables); + desc->tables = NULL; + return; +} + +/* Open the font corresponding to the font-entity FONT_ENTITY. Return + nil upon failure, else the opened font-object. */ + +Lisp_Object +sfntfont_open (struct frame *f, Lisp_Object font_entity, + int pixel_size) +{ + struct sfnt_font_info *font_info; + struct font *font; + struct sfnt_font_desc *desc; + Lisp_Object font_object; + struct charset *charset; + int point_size; + Display_Info *dpyinfo; + struct sfnt_font_tables *tables; + + if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0) + pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)); + else if (pixel_size == 0) + { + /* This bit was copied from xfont.c. The values might need + adjustment. */ + + if (FRAME_FONT (f)) + pixel_size = FRAME_FONT (f)->pixel_size; + else + pixel_size = 12; + } + + /* Now find the font description corresponding to FONT_ENTITY. */ + + if (NILP (AREF (font_entity, FONT_EXTRA_INDEX))) + return Qnil; + + desc = xmint_pointer (XCDR (XCAR (AREF (font_entity, FONT_EXTRA_INDEX)))); + + /* Build the font object. */ + font_object = font_make_object (VECSIZE (struct sfnt_font_info), + font_entity, pixel_size); + font_info = (struct sfnt_font_info *) XFONT_OBJECT (font_object); + + block_input (); + + /* Initialize all the font driver specific data. */ + + font_info->cmap = NULL; + font_info->hhea = NULL; + font_info->maxp = NULL; + font_info->head = NULL; + font_info->glyf = NULL; + font_info->hmtx = NULL; + font_info->loca_short = NULL; + font_info->loca_long = NULL; + font_info->cmap_data = NULL; + font_info->prep = NULL; + font_info->fpgm = NULL; + font_info->cvt = NULL; + font_info->uvs = NULL; + + font_info->outline_cache.next = &font_info->outline_cache; + font_info->outline_cache.last = &font_info->outline_cache; + font_info->outline_cache_size = 0; + font_info->raster_cache.next = &font_info->raster_cache; + font_info->raster_cache.last = &font_info->raster_cache; + font_info->raster_cache_size = 0; + font_info->interpreter = NULL; +#ifdef HAVE_MMAP + font_info->glyf_table_mapped = false; +#endif /* HAVE_MMAP */ +#ifdef HAVE_HARFBUZZ + font_info->hb_font = NULL; + font_info->fd = -1; + font_info->directory = NULL; +#endif /* HAVE_HARFBUZZ */ + + /* Read required tables. This font backend is supposed to be used + mostly on devices with flash memory, so the order in which they + are read is insignificant. */ + + tables = sfnt_reference_font_tables (desc); + + if (!tables) + goto bail; + + /* Copy fields from the table structure to the font for fast + access. */ + font_info->cmap = tables->cmap; + font_info->hhea = tables->hhea; + font_info->maxp = tables->maxp; + font_info->head = tables->head; + font_info->hmtx = tables->hmtx; + font_info->glyf = tables->glyf; + font_info->loca_short = tables->loca_short; + font_info->loca_long = tables->loca_long; + font_info->prep = tables->prep; + font_info->fpgm = tables->fpgm; + font_info->cvt = tables->cvt ; + font_info->cmap_data = tables->cmap_data; + font_info->cmap_subtable = tables->cmap_subtable; + font_info->uvs = tables->uvs; + /* Fill in font data. */ font = &font_info->font; font->pixel_size = pixel_size; @@ -2535,22 +2757,19 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, point_size = PIXEL_TO_POINT (pixel_size, (dpyinfo->resx * dpyinfo->resy / 2)); - sfntfont_setup_interpreter (fd, font_info, subtable, - point_size); + sfntfont_setup_interpreter (font_info, point_size); -#ifndef HAVE_HARFBUZZ - /* Close the font file descriptor. */ - emacs_close (fd); - - /* Free the offset subtable. */ - xfree (subtable); -#else /* HAVE_HARFBUZZ */ +#ifdef HAVE_HARFBUZZ /* HarfBuzz will potentially read font tables after the font has been opened by Emacs. Keep the font open, and record its offset subtable. */ - font_info->fd = fd; - font_info->directory = subtable; -#endif /* !HAVE_HARFBUZZ */ + font_info->fd = tables->fd; + font_info->directory = tables->directory; +#endif /* HAVE_HARFBUZZ */ + + /* Set font->desc so that font tables can be dereferenced if + anything goes wrong. */ + font_info->desc = desc; #ifdef HAVE_MMAP /* Link the font onto the font table. */ @@ -2563,50 +2782,8 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, return font_object; bail6: - xfree (font_info->hmtx); - font_info->hmtx = NULL; - bail5: - xfree (font_info->loca_long); - xfree (font_info->loca_short); - font_info->loca_long = NULL; - font_info->loca_short = NULL; - bail4: - xfree (font_info->hhea); - xfree (font_info->maxp); - -#ifdef HAVE_MMAP - if (font_info->glyf_table_mapped) - { - rc = sfnt_unmap_glyf_table (font_info->glyf); - - if (rc) - emacs_abort (); - } - else -#endif /* HAVE_MMAP */ - xfree (font_info->glyf); - - xfree (font_info->head); - font_info->hhea = NULL; - font_info->maxp = NULL; - font_info->glyf = NULL; - font_info->head = NULL; - - /* This comes in bail4 due to a peculiarity of how the four tables - above are validated. */ - xfree (font_info->cmap_data); - font_info->cmap_data = NULL; - bail3: - - if (font_info->uvs) - sfnt_free_uvs_context (font_info->uvs); - - xfree (font_info->cmap); - font_info->cmap = NULL; - bail2: - xfree (subtable); - bail1: - emacs_close (fd); + sfnt_dereference_font_tables (desc); + font_info->desc = NULL; bail: unblock_input (); return Qnil; @@ -2784,42 +2961,18 @@ sfntfont_close (struct font *font) struct sfnt_font_info *info; #ifdef HAVE_MMAP struct sfnt_font_info **next; - int rc; #endif /* HAVE_MMAP */ info = (struct sfnt_font_info *) font; - xfree (info->cmap); - xfree (info->hhea); - xfree (info->maxp); - xfree (info->head); - xfree (info->hmtx); -#ifdef HAVE_MMAP - if (info->glyf_table_mapped && info->glyf) - { - rc = sfnt_unmap_glyf_table (info->glyf); + /* If info->desc is still set, dereference the font tables. */ + if (info->desc) + sfnt_dereference_font_tables (info->desc); + info->desc = NULL; - if (rc) - emacs_abort (); - } - else -#endif /* HAVE_MMAP */ - xfree (info->glyf); - - xfree (info->loca_short); - xfree (info->loca_long); - xfree (info->cmap_data); - xfree (info->prep); - xfree (info->fpgm); - xfree (info->cvt); + /* Free the interpreter, which is created on a per font basis. */ xfree (info->interpreter); - /* Deallocate any UVS context allocated to look up font variation - sequences. */ - - if (info->uvs) - sfnt_free_uvs_context (info->uvs); - /* Clear these fields. It seems that close can be called twice, once during font driver destruction, and once during GC. */ @@ -2853,17 +3006,11 @@ sfntfont_close (struct font *font) #endif /* HAVE_MMAP */ #ifdef HAVE_HARFBUZZ - /* Close the font file. */ - - if (info->fd != -1) - { - emacs_close (info->fd); - info->fd = -1; - } - - /* Free its table directory. */ - xfree (info->directory); + /* These fields will be freed or closed by + sfnt_dereference_font_tables, but clear them here for good + measure. */ info->directory = NULL; + info->fd = -1; /* Free any hb_font created. */ commit a1c5461edabb8b4c336a708c84aa65e28b2695b0 Merge: c0873f2382f a27b0f7f307 Author: Po Lu Date: Sun Mar 26 14:09:56 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit c0873f2382f575b08b5c9eb6663d7a7acf7ece65 Author: Po Lu Date: Sun Mar 26 14:09:15 2023 +0800 Update Android port * src/sfnt.c (sfnt_table_names): Add avar. (sfnt_read_glyph): Clear distortion fields. (sfnt_build_glyph_outline): Calculate the outline origin point. (sfnt_prepare_raster): Apply the origin point to the X axis offset. (sfnt_scale_metrics_to_pixel_size): New function. (sfnt_build_instructed_outline): Use instructed origin phantom point to determine the outline origin. (sfnt_compute_phantom_points): Apply origin and advance distortion. (struct sfnt_variation_axis, struct sfnt_instance) (struct sfnt_fvar_table, sfnt_read_fvar_table) (struct sfnt_gvar_table, sfnt_read_gvar_table) (sfnt_read_avar_table, struct sfnt_blend, sfnt_init_blend) (sfnt_free_blend, sfnt_normalize_blend, struct sfnt_tuple_header) (struct sfnt_gvar_glyph_header, sfnt_read_packed_deltas) (sfnt_compute_tuple_scale, sfnt_read_cvar_table) (sfnt_infer_deltas_1, sfnt_vary_simple_glyph, sfnt_infer_deltas) (sfnt_vary_glyph, sfnt_vary_compound_glyph) (sfnt_vary_interpreter): New functions. Add structs to sfntfont.h. (struct sfnt_test_dcontext, sfnt_test_get_glyph, main): Test distortable font handling. * src/sfnt.h (SFNT_ENABLE_HINTING): (enum sfnt_table): (struct sfnt_glyph): (struct sfnt_glyph_outline): (struct sfnt_raster): (struct sfnt_default_uvs_table): (struct sfnt_unicode_value_range): (struct sfnt_nondefault_uvs_table): (struct sfnt_uvs_mapping): (struct sfnt_mapped_variation_selector_record): (struct sfnt_table_offset_rec): (struct sfnt_uvs_context): (struct sfnt_mapped_table): (struct sfnt_variation_axis): (struct sfnt_instance): (struct sfnt_fvar_table): (struct sfnt_short_frac_correspondence): (struct sfnt_short_frac_segment): (struct sfnt_avar_table): (struct sfnt_tuple_variation): (struct sfnt_cvar_table): (struct sfnt_gvar_table): (struct sfnt_blend): (struct sfnt_metrics_distortion): (PROTOTYPE): Update prototypes. * src/sfntfont.c (sfntfont_get_glyph_outline): (sfntfont_measure_pcm): Adjust calls. diff --git a/src/sfnt.c b/src/sfnt.c index 21b2ea96e1c..8e7a30e3b05 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -153,6 +153,7 @@ #define MAX(a, b) ((a) > (b) ? (a) : (b)) [SFNT_TABLE_FVAR] = 0x66766172, [SFNT_TABLE_GVAR] = 0x67766172, [SFNT_TABLE_CVAR] = 0x63766172, + [SFNT_TABLE_AVAR] = 0x61766172, }; /* Swap values from TrueType to system byte order. */ @@ -2377,6 +2378,10 @@ sfnt_read_glyph (sfnt_glyph glyph_code, sfnt_swap16 (&glyph.xmax); sfnt_swap16 (&glyph.ymax); + /* This is set later on after `sfnt_vary_X_glyph'. */ + glyph.advance_distortion = 0; + glyph.origin_distortion = 0; + /* Figure out what needs to be read based on glyph.number_of_contours. */ if (glyph.number_of_contours >= 0) @@ -3758,6 +3763,9 @@ sfnt_curve_to_and_build (struct sfnt_point control, given pixel size. Return the outline data with a refcount of 0 upon success, or NULL upon failure. + Use the scaled glyph METRICS to determine the origin point of the + outline. + Call GET_GLYPH and FREE_GLYPH with the specified DCONTEXT to obtain glyphs for compound glyph subcomponents. @@ -3767,12 +3775,14 @@ sfnt_curve_to_and_build (struct sfnt_point control, sfnt_build_glyph_outline (struct sfnt_glyph *glyph, struct sfnt_head_table *head, int pixel_size, + struct sfnt_glyph_metrics *metrics, sfnt_get_glyph_proc get_glyph, sfnt_free_glyph_proc free_glyph, void *dcontext) { struct sfnt_glyph_outline *outline; int rc; + sfnt_fword origin; memset (&build_outline_context, 0, sizeof build_outline_context); @@ -3825,6 +3835,12 @@ sfnt_build_glyph_outline (struct sfnt_glyph *glyph, return NULL; } + /* Compute the origin position. */ + origin = outline->xmin - metrics->lbearing; + outline->origin + = (origin + sfnt_mul_fixed (glyph->origin_distortion, + build_outline_context.factor)); + return outline; } @@ -3887,7 +3903,11 @@ sfnt_prepare_raster (struct sfnt_raster *raster, + (SFNT_POLY_ALIGNMENT - 1)) & ~(SFNT_POLY_ALIGNMENT - 1)); - raster->offx = sfnt_floor_fixed (outline->xmin) >> 16; + /* Apply outline->origin. This is 0 by convention in most fonts. + However, variable fonts typically change this as variations are + applied. */ + raster->offx = sfnt_floor_fixed (outline->xmin + - outline->origin) >> 16; raster->offy = sfnt_floor_fixed (outline->ymin) >> 16; } @@ -4516,6 +4536,23 @@ sfnt_scale_metrics (struct sfnt_glyph_metrics *metrics, = sfnt_mul_fixed (metrics->advance * 65536, factor); } +/* Like `sfnt_scale_metrics', except it scales the specified metrics + by a factor calculated using the given PPEM and HEAD table's UPEM + value. */ + +MAYBE_UNUSED TEST_STATIC void +sfnt_scale_metrics_to_pixel_size (struct sfnt_glyph_metrics *metrics, + int ppem, + struct sfnt_head_table *head) +{ + sfnt_fixed factor; + + /* Now calculate the factor scale lbearing and advance up to the + given PPEM size. */ + factor = sfnt_div_fixed (ppem, head->units_per_em); + sfnt_scale_metrics (metrics, factor); +} + /* Font style parsing. */ @@ -4949,8 +4986,6 @@ sfnt_read_ttc_header (int fd) -#ifdef SFNT_ENABLE_HINTING - /* TrueType hinting support. If you do not read the code in this section in conjunction with @@ -10897,6 +10932,15 @@ sfnt_build_instructed_outline (struct sfnt_instructed_outline *instructed) inside sfnt_decompose_glyph. */ outline = build_outline_context.outline; + /* Finally, obtain the origin point of the glyph after it has been + instructed. */ + + if (instructed->num_points > 1) + outline->origin + = instructed->x_points[instructed->num_points - 2]; + else + outline->origin = 0; + if (rc) { xfree (outline); @@ -10935,6 +10979,10 @@ sfnt_compute_phantom_points (struct sfnt_glyph *glyph, f1 = glyph->xmin - metrics->lbearing; f2 = f1 + metrics->advance; + /* Apply the metrics distortion. */ + f1 += glyph->origin_distortion; + f2 += glyph->advance_distortion; + /* Next, scale both up. */ *x1 = sfnt_mul_f26dot6_fixed (f1 * 64, scale); *x2 = sfnt_mul_f26dot6_fixed (f2 * 64, scale); @@ -11881,7 +11929,6 @@ sfnt_interpret_compound_glyph (struct sfnt_glyph *glyph, return NULL; } -#endif /* SFNT_ENABLE_HINTING */ @@ -12428,8 +12475,6 @@ sfnt_read_table (int fd, struct sfnt_offset_subtable *subtable, #endif /* !TEST */ -#ifdef TEST - /* Glyph variations. Instead of defining separate fonts for each @@ -12442,83 +12487,12 @@ sfnt_read_table (int fd, struct sfnt_offset_subtable *subtable, variation), `gvar' (glyph variation) and `cvar' (CVT variation) tables in a font file. */ -struct sfnt_variation_axis -{ - /* The axis tag. */ - uint32_t axis_tag; - - /* The minimum style coordinate for the axis. */ - sfnt_fixed min_value; - - /* The default style coordinate for the axis. */ - sfnt_fixed default_value; - - /* The maximum style coordinate for the axis. */ - sfnt_fixed max_value; - - /* Set to zero. */ - uint16_t flags; - - /* Identifier under which this axis's name will be found in the - `name' table. */ - uint16_t name_id; -}; - -struct sfnt_instance -{ - /* The instance name ID. */ - uint16_t name_id; - - /* Flags. */ - uint16_t flags; - - /* Optional PostScript name. */ - uint16_t ps_name_id; - - /* Coordinates of each defined instance. */ - sfnt_fixed *coords; -}; - -struct sfnt_fvar_table -{ - /* Major version; should be 1. */ - uint16_t major_version; - - /* Minor version; should be 0. */ - uint16_t minor_version; - - /* Offset in bytes from the beginning of the table to the beginning - of the first axis data. */ - uint16_t offset_to_data; - - /* Reserved field; always 2. */ - uint16_t count_size_pairs; - - /* Number of style axes in this font. */ - uint16_t axis_count; - - /* The number of bytes in each variation axis record. Currently 20 - bytes. */ - uint16_t axis_size; - - /* The number of named instances for the font found in the - instance array. */ - uint16_t instance_count; - - /* The size of each instance record. */ - uint16_t instance_size; - - /* Variable length data. */ - struct sfnt_variation_axis *axis; - struct sfnt_instance *instance; -}; - /* Read an fvar table from the given font FD. Use the table directory specified in SUBTABLE. Return the fvar table upon success, else NULL. */ -static struct sfnt_fvar_table * +TEST_STATIC struct sfnt_fvar_table * sfnt_read_fvar_table (int fd, struct sfnt_offset_subtable *subtable) { struct sfnt_table_directory *directory; @@ -12745,60 +12719,12 @@ sfnt_read_fvar_table (int fd, struct sfnt_offset_subtable *subtable) -struct sfnt_gvar_table -{ - /* Version of the glyph variations table. */ - uint16_t version; - - /* Reserved, currently 0. */ - uint16_t reserved; - - /* The number of style axes for this font. This must be the same - number as axisCount in the 'fvar' table. */ - uint16_t axis_count; - - /* The number of shared coordinates. */ - uint16_t shared_coord_count; - - /* Byte offset from the beginning of this table to the list of - shared style coordinates. */ - uint32_t offset_to_coord; - - /* The number of glyphs in this font; this should match the number - of the glyphs store elsewhere in the font. */ - uint16_t glyph_count; - - /* Bit-field that gives the format of the offset array that - follows. If the flag is 0, the type is uint16. If the flag is 1, - the type is unit 32. */ - uint16_t flags; - - /* Byte offset from the beginning of this table to the first glyph - glyphVariationData. */ - uint32_t offset_to_data; - - /* Number of bytes in the glyph variation data. */ - size_t data_size; - - /* Byte offsets from the beginning of the glyphVariationData array - to the glyphVariationData for each glyph in the font. The format - of this field is set by the flags field. */ - union { - uint16_t *offset_word; - uint32_t *offset_long; - } u; - - /* Other variable length data. */ - sfnt_f2dot14 *global_coords; - unsigned char *glyph_variation_data; -}; - /* Read a gvar table from the given font FD. Use the table directory specified in SUBTABLE. Return the gvar table upon success, else NULL. */ -static struct sfnt_gvar_table * +TEST_STATIC struct sfnt_gvar_table * sfnt_read_gvar_table (int fd, struct sfnt_offset_subtable *subtable) { struct sfnt_table_directory *directory; @@ -12956,160 +12882,130 @@ sfnt_read_gvar_table (int fd, struct sfnt_offset_subtable *subtable) -/* Structure repesenting a set of axis coordinates and their - normalized equivalents. +/* Read an avar table from the given font FD. Use the table directory + specified in SUBTABLE. - To use this structure, call + Return the avar table upon success, else NULL. */ - sfnt_init_blend (&blend, fvar, gvar) +TEST_STATIC struct sfnt_avar_table * +sfnt_read_avar_table (int fd, struct sfnt_offset_subtable *subtable) +{ + struct sfnt_table_directory *directory; + struct sfnt_avar_table *avar; + ssize_t rc; + size_t min_size, size, i, k, j; + uint16_t *buffer; + struct sfnt_short_frac_correspondence *correspondences; - on a `struct sfnt_blend *', with an appropriate fvar and gvar - table. + /* Find the table in the directory. */ - Then, fill in blend.coords with the un-normalized coordinates, - and call + directory = sfnt_find_table (subtable, SFNT_TABLE_AVAR); - sfnt_normalize_blend (&blend) + if (!directory) + return NULL; - finally, call sfnt_vary_glyph and related functions. */ + min_size = SFNT_ENDOF (struct sfnt_avar_table, axis_count, uint32_t); -struct sfnt_blend -{ - /* The fvar table. This determines the number of elements in each - of the arrays below. */ - struct sfnt_fvar_table *fvar; + /* Check that the length is at least min_size. */ + if (directory->length < min_size) + return NULL; - /* The gvar table. This provides the glyph variation data. */ - struct sfnt_gvar_table *gvar; + /* Seek to the location given in the directory. */ + if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1) + return NULL; - /* Un-normalized coordinates. */ - sfnt_fixed *coords; + /* Allocate enough to hold the avar table header. */ + avar = xmalloc (sizeof *avar); - /* Normalized coordinates. */ - sfnt_fixed *norm_coords; -}; + /* Read the avar table header. */ + rc = read (fd, avar, min_size); + if (rc != min_size) + goto bail; -/* Initialize the specified BLEND with the given FVAR and GVAR - tables. */ + /* Swap what was read. */ + sfnt_swap32 (&avar->version); + sfnt_swap32 (&avar->axis_count); -static void -sfnt_init_blend (struct sfnt_blend *blend, struct sfnt_fvar_table *fvar, - struct sfnt_gvar_table *gvar) -{ - size_t size; + if (avar->version != 0x00010000) + goto bail; - blend->fvar = fvar; - blend->gvar = gvar; + if (avar->axis_count < 0) + goto bail; - /* Allocate a single array to hold both coords and norm_coords. */ - size = (fvar->axis_count * sizeof *blend->coords * 2); - blend->coords = xmalloc (size); - blend->norm_coords = blend->coords + fvar->axis_count; -} + /* Allocate a buffer that holds the rest of the data. */ + size = directory->length - min_size; + buffer = xmalloc (size); + rc = read (fd, buffer, size); + if (rc != size) + goto bail1; -/* Free what was initialized in the specified BLEND. */ + /* Swap each word. */ + for (i = 0; i < size / sizeof *buffer; ++i) + sfnt_swap16 (&buffer[i]); -static void -sfnt_free_blend (struct sfnt_blend *blend) -{ - xfree (blend->coords); -} + /* Now, determine how big the resulting data needs to be. Each + struct has a pointer field, and that should be its alignment. */ -/* Normalize BLEND->fvar->axis_count coordinates in BLEND->coords and - place the result in BLEND->norm_coords. */ + k = 0; + min_size = sizeof *avar; + for (i = 0; i < avar->axis_count; ++i) + { + /* Check that k remains within bounds. */ + if (k >= size / sizeof *buffer) + goto bail1; -static void -sfnt_normalize_blend (struct sfnt_blend *blend) -{ - struct sfnt_variation_axis *axis; - int i; - sfnt_fixed coord; + /* Now add one struct sfnt_short_frac_segment for each axis and + each of its correspondences. */ + if (INT_ADD_WRAPV (sizeof (struct sfnt_short_frac_segment), + min_size, &min_size) + || INT_ADD_WRAPV (sizeof (struct sfnt_short_frac_correspondence) + * buffer[k], min_size, &min_size)) + goto bail1; - /* For each axis... */ - for (i = 0; i < blend->fvar->axis_count; ++i) - { - /* Normalize based on [min, default, max], into [-1, 0, 1]. */ - axis = &blend->fvar->axis[i]; + /* Verify that words from here to buffer[1 + buffer[k] * 2], the + next pairCount field, are within bounds. */ + if (k + 1 + buffer[k] * 2 > size / sizeof *buffer) + goto bail1; - /* Load the current design coordinate. */ - coord = blend->coords[i]; + /* Move to the next pairCount field. */ + k += 1 + buffer[k] * 2; + } - /* Keep it within bounds. */ + /* Resize avar to min_size and start filling in various + pointers. */ + avar = xrealloc (avar, min_size); + avar->segments = (struct sfnt_short_frac_segment *) (avar + 1); + correspondences + = ((struct sfnt_short_frac_correspondence *) (avar->segments + + avar->axis_count)); - if (coord > axis->max_value) - coord = axis->max_value; - else if (coord < axis->min_value) - coord = axis->min_value; + k = 0; + for (i = 0; i < avar->axis_count; ++i) + { + avar->segments[i].pair_count = buffer[k++]; + avar->segments[i].correspondence = correspondences; - if (coord > axis->default_value) - { - /* Avoid division by 0. */ - if (axis->max_value != axis->default_value) - blend->norm_coords[i] - = sfnt_div_fixed (sfnt_sub (coord, axis->default_value), - sfnt_sub (axis->max_value, - axis->default_value)); - else - blend->norm_coords[i] = 0; - } - else if (coord < axis->default_value) + for (j = 0; j < avar->segments[i].pair_count; ++j) { - if (axis->default_value != axis->min_value) - blend->norm_coords[i] - = sfnt_div_fixed (sfnt_sub (coord, axis->default_value), - sfnt_sub (axis->default_value, - axis->min_value)); - else - blend->norm_coords[i] = 0; + correspondences->from_coord = buffer[k++]; + correspondences->to_coord = buffer[k++]; + correspondences++; } - else - blend->norm_coords[i] = 0; } - /* TODO: process avar tables. */ + /* Return the read avar table. Free buffer. */ + xfree (buffer); + return avar; + + bail1: + xfree (buffer); + bail: + xfree (avar); + return NULL; } -struct sfnt_tuple_header -{ - /* The size in bytes of the serialized data for this tuple variation - table. */ - uint16_t variation_data_size; - - /* A packed field. The high 4 bits are flags (see below). The low 12 - bits are an index into a shared tuple records array. */ - uint16_t tuple_index; - - /* Embedded coordinate tuples, if any. */ - sfnt_f2dot14 *embedded_coord; - - /* Intermediate coordinate tuples, if any. */ - sfnt_f2dot14 *intermediate_coord; - - /* Number of points associated with this tuple. - Times two, the number of deltas associated with this tuple. */ - uint16_t npoints; - - /* Points associated with this tuple. */ - uint16_t *points; - - /* Deltas associated with this tuple. */ - sfnt_fword *deltas; -}; - -struct sfnt_gvar_glyph_header -{ - /* A packed field. The high 4 bits are flags and the low 12 bits are - the number of tuples for this glyph. The number of tuples can be - any number between 1 and 4095. */ - uint16_t tuple_count; - - /* Offset from the start of the GlyphVariationData table to the - serialized data. */ - uint16_t data_offset; -}; - /* Read a sequence of packed points starting from DATA. Return the number of points read in *NPOINTS_RETURN and the array of unpacked points, or NULL upon failure. @@ -13234,6 +13130,7 @@ sfnt_read_packed_deltas (unsigned char *restrict data, sfnt_fword *deltas; int i, count; unsigned char control; + uint16_t value; if (data >= end) return NULL; @@ -13258,9 +13155,9 @@ sfnt_read_packed_deltas (unsigned char *restrict data, if (data + 1 >= end) goto fail; - deltas[i] = (signed char) *data++; - deltas[i] *= 65536; - deltas[i++] |= *data++; + value = *data++ << 8; + value |= *data++; + deltas[i++] = value; } else { @@ -13282,489 +13179,1323 @@ sfnt_read_packed_deltas (unsigned char *restrict data, return NULL; } -/* Given a BLEND containing normalized coordinates, an array of - BLEND->gvar->axis_count tuple coordinates, and, if INTERMEDIATE_P, - a range of tuple coordinates from INTERMEDIATE_START to - INTERMEDIATE_END, return the scaling factor to apply to deltas for - each corresponding point. */ +/* Read a cvar table from the given font FD. Use the table directory + specified in SUBTABLE, axis information provided in the fvar table + FVAR, and CVT information provided in the cvt table CVT. -static sfnt_fixed -sfnt_compute_tuple_scale (struct sfnt_blend *blend, bool intermediate_p, - sfnt_f2dot14 *coords, - sfnt_f2dot14 *intermediate_start, - sfnt_f2dot14 *intermediate_end) + Return the cvar table upon success, else NULL. */ + +TEST_STATIC struct sfnt_cvar_table * +sfnt_read_cvar_table (int fd, struct sfnt_offset_subtable *subtable, + struct sfnt_fvar_table *fvar, + struct sfnt_cvt_table *cvt) { - int i; - sfnt_fixed coord, start, end; - sfnt_fixed scale; + struct sfnt_table_directory *directory; + struct sfnt_cvar_table *cvar; + ssize_t rc; + size_t ntuples, size; + int i, j; + sfnt_f2dot14 *coords; + uint16_t *local, *points, npoints, data_size, min_size, index; + unsigned char *buffer, *data, *end, *tuple; + ptrdiff_t data_offset; + sfnt_fword *deltas; - /* scale is initially 1.0. */ - scale = 0200000; + /* Find the table in the directory. */ - for (i = 0; i < blend->gvar->axis_count; ++i) - { - /* Load values for this axis, scaled up to sfnt_fixed. */ - coord = coords[i] * 4; + directory = sfnt_find_table (subtable, SFNT_TABLE_CVAR); - if (intermediate_p) - { - start = intermediate_start[i] * 4; - end = intermediate_start[i] * 4; - } + if (!directory) + return NULL; - /* Ignore tuples that can be skipped. */ + min_size = SFNT_ENDOF (struct sfnt_cvar_table, data_offset, + uint16_t); - if (!coord) - continue; + /* Check that the length is at least min_size. */ + if (directory->length < min_size) + return NULL; - /* If the coordinate is set to 0, then deltas should not be - applied. Return 0. */ + /* Seek to the location given in the directory. */ + if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1) + return NULL; - if (!blend->norm_coords[i]) - return 0; + /* Allocate enough to hold the cvar table header. */ + cvar = xmalloc (sizeof *cvar); - /* If no scaling need take place, continue. */ + /* Read the cvar table header. */ + rc = read (fd, cvar, min_size); + if (rc != min_size) + goto bail; - if (blend->norm_coords[i] == coord) - continue; + /* Swap what was read. */ + sfnt_swap32 (&cvar->version); + sfnt_swap16 (&cvar->tuple_count); + sfnt_swap16 (&cvar->data_offset); + + /* Read the rest of the table. */ + size = directory->length - min_size; + buffer = xmalloc (size); + rc = read (fd, buffer, size); + if (rc != size) + goto bail; - if (!intermediate_p) - { - /* Not an intermediate tuple; if coord is less than 0 and - blend->norm_coords[i] < coord, or coord is more than 0 - and blend->norm_coords[i] > coord, then it doesn't fit, - so return. */ + /* Now figure out how large cvar must be by reading the tuples. */ - if (blend->norm_coords[i] < MIN (0, coord) - || blend->norm_coords[i] > MAX (0, coord)) - return 0; + ntuples = cvar->tuple_count & 0x0fff; + data_offset = ((ptrdiff_t) cvar->data_offset + - (ptrdiff_t) min_size); + end = buffer + size; - scale = sfnt_multiply_divide_signed (scale, - blend->norm_coords[i], - coord); - } - else - { - /* Otherwise, renormalize between start and end. */ + if (data_offset < 0) + goto bail1; - if (blend->norm_coords[i] < start - || blend->norm_coords[i] > end) - return 0; + /* See if there are shared points, and read them if there are. */ - if (blend->norm_coords[i] < coord) - scale = sfnt_multiply_divide (scale, - blend->norm_coords[i] - start, - coord - start); - else - scale = sfnt_multiply_divide (scale, - end - blend->norm_coords[i], - end - coord); - } - } + data = buffer + data_offset; + tuple = buffer; + points = NULL; - return scale; -} + if (cvar->tuple_count & 0x8000) + { + points = sfnt_read_packed_points (data, &npoints, end, + &tuple); + if (!points) + goto bail1; -/* Infer point positions for points that have been partially moved - within the contour in GLYPH denoted by START and END. */ + /* Add npoints words to the size. */ + size = npoints * sizeof *points; + } + else + size = 0; -static void -sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start, - size_t end, bool *touched, sfnt_fword *x, - sfnt_fword *y) -{ - size_t i, pair_start, pair_end, pair_first, j; - sfnt_fword min_pos, max_pos, position; - sfnt_fixed ratio, delta; + while (ntuples--) + { + data = buffer + data_offset; - pair_start = pair_first = -1; + /* Read the tuple. */ + if (tuple + 3 >= end) + goto bail2; - /* Look for pairs of touched points. */ + memcpy (&data_size, tuple, sizeof data_size); + tuple += sizeof data_size; + memcpy (&index, tuple, sizeof index); + tuple += sizeof index; + sfnt_swap16 (&data_size); + sfnt_swap16 (&index); - for (i = start; i <= end; ++i) - { - if (!touched[i]) - continue; + /* Increment the offset to the data by the data size specified + here. */ + data_offset += data_size; - if (pair_start == -1) + if (index & 0x8000) { - pair_first = i; - goto next; - } + /* Embedded coordinates are present. Read each + coordinate and add it to the size. */ - pair_end = i; + if (tuple + fvar->axis_count * sizeof *coords - 1 >= end) + goto bail2; - /* pair_start to pair_end are now a pair of points, where points - in between should be interpolated. */ + tuple += sizeof *coords * fvar->axis_count; + if (INT_ADD_WRAPV (size, sizeof *coords * fvar->axis_count, + &size)) + goto bail2; + } + else + /* This table is invalid, as cvar tables don't have global + coordinates. */ + goto bail2; - for (j = pair_start + 1; j < pair_end; ++j) + /* Now read indeterminate tuples if required. */ + if (index & 0x4000) { - /* Consider the X axis. Set min_pos and max_pos to the - smallest and greatest values along that axis. */ - min_pos = MIN (x[pair_start], x[pair_end]); - max_pos = MAX (x[pair_start], x[pair_end]); + tuple += fvar->axis_count * 4; + if (INT_ADD_WRAPV (size, fvar->axis_count * 4, &size)) + goto bail2; + } - /* Now see if the current point lies between min and - max... */ - if (x[j] >= min_pos && x[j] <= max_pos) - { - /* If min_pos and max_pos are the same, apply - pair_start's delta if it is identical to that of - pair_end, or apply nothing at all otherwise. */ + /* Add one point and one delta for each CVT element. */ + if (INT_ADD_WRAPV (size, cvt->num_elements * 4, &size)) + goto bail2; - if (min_pos == max_pos) - { - if ((glyph->simple->x_coordinates[pair_start] - - x[pair_start]) - == (glyph->simple->x_coordinates[pair_end] - - x[pair_end])) - glyph->simple->x_coordinates[j] - += (glyph->simple->x_coordinates[pair_start] - - x[pair_start]); + /* Now add the size of the tuple. */ + if (INT_ADD_WRAPV (size, sizeof *cvar->variation, &size)) + goto bail2; + } - continue; - } + if (INT_ADD_WRAPV (sizeof *cvar, size, &size)) + goto bail2; - /* Interpolate between min_pos and max_pos. */ - ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos) - * 65536), - (sfnt_sub (max_pos, min_pos) - * 65536)); + /* Reallocate cvar. */ + cvar = xrealloc (cvar, size); + ntuples = cvar->tuple_count & 0x0fff; + cvar->variation = (struct sfnt_tuple_variation *) (cvar + 1); + coords = (sfnt_f2dot14 *) (cvar->variation + ntuples); + tuple = buffer; - /* Load the current positions of pair_start and pair_end - along this axis. */ - min_pos = MIN (glyph->simple->x_coordinates[pair_start], - glyph->simple->x_coordinates[pair_end]); - max_pos = MAX (glyph->simple->x_coordinates[pair_start], - glyph->simple->x_coordinates[pair_end]); + data_offset = ((ptrdiff_t) cvar->data_offset + - (ptrdiff_t) min_size); - /* Lerp in between. */ - delta = sfnt_sub (max_pos, min_pos); - delta = sfnt_mul_fixed (ratio, delta); - glyph->simple->x_coordinates[j] = min_pos + delta; - } - else + /* Start reading the tuples into cvar. */ + for (i = 0; i < ntuples; ++i) + { + data = buffer + data_offset; + + /* Read the tuple. */ + if (tuple + 3 >= end) + goto bail2; + + memcpy (&data_size, tuple, sizeof data_size); + tuple += sizeof data_size; + memcpy (&index, tuple, sizeof index); + tuple += sizeof index; + sfnt_swap16 (&data_size); + sfnt_swap16 (&index); + + /* Increment the offset to the data by the data size specified + here. */ + data_offset += data_size; + + cvar->variation[i].intermediate_start = NULL; + cvar->variation[i].intermediate_end = NULL; + + if (index & 0x8000) + { + /* Embedded coordinates are present. Read each + coordinate. */ + cvar->variation[i].coordinates = coords; + + for (j = 0; j < fvar->axis_count; ++j) { - /* ... otheriwse, move point j by the delta of the - nearest touched point. */ + if (tuple + 1 >= end) + goto bail2; - if (x[j] >= max_pos) - { - position = MAX (glyph->simple->x_coordinates[pair_start], - glyph->simple->x_coordinates[pair_end]); - delta = position - max_pos; - } - else - { - position = MIN (glyph->simple->x_coordinates[pair_start], - glyph->simple->x_coordinates[pair_end]); - delta = position - min_pos; - } + memcpy (coords++, tuple, sizeof *coords); + tuple += sizeof *coords; + sfnt_swap16 (coords); + } + } + else + goto bail2; - glyph->simple->x_coordinates[j] = x[j] + delta; + /* Now read indeterminate tuples if required. */ + if (index & 0x4000) + { + cvar->variation[i].intermediate_start = coords; + + for (j = 0; j < fvar->axis_count; ++j) + { + if (tuple + 1 >= end) + goto bail2; + + memcpy (coords++, tuple, sizeof *coords); + tuple += sizeof *coords; + sfnt_swap16 (coords); } - /* Now, consider the Y axis. */ - min_pos = MIN (y[pair_start], y[pair_end]); - max_pos = MAX (y[pair_start], y[pair_end]); + cvar->variation[i].intermediate_end = coords; - /* Now see if the current point lies between min and - max... */ - if (y[j] >= min_pos && y[j] <= max_pos) + for (j = 0; j < fvar->axis_count; ++j) { - /* If min_pos and max_pos are the same, apply - pair_start's delta if it is identical to that of - pair_end, or apply nothing at all otherwise. */ + if (tuple + 1 >= end) + goto bail2; - if (min_pos == max_pos) - { - if ((glyph->simple->y_coordinates[pair_start] - - y[pair_start]) - == (glyph->simple->y_coordinates[pair_end] - - y[pair_end])) - glyph->simple->y_coordinates[j] - += (glyph->simple->y_coordinates[pair_start] - - y[pair_start]); + memcpy (coords++, tuple, sizeof *coords); + tuple += sizeof *coords; + sfnt_swap16 (coords); + } + } - continue; - } + /* Finally, read private ``point'' numbers. If this flag is not + set, use shared point numbers previously read. - /* Interpolate between min_pos and max_pos. */ - ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos) - * 65536), - (sfnt_sub (max_pos, min_pos) - * 65536)); + Read at most CVT->num_elements points, as that is all the + storage allocated. */ - /* Load the current positions of pair_start and pair_end - along this axis. */ - min_pos = MIN (glyph->simple->y_coordinates[pair_start], - glyph->simple->y_coordinates[pair_end]); - max_pos = MAX (glyph->simple->y_coordinates[pair_start], - glyph->simple->y_coordinates[pair_end]); + if (index & 0x2000) + { + local = sfnt_read_packed_points (data, &cvar->variation[i].num_points, + end, &data); + if (!local) + goto bail2; - /* Lerp in between. */ - delta = sfnt_sub (max_pos, min_pos); - delta = sfnt_mul_fixed (ratio, delta); - glyph->simple->y_coordinates[j] = min_pos + delta; + /* If points apply to all CVT indices, skip this part. */ + + if (cvar->variation[i].num_points != UINT16_MAX) + { + if (cvar->variation[i].num_points > cvt->num_elements) + cvar->variation[i].num_points = cvt->num_elements; + + cvar->variation[i].points = (uint16_t *) coords; + for (j = 0; j < cvar->variation[i].num_points; ++j) + *coords++ = local[j]; + xfree (local); } else - { - /* ... otheriwse, move point j by the delta of the - nearest touched point. */ + cvar->variation[i].points = NULL; + } + else + { + /* Copy in the shared point numbers instead. */ + cvar->variation[i].num_points = npoints; - if (y[j] >= max_pos) - { - position = MAX (glyph->simple->y_coordinates[pair_start], - glyph->simple->y_coordinates[pair_end]); - delta = position - max_pos; - } - else - { - position = MIN (glyph->simple->y_coordinates[pair_start], - glyph->simple->y_coordinates[pair_end]); - delta = position - min_pos; - } + if (npoints != UINT16_MAX) + { + if (cvar->variation[i].num_points > cvt->num_elements) + cvar->variation[i].num_points = cvt->num_elements; - glyph->simple->y_coordinates[j] = y[j] + delta; + cvar->variation[i].points = (uint16_t *) coords; + for (j = 0; j < cvar->variation[i].num_points; ++j) + *coords++ = points[j]; } + else + cvar->variation[i].points = NULL; } - next: - pair_start = i; - } - - /* If pair_start is set, then lerp points between it and - pair_first. */ + /* And read packed deltas. If cvar->variation[i].num_points is + UINT16_MAX, then there is one delta for each CVT entry. + Otherwise, there are that many deltas. */ - if (pair_start != (size_t) -1) - { - j = pair_start + 1; + if (cvar->variation[i].num_points == UINT16_MAX) + { + deltas = sfnt_read_packed_deltas (data, end, cvt->num_elements, + &data); - if (j > end) - j = start; + if (!deltas) + goto bail2; - pair_end = pair_first; + cvar->variation[i].deltas = coords; - while (j != pair_first) + for (j = 0; j < cvt->num_elements; ++j) + *coords++ = deltas[j]; + xfree (deltas); + } + else { - /* Consider the X axis. Set min_pos and max_pos to the - smallest and greatest values along that axis. */ - min_pos = MIN (x[pair_start], x[pair_end]); - max_pos = MAX (x[pair_start], x[pair_end]); + deltas = sfnt_read_packed_deltas (data, end, + cvar->variation[i].num_points, + &data); + if (!deltas) + goto bail2; - /* Now see if the current point lies between min and - max... */ - if (x[j] >= min_pos && x[j] <= max_pos) - { - /* If min_pos and max_pos are the same, apply - pair_start's delta if it is identical to that of - pair_end, or apply nothing at all otherwise. */ + cvar->variation[i].deltas = coords; - if (min_pos == max_pos) - { - if ((glyph->simple->x_coordinates[pair_start] - - x[pair_start]) - == (glyph->simple->x_coordinates[pair_end] - - x[pair_end])) - glyph->simple->x_coordinates[j] - += (glyph->simple->x_coordinates[pair_start] - - x[pair_start]); + for (j = 0; j < cvar->variation[i].num_points; ++j) + *coords++ = deltas[j]; + xfree (deltas); + } + } - goto next_1; - } + /* Free data and return the read cvar table. */ + if (points != (void *) -1) + xfree (points); + xfree (buffer); + return cvar; - /* Interpolate between min_pos and max_pos. */ - ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos) - * 65536), - (sfnt_sub (max_pos, min_pos) - * 65536)); + bail2: + if (points != (void *) -1) + xfree (points); + bail1: + xfree (buffer); + bail: + xfree (cvar); + return NULL; +} - /* Load the current positions of pair_start and pair_end - along this axis. */ - min_pos = MIN (glyph->simple->x_coordinates[pair_start], - glyph->simple->x_coordinates[pair_end]); - max_pos = MAX (glyph->simple->x_coordinates[pair_start], - glyph->simple->x_coordinates[pair_end]); + - /* Lerp in between. */ - delta = sfnt_sub (max_pos, min_pos); - delta = sfnt_mul_fixed (ratio, delta); - glyph->simple->x_coordinates[j] = min_pos + delta; - } - else - { - /* ... otheriwse, move point j by the delta of the - nearest touched point. */ +/* Initialize the specified BLEND with the given FVAR and GVAR tables. + If non-NULL, adjust normalized coordinates using the axis variation + table AVAR; similarly, adjust interpreter CVT values using CVAR, if + specified. */ - if (x[j] >= max_pos) - { - position = MAX (glyph->simple->x_coordinates[pair_start], - glyph->simple->x_coordinates[pair_end]); - delta = position - max_pos; - } - else +TEST_STATIC void +sfnt_init_blend (struct sfnt_blend *blend, struct sfnt_fvar_table *fvar, + struct sfnt_gvar_table *gvar, struct sfnt_avar_table *avar, + struct sfnt_cvar_table *cvar) +{ + size_t size; + + blend->fvar = fvar; + blend->gvar = gvar; + blend->avar = avar; + blend->cvar = cvar; + + /* Allocate a single array to hold both coords and norm_coords. */ + size = (fvar->axis_count * sizeof *blend->coords * 2); + blend->coords = xmalloc (size); + blend->norm_coords = blend->coords + fvar->axis_count; +} + +/* Free what was initialized in the specified BLEND. */ + +TEST_STATIC void +sfnt_free_blend (struct sfnt_blend *blend) +{ + xfree (blend->coords); +} + +/* Normalize BLEND->fvar->axis_count coordinates in BLEND->coords and + place the result in BLEND->norm_coords. */ + +TEST_STATIC void +sfnt_normalize_blend (struct sfnt_blend *blend) +{ + struct sfnt_variation_axis *axis; + int i, j; + sfnt_fixed from, coord, j0, j1, j2; + sfnt_fixed from_last, coord_last; + struct sfnt_short_frac_segment *segment; + + /* For each axis... */ + for (i = 0; i < blend->fvar->axis_count; ++i) + { + /* Normalize based on [min, default, max], into [-1, 0, 1]. */ + axis = &blend->fvar->axis[i]; + + /* Load the current design coordinate. */ + coord = blend->coords[i]; + + /* Keep it within bounds. */ + + if (coord > axis->max_value) + coord = axis->max_value; + else if (coord < axis->min_value) + coord = axis->min_value; + + if (coord > axis->default_value) + { + /* Avoid division by 0. */ + if (axis->max_value != axis->default_value) + blend->norm_coords[i] + = sfnt_div_fixed (sfnt_sub (coord, axis->default_value), + sfnt_sub (axis->max_value, + axis->default_value)); + else + blend->norm_coords[i] = 0; + } + else if (coord < axis->default_value) + { + if (axis->default_value != axis->min_value) + blend->norm_coords[i] + = sfnt_div_fixed (sfnt_sub (coord, axis->default_value), + sfnt_sub (axis->default_value, + axis->min_value)); + else + blend->norm_coords[i] = 0; + } + else + blend->norm_coords[i] = 0; + } + + /* Now, apply axis variations, but only if the avar table has the + right number of axes. */ + + if (blend->fvar->axis_count == blend->avar->axis_count) + { + for (i = 0; i < blend->fvar->axis_count; ++i) + { + segment = &blend->avar->segments[i]; + + /* Search for a correspondence record above the normalized + coordinate of this axis. */ + + for (j = 1; j < segment->pair_count; ++j) + { + from = segment->correspondence[j].from_coord * 4; + coord = segment->correspondence[j].to_coord * 4; + + if (blend->norm_coords[i] < from) { - position = MIN (glyph->simple->x_coordinates[pair_start], - glyph->simple->x_coordinates[pair_end]); - delta = position - min_pos; + from_last + = segment->correspondence[j - 1].from_coord * 4; + coord_last + = segment->correspondence[j - 1].to_coord * 4; + + j0 = blend->norm_coords[i] - from_last; + j1 = coord - coord_last; + j2 = from - from_last; + + blend->norm_coords[i] + = (sfnt_multiply_divide_signed (j0, j1, j2) + coord_last); + break; + } + } + } + } +} + + + +struct sfnt_gvar_glyph_header +{ + /* A packed field. The high 4 bits are flags and the low 12 bits are + the number of tuples for this glyph. The number of tuples can be + any number between 1 and 4095. */ + uint16_t tuple_count; + + /* Offset from the start of the GlyphVariationData table to the + serialized data. */ + uint16_t data_offset; +}; + +/* Given a BLEND containing normalized coordinates, an array of + BLEND->gvar->axis_count tuple coordinates, and, if INTERMEDIATE_P, + a range of tuple coordinates from INTERMEDIATE_START to + INTERMEDIATE_END, return the scaling factor to apply to deltas for + each corresponding point. */ + +static sfnt_fixed +sfnt_compute_tuple_scale (struct sfnt_blend *blend, bool intermediate_p, + sfnt_f2dot14 *coords, + sfnt_f2dot14 *intermediate_start, + sfnt_f2dot14 *intermediate_end) +{ + int i; + sfnt_fixed coord, start, end; + sfnt_fixed scale; + + /* scale is initially 1.0. */ + scale = 0200000; + + for (i = 0; i < blend->gvar->axis_count; ++i) + { + /* Load values for this axis, scaled up to sfnt_fixed. */ + coord = coords[i] * 4; + + if (intermediate_p) + { + start = intermediate_start[i] * 4; + end = intermediate_start[i] * 4; + } + + /* Ignore tuples that can be skipped. */ + + if (!coord) + continue; + + /* If the coordinate is set to 0, then deltas should not be + applied. Return 0. */ + + if (!blend->norm_coords[i]) + return 0; + + /* If no scaling need take place, continue. */ + + if (blend->norm_coords[i] == coord) + continue; + + if (!intermediate_p) + { + /* Not an intermediate tuple; if coord is less than 0 and + blend->norm_coords[i] < coord, or coord is more than 0 + and blend->norm_coords[i] > coord, then it doesn't fit, + so return. */ + + if (blend->norm_coords[i] < MIN (0, coord) + || blend->norm_coords[i] > MAX (0, coord)) + return 0; + + scale = sfnt_multiply_divide_signed (scale, + blend->norm_coords[i], + coord); + } + else + { + /* Otherwise, renormalize between start and end. */ + + if (blend->norm_coords[i] < start + || blend->norm_coords[i] > end) + return 0; + + if (blend->norm_coords[i] < coord) + scale = sfnt_multiply_divide (scale, + blend->norm_coords[i] - start, + coord - start); + else + scale = sfnt_multiply_divide (scale, + end - blend->norm_coords[i], + end - coord); + } + } + + return scale; +} + +/* Infer point positions for points that have been partially moved + within the contour in GLYPH denoted by START and END. */ + +static void +sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start, + size_t end, bool *touched, sfnt_fword *x, + sfnt_fword *y) +{ + size_t i, pair_start, pair_end, pair_first, j; + sfnt_fword min_pos, max_pos, position; + sfnt_fixed ratio, delta; + + pair_start = pair_first = -1; + + /* Look for pairs of touched points. */ + + for (i = start; i <= end; ++i) + { + if (!touched[i]) + continue; + + if (pair_start == -1) + { + pair_first = i; + goto next; + } + + pair_end = i; + + /* pair_start to pair_end are now a pair of points, where points + in between should be interpolated. */ + + for (j = pair_start + 1; j < pair_end; ++j) + { + /* Consider the X axis. Set min_pos and max_pos to the + smallest and greatest values along that axis. */ + min_pos = MIN (x[pair_start], x[pair_end]); + max_pos = MAX (x[pair_start], x[pair_end]); + + /* Now see if the current point lies between min and + max... */ + if (x[j] >= min_pos && x[j] <= max_pos) + { + /* If min_pos and max_pos are the same, apply + pair_start's delta if it is identical to that of + pair_end, or apply nothing at all otherwise. */ + + if (min_pos == max_pos) + { + if ((glyph->simple->x_coordinates[pair_start] + - x[pair_start]) + == (glyph->simple->x_coordinates[pair_end] + - x[pair_end])) + glyph->simple->x_coordinates[j] + += (glyph->simple->x_coordinates[pair_start] + - x[pair_start]); + + continue; } - glyph->simple->x_coordinates[j] = x[j] + delta; - } + /* Interpolate between min_pos and max_pos. */ + ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos) + * 65536), + (sfnt_sub (max_pos, min_pos) + * 65536)); + + /* Load the current positions of pair_start and pair_end + along this axis. */ + min_pos = MIN (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + max_pos = MAX (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + + /* Lerp in between. */ + delta = sfnt_sub (max_pos, min_pos); + delta = sfnt_mul_fixed (ratio, delta); + glyph->simple->x_coordinates[j] = min_pos + delta; + } + else + { + /* ... otheriwse, move point j by the delta of the + nearest touched point. */ + + if (x[j] >= max_pos) + { + position = MAX (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + delta = position - max_pos; + } + else + { + position = MIN (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + delta = position - min_pos; + } + + glyph->simple->x_coordinates[j] = x[j] + delta; + } + + /* Now, consider the Y axis. */ + min_pos = MIN (y[pair_start], y[pair_end]); + max_pos = MAX (y[pair_start], y[pair_end]); + + /* Now see if the current point lies between min and + max... */ + if (y[j] >= min_pos && y[j] <= max_pos) + { + /* If min_pos and max_pos are the same, apply + pair_start's delta if it is identical to that of + pair_end, or apply nothing at all otherwise. */ + + if (min_pos == max_pos) + { + if ((glyph->simple->y_coordinates[pair_start] + - y[pair_start]) + == (glyph->simple->y_coordinates[pair_end] + - y[pair_end])) + glyph->simple->y_coordinates[j] + += (glyph->simple->y_coordinates[pair_start] + - y[pair_start]); + + continue; + } + + /* Interpolate between min_pos and max_pos. */ + ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos) + * 65536), + (sfnt_sub (max_pos, min_pos) + * 65536)); + + /* Load the current positions of pair_start and pair_end + along this axis. */ + min_pos = MIN (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + max_pos = MAX (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + + /* Lerp in between. */ + delta = sfnt_sub (max_pos, min_pos); + delta = sfnt_mul_fixed (ratio, delta); + glyph->simple->y_coordinates[j] = min_pos + delta; + } + else + { + /* ... otheriwse, move point j by the delta of the + nearest touched point. */ + + if (y[j] >= max_pos) + { + position = MAX (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + delta = position - max_pos; + } + else + { + position = MIN (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + delta = position - min_pos; + } + + glyph->simple->y_coordinates[j] = y[j] + delta; + } + } + + next: + pair_start = i; + } + + /* If pair_start is set, then lerp points between it and + pair_first. */ + + if (pair_start != (size_t) -1) + { + j = pair_start + 1; + + if (j > end) + j = start; + + pair_end = pair_first; + + while (j != pair_first) + { + /* Consider the X axis. Set min_pos and max_pos to the + smallest and greatest values along that axis. */ + min_pos = MIN (x[pair_start], x[pair_end]); + max_pos = MAX (x[pair_start], x[pair_end]); + + /* Now see if the current point lies between min and + max... */ + if (x[j] >= min_pos && x[j] <= max_pos) + { + /* If min_pos and max_pos are the same, apply + pair_start's delta if it is identical to that of + pair_end, or apply nothing at all otherwise. */ + + if (min_pos == max_pos) + { + if ((glyph->simple->x_coordinates[pair_start] + - x[pair_start]) + == (glyph->simple->x_coordinates[pair_end] + - x[pair_end])) + glyph->simple->x_coordinates[j] + += (glyph->simple->x_coordinates[pair_start] + - x[pair_start]); + + goto next_1; + } + + /* Interpolate between min_pos and max_pos. */ + ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos) + * 65536), + (sfnt_sub (max_pos, min_pos) + * 65536)); + + /* Load the current positions of pair_start and pair_end + along this axis. */ + min_pos = MIN (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + max_pos = MAX (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + + /* Lerp in between. */ + delta = sfnt_sub (max_pos, min_pos); + delta = sfnt_mul_fixed (ratio, delta); + glyph->simple->x_coordinates[j] = min_pos + delta; + } + else + { + /* ... otheriwse, move point j by the delta of the + nearest touched point. */ + + if (x[j] >= max_pos) + { + position = MAX (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + delta = position - max_pos; + } + else + { + position = MIN (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + delta = position - min_pos; + } + + glyph->simple->x_coordinates[j] = x[j] + delta; + } + + /* Now, consider the Y axis. */ + min_pos = MIN (y[pair_start], y[pair_end]); + max_pos = MAX (y[pair_start], y[pair_end]); + + /* Now see if the current point lies between min and + max... */ + if (y[j] >= min_pos && y[j] <= max_pos) + { + /* If min_pos and max_pos are the same, apply + pair_start's delta if it is identical to that of + pair_end, or apply nothing at all otherwise. */ + + if (min_pos == max_pos) + { + if ((glyph->simple->y_coordinates[pair_start] + - y[pair_start]) + == (glyph->simple->y_coordinates[pair_end] + - y[pair_end])) + glyph->simple->y_coordinates[j] + += (glyph->simple->y_coordinates[pair_start] + - y[pair_start]); + + goto next_1; + } + + /* Interpolate between min_pos and max_pos. */ + ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos) + * 65536), + (sfnt_sub (max_pos, min_pos) + * 65536)); + + /* Load the current positions of pair_start and pair_end + along this axis. */ + min_pos = MIN (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + max_pos = MAX (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + + /* Lerp in between. */ + delta = sfnt_sub (max_pos, min_pos); + delta = sfnt_mul_fixed (ratio, delta); + glyph->simple->y_coordinates[j] = min_pos + delta; + } + else + { + /* ... otheriwse, move point j by the delta of the + nearest touched point. */ + + if (y[j] >= max_pos) + { + position = MAX (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + delta = position - max_pos; + } + else + { + position = MIN (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + delta = position - min_pos; + } + + glyph->simple->y_coordinates[j] = y[j] + delta; + } + + next_1: + j++; + if (j > end) + j = start; + } + } +} + +/* Infer point positions for contours that have been partially moved + by variation. For each contour in GLYPH, find pairs of points + which have had deltas applied. For each such pair, interpolate + points between the first point in the pair and the second by + considering each point along every one of the two axes (X and Y) + like so: + + - For each point that lies between the first point and the last + on the axis currently being considered, interpolate its + position in that axis so that the ratio between the first + point and the last in the original outline still holds. + + - For each point that lies to the left or top of the first point + on the axis being considered, use the delta of the first point. + + - And finally, for each point that lies to the right or bottom of + the last point on that axis, use the delta of the last + point. + + X and Y contain the original positions positions of each point. + TOUCHED contains whether or not each point has been changed by + an explicitly specified delta. + + Apply the inferred deltas back to GLYPH. */ + +static void +sfnt_infer_deltas (struct sfnt_glyph *glyph, bool *touched, + sfnt_fword *x, sfnt_fword *y) +{ + size_t i; + int point, first, end; + + point = 0; + for (i = 0; i < glyph->number_of_contours; ++i) + { + first = point; + end = glyph->simple->end_pts_of_contours[i]; + + /* Return if the glyph is invalid. */ + + if (first >= glyph->simple->number_of_points + || end >= glyph->simple->number_of_points + || first > end) + return; + + sfnt_infer_deltas_1 (glyph, first, end, touched, x, y); + point = end + 1; + } +} + +/* Read the glyph variation data for the specified glyph ID from + BLEND's gvar table. Apply the offsets to each point in the + specified simple GLYPH, based on the specified BLEND. + + Value is 0 upon success, else 1. + + The glyph variation data consists of a number of elements, each of + which has its own associated point numbers and deltas, and a list + of one or two coordinates for each axis. Each such list is + referred to as a ``tuple''. + + The deltas, one for each point, are multipled by the normalized + value of each axis and applied to those points for each tuple that + is found to be applicable. + + Each element of the glyph variation data is applicable to an axis + if its list of coordinates: + + - contains one element for each axis, and its axis has a value + between 0 and that element. + + - contains two elements for each axis, and its axis has a value + between the first element and the second. + + Return the deltas that would normally be applied to the two phantom + points describing horizontal bounds in *DISTORTION. Do not + transform the outline to reflect adjustments to the origin + point. */ + +TEST_STATIC int +sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id, + struct sfnt_glyph *glyph, + struct sfnt_metrics_distortion *distortion) +{ + uint32_t offset; + struct sfnt_gvar_glyph_header header; + uint16_t *points, npoints; + int i, ntuples, j, point_count; + unsigned char *tuple, *end, *data; + uint16_t data_size, index, *glyph_points; + sfnt_f2dot14 *restrict coords; + sfnt_f2dot14 *restrict intermediate_start; + sfnt_f2dot14 *restrict intermediate_end; + sfnt_fword *restrict dx, *restrict dy, fword; + struct sfnt_gvar_table *gvar; + uint16_t *local_points, n_local_points; + sfnt_fixed scale; + ptrdiff_t data_offset; + bool *touched; + sfnt_fword *restrict original_x, *restrict original_y; + + gvar = blend->gvar; + + if (gvar->axis_count != blend->fvar->axis_count) + return 1; + + if (gvar->glyph_count <= id) + return 1; + + if (gvar->flags & 1) + offset = gvar->u.offset_long[id]; + else + offset = gvar->u.offset_word[id] * 2u; + + if (offset >= gvar->data_size) + return 1; + + end = gvar->glyph_variation_data + gvar->data_size; + + /* Start reading the header. */ + + if (offset + sizeof header > gvar->data_size) + return 1; + + /* Clear the distortion. */ + distortion->origin = 0; + distortion->advance = 0; + + memcpy (&header, gvar->glyph_variation_data + offset, + sizeof header); + + /* Swap the header. */ + sfnt_swap16 (&header.tuple_count); + sfnt_swap16 (&header.data_offset); + + /* Prepare to read each tuple. */ + ntuples = header.tuple_count & 0x0fff; + + /* Initialize the data offset. This is incremented with each tuple + read. */ + data_offset = header.data_offset; + + /* If gvar->flags & tuples_share_point_numbers, read the shared + point numbers. */ + + npoints = 0; + + if (header.tuple_count & 0x8000) + { + data = gvar->glyph_variation_data + offset + data_offset; + points = sfnt_read_packed_points (data, &npoints, end, + &tuple); + + if (!points) + return 1; + + /* Shared point numbers are part of the data after the tuple + array. Thus, increment data_offset by tuple - data. `tuple' + here holds no relation to a pointer to the current part of + the tuple array that is being read later on. */ + data_offset += tuple - data; + } + else + points = NULL; + + /* Start reading each tuple. */ + tuple = gvar->glyph_variation_data + offset + sizeof header; + + if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) + coords = xmalloc (gvar->axis_count * sizeof *coords * 3); + else + coords = alloca (gvar->axis_count * sizeof *coords * 3); + + intermediate_start = coords + gvar->axis_count; + intermediate_end = coords + gvar->axis_count; + + /* Allocate arrays of booleans and fwords to keep track of which + points have been touched. */ + touched = NULL; + original_x = NULL; + original_y = NULL; + + while (ntuples--) + { + data = gvar->glyph_variation_data + offset + data_offset; + + if (tuple + 3 >= end) + goto fail1; + + memcpy (&data_size, tuple, sizeof data_size); + tuple += sizeof data_size; + memcpy (&index, tuple, sizeof index); + tuple += sizeof index; + sfnt_swap16 (&data_size); + sfnt_swap16 (&index); + + /* Increment the offset to the data by the data size specified + here. */ + data_offset += data_size; + + if (index & 0x8000) + { + /* Embedded coordinates are present. Read each + coordinate and add it to the tuple. */ + for (j = 0; j < gvar->axis_count; ++j) + { + if (tuple + 1 >= end) + goto fail1; + + memcpy (&coords[j], tuple, sizeof *coords); + tuple += sizeof *coords; + sfnt_swap16 (&coords[j]); + } + } + else if ((index & 0xfff) > gvar->shared_coord_count) + /* index exceeds the number of shared tuples present. */ + goto fail1; + else + /* index points into gvar->axis_count coordinates making up + the tuple. */ + memcpy (coords, (gvar->global_coords + + ((index & 0xfff) * gvar->axis_count)), + gvar->axis_count * sizeof *coords); + + /* Now read indeterminate tuples if required. */ + if (index & 0x4000) + { + for (j = 0; j < gvar->axis_count; ++j) + { + if (tuple + 1 >= end) + goto fail1; + + memcpy (&intermediate_start[j], tuple, + sizeof *intermediate_start); + tuple += sizeof *intermediate_start; + sfnt_swap16 (&intermediate_start[j]); + } + + for (j = 0; j < gvar->axis_count; ++j) + { + if (tuple + 1 >= end) + goto fail1; + + memcpy (&intermediate_end[j], tuple, + sizeof *intermediate_end); + tuple += sizeof *intermediate_end; + sfnt_swap16 (&intermediate_end[j]); + } + } + + /* See whether or not the tuple applies to the current variation + configuration, and how much to scale them by. */ + + scale = sfnt_compute_tuple_scale (blend, index & 0x1000, + coords, intermediate_start, + intermediate_end); + + if (!scale) + continue; + + local_points = NULL; + + /* Finally, read private point numbers. + Set local_points to those numbers; it will be freed + once the loop ends. */ + + if (index & 0x2000) + { + local_points = sfnt_read_packed_points (data, &n_local_points, + end, &data); + if (!local_points) + goto fail1; + + point_count = n_local_points; + glyph_points = local_points; + } + else + { + /* If there are no private point numbers, use global + points. */ + point_count = npoints; + glyph_points = points; + } + + /* Now, read packed deltas. */ + + dx = NULL; + dy = NULL; + + switch (point_count) + { + case UINT16_MAX: + /* Deltas are provided for all points in the glyph. + No glyph should have more than 65535 points. */ + + /* Add 4 phantom points to each end. */ + dx = sfnt_read_packed_deltas (data, end, + glyph->simple->number_of_points + 4, + &data); + dy = sfnt_read_packed_deltas (data, end, + glyph->simple->number_of_points + 4, + &data); + + if (!dx || !dy) + goto fail3; + + /* Apply each delta to the simple glyph. */ + + for (i = 0; i < glyph->simple->number_of_points; ++i) + { + fword = sfnt_mul_fixed (dx[i], scale); + glyph->simple->x_coordinates[i] += fword; + fword = sfnt_mul_fixed (dy[i], scale); + glyph->simple->y_coordinates[i] += fword; + } + + /* Apply the deltas for the two phantom points. */ + distortion->origin += sfnt_mul_fixed (dx[i++], scale); + distortion->advance += sfnt_mul_fixed (dx[i], scale); + break; + + default: + dx = sfnt_read_packed_deltas (data, end, point_count, &data); + dy = sfnt_read_packed_deltas (data, end, point_count, &data); + + if (!dx || !dy) + goto fail3; - /* Now, consider the Y axis. */ - min_pos = MIN (y[pair_start], y[pair_end]); - max_pos = MAX (y[pair_start], y[pair_end]); + /* Deltas are only applied for each point number read. */ - /* Now see if the current point lies between min and - max... */ - if (y[j] >= min_pos && y[j] <= max_pos) + if (!original_x) { - /* If min_pos and max_pos are the same, apply - pair_start's delta if it is identical to that of - pair_end, or apply nothing at all otherwise. */ - - if (min_pos == max_pos) - { - if ((glyph->simple->y_coordinates[pair_start] - - y[pair_start]) - == (glyph->simple->y_coordinates[pair_end] - - y[pair_end])) - glyph->simple->y_coordinates[j] - += (glyph->simple->y_coordinates[pair_start] - - y[pair_start]); + if ((glyph->simple->number_of_points + * sizeof *touched) >= 1024 * 16) + touched = xmalloc (sizeof *touched + * glyph->simple->number_of_points); + else + touched = alloca (sizeof *touched + * glyph->simple->number_of_points); - goto next_1; - } + if ((sizeof *original_x * 2 + * glyph->simple->number_of_points) >= 1024 * 16) + original_x = xmalloc (sizeof *original_x * 2 + * glyph->simple->number_of_points); + else + original_x = alloca (sizeof *original_x * 2 + * glyph->simple->number_of_points); - /* Interpolate between min_pos and max_pos. */ - ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos) - * 65536), - (sfnt_sub (max_pos, min_pos) - * 65536)); + original_y = original_x + glyph->simple->number_of_points; + memcpy (original_x, glyph->simple->x_coordinates, + (sizeof *original_x + * glyph->simple->number_of_points)); + memcpy (original_y, glyph->simple->y_coordinates, + (sizeof *original_y + * glyph->simple->number_of_points)); + } - /* Load the current positions of pair_start and pair_end - along this axis. */ - min_pos = MIN (glyph->simple->y_coordinates[pair_start], - glyph->simple->y_coordinates[pair_end]); - max_pos = MAX (glyph->simple->y_coordinates[pair_start], - glyph->simple->y_coordinates[pair_end]); + memset (touched, 0, (sizeof *touched + * glyph->simple->number_of_points)); - /* Lerp in between. */ - delta = sfnt_sub (max_pos, min_pos); - delta = sfnt_mul_fixed (ratio, delta); - glyph->simple->y_coordinates[j] = min_pos + delta; - } - else + for (i = 0; i < point_count; ++i) { - /* ... otheriwse, move point j by the delta of the - nearest touched point. */ + /* Apply deltas to phantom points. */ - if (y[j] >= max_pos) + if (glyph_points[i] == glyph->simple->number_of_points) { - position = MAX (glyph->simple->y_coordinates[pair_start], - glyph->simple->y_coordinates[pair_end]); - delta = position - max_pos; + distortion->origin += sfnt_mul_fixed (dx[i], scale); + continue; } - else + + if (glyph_points[i] == glyph->simple->number_of_points + 1) { - position = MIN (glyph->simple->y_coordinates[pair_start], - glyph->simple->y_coordinates[pair_end]); - delta = position - min_pos; + distortion->advance += sfnt_mul_fixed (dx[i], scale); + continue; } - glyph->simple->y_coordinates[j] = y[j] + delta; + /* Make sure the point doesn't end up out of bounds. */ + if (glyph_points[i] >= glyph->simple->number_of_points) + continue; + + fword = sfnt_mul_fixed (dx[i], scale); + glyph->simple->x_coordinates[glyph_points[i]] += fword; + fword = sfnt_mul_fixed (dy[i], scale); + glyph->simple->y_coordinates[glyph_points[i]] += fword; + touched[glyph_points[i]] = true; } - next_1: - j++; - if (j > end) - j = start; + sfnt_infer_deltas (glyph, touched, original_x, + original_y); + break; } - } -} - -/* Infer point positions for contours that have been partially moved - by variation. For each contour in GLYPH, find pairs of points - which have had deltas applied. For each such pair, interpolate - points between the first point in the pair and the second by - considering each point along every one of the two axes (X and Y) - like so: - - For each point that lies between the first point and the last - on the axis currently being considered, interpolate its - position in that axis so that the ratio between the first - point and the last in the original outline still holds. - - - For each point that lies to the left or top of the first point - on the axis being considered, use the delta of the first point. + xfree (dx); + xfree (dy); - - And finally, for each point that lies to the right or bottom of - the last point on that axis, use the delta of the last - point. + if (local_points != (uint16_t *) -1) + xfree (local_points); + } - X and Y contain the original positions positions of each point. - TOUCHED contains whether or not each point has been changed by - an explicitly specified delta. + /* Return success. */ - Apply the inferred deltas back to GLYPH. */ + if ((glyph->simple->number_of_points + * sizeof *touched) >= 1024 * 16) + xfree (touched); -static void -sfnt_infer_deltas (struct sfnt_glyph *glyph, bool *touched, - sfnt_fword *x, sfnt_fword *y) -{ - size_t i; - int point, first, end; + if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) + xfree (coords); - point = 0; - for (i = 0; i < glyph->number_of_contours; ++i) - { - first = point; - end = glyph->simple->end_pts_of_contours[i]; + if ((sizeof *original_x * 2 + * glyph->simple->number_of_points) >= 1024 * 16) + xfree (original_x); - /* Return if the glyph is invalid. */ + if (points != (uint16_t *) -1) + xfree (points); - if (first >= glyph->simple->number_of_points - || end >= glyph->simple->number_of_points - || first > end) - return; + /* Set the glyph metrics distortion as well. */ + glyph->advance_distortion = distortion->advance; + glyph->origin_distortion = distortion->origin; - sfnt_infer_deltas_1 (glyph, first, end, touched, x, y); - point = end + 1; - } -} + return 0; -/* Read the glyph variation data for the specified glyph ID from - BLEND's gvar table. Apply the offsets to each point in the - specified simple GLYPH, based on the specified BLEND. + fail3: + xfree (dx); + xfree (dy); + xfree (local_points); + fail1: - Value is 0 upon success, else 1. + if ((glyph->simple->number_of_points + * sizeof *touched) >= 1024 * 16) + xfree (touched); - The glyph variation data consists of a number of elements, each of - which has its own associated point numbers and deltas, and a list - of one or two coordinates for each axis. Each such list is - referred to as a ``tuple''. + if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) + xfree (coords); - The deltas, one for each point, are multipled by the normalized - value of each axis and applied to those points for each tuple that - is found to be applicable. + if ((sizeof *original_x * 2 + * glyph->simple->number_of_points) >= 1024 * 16) + xfree (original_x); - Each element of the glyph variation data is applicable to an axis - if its list of coordinates: + if (points != (uint16_t *) -1) + xfree (points); - - contains one element for each axis, and its axis has a value - between 0 and that element. + return 1; +} - - contains two elements for each axis, and its axis has a value - between the first element and the second. +/* Read the glyph variation data for the specified glyph ID from + BLEND's gvar table. Apply the deltas specified within to each + component with offsets in the specified compound GLYPH, based on + the specified BLEND. Return distortions to phantom points in + *DISTORTION. - After the deltas are applied, any points without deltas must be - interpolated similar to an IUP instruction. In addition, deltas - may also be applied to phantom points within a glyph. */ + Value is 0 upon success, 1 otherwise. */ -static int -sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, - struct sfnt_glyph *glyph) +TEST_STATIC int +sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id, + struct sfnt_glyph *glyph, + struct sfnt_metrics_distortion *distortion) { uint32_t offset; struct sfnt_gvar_glyph_header header; @@ -13775,13 +14506,14 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, sfnt_f2dot14 *restrict coords; sfnt_f2dot14 *restrict intermediate_start; sfnt_f2dot14 *restrict intermediate_end; - sfnt_fword *dx, *dy, fword; + sfnt_fword *restrict dx, *restrict dy, fword, word; struct sfnt_gvar_table *gvar; uint16_t *local_points, n_local_points; sfnt_fixed scale; ptrdiff_t data_offset; bool *touched; sfnt_fword *restrict original_x, *restrict original_y; + struct sfnt_compound_glyph_component *component; gvar = blend->gvar; @@ -13806,6 +14538,10 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, if (offset + sizeof header > gvar->data_size) return 1; + /* Clear the distortion. */ + distortion->origin = 0; + distortion->advance = 0; + memcpy (&header, gvar->glyph_variation_data + offset, sizeof header); @@ -13846,7 +14582,11 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* Start reading each tuple. */ tuple = gvar->glyph_variation_data + offset + sizeof header; - coords = xmalloc (gvar->axis_count * sizeof *coords * 3); + if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) + coords = xmalloc (gvar->axis_count * sizeof *coords * 3); + else + coords = alloca (gvar->axis_count * sizeof *coords * 3); + intermediate_start = coords + gvar->axis_count; intermediate_end = coords + gvar->axis_count; @@ -13856,7 +14596,7 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, original_x = NULL; original_y = NULL; - for (i = 0; i < ntuples; ++i) + while (ntuples--) { data = gvar->glyph_variation_data + offset + data_offset; @@ -13899,7 +14639,7 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, gvar->axis_count * sizeof *coords); /* Now read indeterminate tuples if required. */ - if (index & 0x1000) + if (index & 0x4000) { for (j = 0; j < gvar->axis_count; ++j) { @@ -13966,34 +14706,55 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, switch (point_count) { case UINT16_MAX: - /* Deltas are provided for all points in the glyph. - No glyph should have more than 65535 points. */ - - if (glyph->simple->number_of_points > 65535) - abort (); + /* Deltas are provided for all components in the glyph. */ /* Add 4 phantom points to each end. */ dx = sfnt_read_packed_deltas (data, end, - glyph->simple->number_of_points + 4, + glyph->compound->num_components + 4, &data); dy = sfnt_read_packed_deltas (data, end, - glyph->simple->number_of_points + 4, + glyph->compound->num_components + 4, &data); if (!dx || !dy) goto fail3; - /* Apply each delta to the simple glyph. */ + /* Apply each delta to the compound glyph. */ - for (i = 0; i < glyph->simple->number_of_points; ++i) + for (i = 0; i < glyph->compound->num_components; ++i) { + component = &glyph->compound->components[i]; + + /* Check if the component uses deltas at all. */ + if (!(component->flags & 02)) + continue; + + /* Vary the X offset. */ + + if (!(component->flags & 01)) + word = component->argument1.b; + else + word = component->argument1.d; + fword = sfnt_mul_fixed (dx[i], scale); - glyph->simple->x_coordinates[i] += fword; + component->flags |= 01; + component->argument1.d = word + fword; + + /* Vary the Y offset. */ + + if (!(component->flags & 01)) + word = component->argument2.b; + else + word = component->argument2.d; + fword = sfnt_mul_fixed (dy[i], scale); - glyph->simple->y_coordinates[i] += fword; + component->flags |= 01; + component->argument2.d = word + fword; } - /* TODO: apply metrics variations. */ + /* Apply the deltas for the two phantom points. */ + distortion->origin += sfnt_mul_fixed (dx[i++], scale); + distortion->advance += sfnt_mul_fixed (dx[i], scale); break; default: @@ -14007,39 +14768,83 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, if (!original_x) { - touched = xmalloc (sizeof *touched - * glyph->simple->number_of_points); - original_x = xmalloc (sizeof *original_x * 2 - * glyph->simple->number_of_points); - original_y = original_x + glyph->simple->number_of_points; + if ((glyph->compound->num_components + * sizeof *touched) >= 1024 * 16) + touched = xmalloc (sizeof *touched + * glyph->compound->num_components); + else + touched = alloca (sizeof *touched + * glyph->compound->num_components); + + if ((sizeof *original_x * 2 + * glyph->compound->num_components) >= 1024 * 16) + original_x = xmalloc (sizeof *original_x * 2 + * glyph->compound->num_components); + else + original_x = alloca (sizeof *original_x * 2 + * glyph->compound->num_components); + + original_y = original_x + glyph->compound->num_components; memcpy (original_x, glyph->simple->x_coordinates, (sizeof *original_x - * glyph->simple->number_of_points)); + * glyph->compound->num_components)); memcpy (original_y, glyph->simple->y_coordinates, (sizeof *original_y - * glyph->simple->number_of_points)); + * glyph->compound->num_components)); } memset (touched, 0, (sizeof *touched - * glyph->simple->number_of_points)); + * glyph->compound->num_components)); for (i = 0; i < point_count; ++i) { + /* Apply deltas to phantom points. */ + + if (glyph_points[i] == glyph->compound->num_components) + { + distortion->origin += sfnt_mul_fixed (dx[i], scale); + continue; + } + + if (glyph_points[i] == glyph->compound->num_components + 1) + { + distortion->advance += sfnt_mul_fixed (dx[i], scale); + continue; + } + /* Make sure the point doesn't end up out of bounds. */ - if (glyph_points[i] >= glyph->simple->number_of_points) + if (glyph_points[i] >= glyph->compound->num_components) + continue; + + component = &glyph->compound->components[glyph_points[i]]; + + /* Check if the component uses deltas at all. */ + if (!(component->flags & 02)) continue; + /* Vary the X offset. */ + + if (!(component->flags & 01)) + word = component->argument1.b; + else + word = component->argument1.d; + fword = sfnt_mul_fixed (dx[i], scale); - glyph->simple->x_coordinates[glyph_points[i]] += fword; + component->flags |= 01; + component->argument1.d = word + fword; + + /* Vary the Y offset. */ + + if (!(component->flags & 01)) + word = component->argument2.b; + else + word = component->argument2.d; + fword = sfnt_mul_fixed (dy[i], scale); - glyph->simple->y_coordinates[glyph_points[i]] += fword; - touched[glyph_points[i]] = true; + component->flags |= 01; + component->argument2.d = word + fword; } - sfnt_infer_deltas (glyph, touched, original_x, - original_y); - - /* TODO: apply metrics variations. */ break; } @@ -14052,13 +14857,24 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, /* Return success. */ - xfree (touched); - xfree (coords); - xfree (original_x); + if ((glyph->compound->num_components + * sizeof *touched) >= 1024 * 16) + xfree (touched); + + if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) + xfree (coords); + + if ((sizeof *original_x * 2 + * glyph->compound->num_components) >= 1024 * 16) + xfree (original_x); if (points != (uint16_t *) -1) xfree (points); + /* Set the glyph metrics distortion as well. */ + glyph->advance_distortion = distortion->advance; + glyph->origin_distortion = distortion->origin; + return 0; fail3: @@ -14066,17 +14882,84 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, xfree (dy); xfree (local_points); fail1: - xfree (touched); - xfree (coords); - xfree (original_x); - if (points != (uint16_t *) -1) - xfree (points); + if ((glyph->compound->num_components + * sizeof *touched) >= 1024 * 16) + xfree (touched); + + if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16) + xfree (coords); + + if ((sizeof *original_x * 2 + * glyph->compound->num_components) >= 1024 * 16) + xfree (original_x); + + if (points != (uint16_t *) -1) + xfree (points); + + return 1; +} + +/* Vary the specified INTERPRETER's control value table using the + variations in BLEND's CVT variations table. + + The CVT table used to create INTERPRETER must be the same used + to read BLEND->cvar. If not, behavior is undefined. */ + +TEST_STATIC void +sfnt_vary_interpreter (struct sfnt_interpreter *interpreter, + struct sfnt_blend *blend) +{ + sfnt_fixed scale; + int i; + struct sfnt_tuple_variation *variation; + size_t ndeltas, j, index; + sfnt_f26dot6 delta; + + /* Return if there's no cvar table. */ + if (!blend->cvar) + return; + + /* For each tuple in the cvar table... */ + for (i = 0; i < (blend->cvar->tuple_count & 0x0fff); ++i) + { + /* See if the tuple applies. */ + variation = &blend->cvar->variation[i]; + scale = sfnt_compute_tuple_scale (blend, + variation->intermediate_start != NULL, + variation->coordinates, + variation->intermediate_start, + variation->intermediate_end); + if (!scale) + continue; + + /* Figure out how many deltas there are. If variation->points, + there are num_points deltas. Otherwise, there are + interpreter->cvt->num_elements deltas. */ + + ndeltas = (variation->points + ? variation->num_points + : interpreter->cvt_size); + + for (j = 0; j < ndeltas; ++j) + { + /* Figure out which CVT entry this applies to. */ + index = variation->points ? variation->points[j] : j; + + if (index > interpreter->cvt_size) + continue; - return 1; -} + /* Multiply the delta by the interpreter scale factor and + then the tuple scale factor. */ + delta = sfnt_mul_f26dot6_fixed (variation->deltas[j] * 64, + interpreter->scale); + delta = sfnt_mul_fixed (delta, scale); -#endif /* TEST */ + /* Apply the delta to the control value table. */ + interpreter->cvt[i] += delta; + } + } +} @@ -14088,6 +14971,7 @@ sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, struct sfnt_glyf_table *glyf; struct sfnt_loca_table_short *loca_short; struct sfnt_loca_table_long *loca_long; + struct sfnt_blend *blend; }; /* Global context for test functions. Height of glyph. */ @@ -14120,17 +15004,31 @@ sfnt_test_curve_to (struct sfnt_point control, } static struct sfnt_glyph * -sfnt_test_get_glyph (sfnt_glyph glyph, void *dcontext, +sfnt_test_get_glyph (sfnt_glyph id, void *dcontext, bool *need_free) { struct sfnt_test_dcontext *tables; + struct sfnt_glyph *glyph; + struct sfnt_metrics_distortion distortion; tables = dcontext; *need_free = true; - return sfnt_read_glyph (glyph, tables->glyf, - tables->loca_short, - tables->loca_long); + glyph = sfnt_read_glyph (id, tables->glyf, + tables->loca_short, + tables->loca_long); + + if (tables->blend && glyph) + { + if (glyph->simple) + sfnt_vary_simple_glyph (tables->blend, id, glyph, + &distortion); + else + sfnt_vary_compound_glyph (tables->blend, id, glyph, + &distortion); + } + + return glyph; } static void @@ -17881,7 +18779,7 @@ main (int argc, char **argv) struct sfnt_cmap_encoding_subtable *subtables; struct sfnt_cmap_encoding_subtable_data **data; struct sfnt_cmap_table *table; - int fd, i; + int fd, i, j; sfnt_char character; struct sfnt_head_table *head; struct sfnt_hhea_table *hhea; @@ -17911,6 +18809,8 @@ main (int argc, char **argv) struct sfnt_instructed_outline *value; struct sfnt_fvar_table *fvar; struct sfnt_gvar_table *gvar; + struct sfnt_avar_table *avar; + struct sfnt_cvar_table *cvar; sfnt_fixed scale; char *fancy; int *advances; @@ -17919,6 +18819,7 @@ main (int argc, char **argv) char *axis_name; struct sfnt_instance *instance; struct sfnt_blend blend; + struct sfnt_metrics_distortion distortion; if (argc < 2) return 1; @@ -18029,8 +18930,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 12 -#define EASY_PPEM 12 +#define FANCY_PPEM 15 +#define EASY_PPEM 15 interpreter = NULL; head = sfnt_read_head_table (fd, font); @@ -18044,16 +18945,123 @@ #define EASY_PPEM 12 prep = sfnt_read_prep_table (fd, font); fvar = sfnt_read_fvar_table (fd, font); gvar = sfnt_read_gvar_table (fd, font); + avar = sfnt_read_avar_table (fd, font); + cvar = NULL; hmtx = NULL; + if (fvar && cvt) + cvar = sfnt_read_cvar_table (fd, font, fvar, cvt); + + if (cvar) + fprintf (stderr, "cvar table found\n"); + exec_prep = prep; exec_fpgm = fpgm; fancy = getenv ("SFNT_FANCY_TEST"); - + + loca_long = NULL; + loca_short = NULL; + + if (fvar) + { + fprintf (stderr, "FVAR table found!\n" + "version: %"PRIu16".%"PRIu16"\n" + "axis_count: %"PRIu16"\n" + "axis_size: %"PRIu16"\n" + "instance_count: %"PRIu16"\n" + "instance_size: %"PRIu16"\n", + fvar->major_version, + fvar->minor_version, + fvar->axis_count, + fvar->axis_size, + fvar->instance_count, + fvar->instance_size); + + for (i = 0; i < fvar->axis_count; ++i) + { + if (name) + { + axis_name + = (char *) sfnt_find_name (name, fvar->axis[i].name_id, + &record); + + if (axis_name) + fprintf (stderr, "axis no: %d; name: %.*s\n", + i, record.length, axis_name); + } + + fprintf (stderr, " axis: %"PRIx32" %g %g %g\n", + fvar->axis[i].axis_tag, + sfnt_coerce_fixed (fvar->axis[i].min_value), + sfnt_coerce_fixed (fvar->axis[i].default_value), + sfnt_coerce_fixed (fvar->axis[i].max_value)); + } + + for (i = 0; i < fvar->instance_count; ++i) + { + if (name) + { + axis_name + = (char *) sfnt_find_name (name, fvar->instance[i].name_id, + &record); + + if (axis_name) + fprintf (stderr, "instance no: %d; name: %.*s\n", + i, record.length, axis_name); + } + } + + if (fvar->instance_count > 1) + { + printf ("instance? "); + + if (scanf ("%d", &i) == EOF) + goto free_lab; + + if (i >= fvar->instance_count) + goto free_lab; + + if (i >= 0) + instance = &fvar->instance[i]; + } + } + + if (gvar) + fprintf (stderr, "gvar table found\n"); + + if (avar) + { + fprintf (stderr, "avar table found\n"); + + for (i = 0; i < avar->axis_count; ++i) + { + fprintf (stderr, "axis: %d, %"PRIu16" pairs\n", + i, avar->segments[i].pair_count); + + for (j = 0; j < avar->segments[i].pair_count; ++j) + fprintf (stderr, "pair: %g, %g\n", + (avar->segments[i].correspondence[j].from_coord + / 16384.0), + (avar->segments[i].correspondence[j].to_coord + / 16384.0)); + } + } + + memset (&blend, 0, sizeof blend); + + if (instance && gvar) + { + sfnt_init_blend (&blend, fvar, gvar, avar, + cvar); + + for (i = 0; i < fvar->axis_count; ++i) + blend.coords[i] = instance->coords[i]; + + sfnt_normalize_blend (&blend); + } + if (fancy) { - loca_long = NULL; - loca_short = NULL; length = strlen (fancy); scale = sfnt_div_fixed (FANCY_PPEM, head->units_per_em); @@ -18085,6 +19093,8 @@ #define EASY_PPEM 12 interpreter = sfnt_make_interpreter (maxp, cvt, head, FANCY_PPEM, FANCY_PPEM); + if (instance && gvar) + sfnt_vary_interpreter (interpreter, &blend); if (!interpreter) exit (1); @@ -18128,6 +19138,10 @@ #define EASY_PPEM 12 if (!glyph || !glyph->simple) exit (3); + if (instance && gvar) + sfnt_vary_simple_glyph (&blend, code, glyph, + &distortion); + if (sfnt_lookup_glyph_metrics (code, -1, &metrics, hmtx, hhea, @@ -18159,58 +19173,14 @@ #define EASY_PPEM 12 xfree (outline); rasters[i] = raster; - advances[i] = sfnt_mul_fixed (metrics.advance, scale); + advances[i] = (sfnt_mul_fixed (metrics.advance, scale) + + sfnt_mul_fixed (distortion.advance, scale)); } sfnt_x_raster (rasters, advances, length, hhea, scale); exit (0); } - if (head && maxp && maxp->version >= 0x00010000) - { - fprintf (stderr, "creating interpreter\n" - "the size of the stack is %"PRIu16"\n" - "the size of the twilight zone is %"PRIu16"\n" - "the size of the storage area is %"PRIu16"\n" - "there are at most %"PRIu16" idefs\n" - "there are at most %"PRIu16" fdefs\n" - "the cvt is %zu fwords in length\n", - maxp->max_stack_elements, - maxp->max_twilight_points, - maxp->max_storage, - maxp->max_instruction_defs, - maxp->max_function_defs, - cvt ? cvt->num_elements : 0ul); - - interpreter = sfnt_make_interpreter (maxp, cvt, head, - FANCY_PPEM, - FANCY_PPEM); - state = interpreter->state; - - if (fpgm) - { - fprintf (stderr, "interpreting the font program, with" - " %zu instructions\n", fpgm->num_instructions); - - trap = sfnt_interpret_font_program (interpreter, fpgm); - - if (trap) - fprintf (stderr, "**TRAP**: %s\n", trap); - } - - if (prep) - { - fprintf (stderr, "interpreting the control value program, with" - " %zu instructions\n", prep->num_instructions); - - trap = sfnt_interpret_control_value_program (interpreter, prep, - &state); - - if (trap) - fprintf (stderr, "**TRAP**: %s\n", trap); - } - } - if (hhea && maxp) hmtx = sfnt_read_hmtx_table (fd, font, hhea, maxp); @@ -18315,82 +19285,54 @@ #define EASY_PPEM 12 (int) hhea->caret_slope_rise, (int) hhea->caret_slope_run); - if (fvar) + if (head && maxp && maxp->version >= 0x00010000) { - fprintf (stderr, "FVAR table found!\n" - "version: %"PRIu16".%"PRIu16"\n" - "axis_count: %"PRIu16"\n" - "axis_size: %"PRIu16"\n" - "instance_count: %"PRIu16"\n" - "instance_size: %"PRIu16"\n", - fvar->major_version, - fvar->minor_version, - fvar->axis_count, - fvar->axis_size, - fvar->instance_count, - fvar->instance_size); - - for (i = 0; i < fvar->axis_count; ++i) - { - if (name) - { - axis_name - = (char *) sfnt_find_name (name, fvar->axis[i].name_id, - &record); + fprintf (stderr, "creating interpreter\n" + "the size of the stack is %"PRIu16"\n" + "the size of the twilight zone is %"PRIu16"\n" + "the size of the storage area is %"PRIu16"\n" + "there are at most %"PRIu16" idefs\n" + "there are at most %"PRIu16" fdefs\n" + "the cvt is %zu fwords in length\n", + maxp->max_stack_elements, + maxp->max_twilight_points, + maxp->max_storage, + maxp->max_instruction_defs, + maxp->max_function_defs, + cvt ? cvt->num_elements : 0ul); - if (axis_name) - fprintf (stderr, "axis no: %d; name: %.*s\n", - i, record.length, axis_name); - } + interpreter = sfnt_make_interpreter (maxp, cvt, head, + FANCY_PPEM, + FANCY_PPEM); + state = interpreter->state; - fprintf (stderr, " axis: %"PRIx32" %g %g %g\n", - fvar->axis[i].axis_tag, - sfnt_coerce_fixed (fvar->axis[i].min_value), - sfnt_coerce_fixed (fvar->axis[i].default_value), - sfnt_coerce_fixed (fvar->axis[i].max_value)); - } + if (instance && gvar) + sfnt_vary_interpreter (interpreter, &blend); - for (i = 0; i < fvar->instance_count; ++i) + if (fpgm) { - if (name) - { - axis_name - = (char *) sfnt_find_name (name, fvar->instance[i].name_id, - &record); + fprintf (stderr, "interpreting the font program, with" + " %zu instructions\n", fpgm->num_instructions); - if (axis_name) - fprintf (stderr, "instance no: %d; name: %.*s\n", - i, record.length, axis_name); - } + trap = sfnt_interpret_font_program (interpreter, fpgm); + + if (trap) + fprintf (stderr, "**TRAP**: %s\n", trap); } - if (fvar->instance_count > 1) + if (prep) { - printf ("instance? "); - - if (scanf ("%d", &i) == EOF) - goto free_lab; + fprintf (stderr, "interpreting the control value program, with" + " %zu instructions\n", prep->num_instructions); - if (i < 0 || i >= fvar->instance_count) - goto free_lab; + trap = sfnt_interpret_control_value_program (interpreter, prep, + &state); - instance = &fvar->instance[i]; + if (trap) + fprintf (stderr, "**TRAP**: %s\n", trap); } } - if (gvar) - fprintf (stderr, "gvar table found\n"); - - if (instance && gvar) - { - sfnt_init_blend (&blend, fvar, gvar); - - for (i = 0; i < fvar->axis_count; ++i) - blend.coords[i] = instance->coords[i]; - - sfnt_normalize_blend (&blend); - } - while (true) { printf ("table, character? "); @@ -18428,11 +19370,33 @@ #define EASY_PPEM 12 dcontext.loca_short = loca_short; dcontext.loca_long = loca_long; + if (instance && gvar) + dcontext.blend = &blend; + else + dcontext.blend = NULL; + if (glyph->simple && instance && gvar) { printf ("applying variations to simple glyph...\n"); - if (sfnt_vary_glyph (&blend, code, glyph)) + clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start); + if (sfnt_vary_simple_glyph (&blend, code, glyph, + &distortion)) + printf ("variation failed!\n"); + clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end); + sub = timespec_sub (end, start); + + printf ("time spent varying: %lld sec %ld nsec\n", + (long long) sub.tv_sec, sub.tv_nsec); + printf ("distortions: %"PRIi16", %"PRIi16"\n", + distortion.origin, distortion.advance); + } + else if (instance && gvar) + { + printf ("applying variations to compound glyph...\n"); + + if (sfnt_vary_compound_glyph (&blend, code, glyph, + &distortion)) printf ("variation failed!\n"); } @@ -18444,10 +19408,20 @@ #define EASY_PPEM 12 &dcontext)) printf ("decomposition failure\n"); + if (sfnt_lookup_glyph_metrics (code, EASY_PPEM, + &metrics, + hmtx, hhea, + head, maxp)) + { + printf ("metrics lookup failure"); + memset (&metrics, 0, sizeof metrics); + } + /* Time this important bit. */ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start); outline = sfnt_build_glyph_outline (glyph, head, EASY_PPEM, + &metrics, sfnt_test_get_glyph, sfnt_test_free_glyph, &dcontext); @@ -18618,6 +19592,9 @@ #define FG sfnt_test_free_glyph for (i = 0; i < table->num_subtables; ++i) xfree (data[i]); + if (instance && gvar) + sfnt_free_blend (&blend); + xfree (table); xfree (data); xfree (subtables); @@ -18637,9 +19614,8 @@ #define FG sfnt_test_free_glyph xfree (prep); xfree (fvar); xfree (gvar); - - if (instance && gvar) - sfnt_free_blend (&blend); + xfree (avar); + xfree (cvar); return 0; } diff --git a/src/sfnt.h b/src/sfnt.h index 82fa343f51d..84e51ff6766 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -27,10 +27,6 @@ #define _SFNT_H_ #include -#if defined emacs || defined TEST -#define SFNT_ENABLE_HINTING -#endif - /* Container structure and enumerator definitions. */ @@ -56,6 +52,7 @@ #define SFNT_ENABLE_HINTING SFNT_TABLE_FVAR, SFNT_TABLE_GVAR, SFNT_TABLE_CVAR, + SFNT_TABLE_AVAR, }; #define SFNT_ENDOF(type, field, type1) \ @@ -664,6 +661,12 @@ #define sfnt_coerce_fixed(fixed) ((sfnt_fixed) (fixed) / 65535.0) /* Coordinate bounds. */ sfnt_fword xmin, ymin, xmax, ymax; + /* Distortion applied to the right side phantom point. */ + sfnt_fword advance_distortion; + + /* Distortion applied to the origin point. */ + sfnt_fword origin_distortion; + /* Either a simple glyph or a compound glyph, depending on which is set. */ struct sfnt_simple_glyph *simple; @@ -718,6 +721,10 @@ #define sfnt_coerce_fixed(fixed) ((sfnt_fixed) (fixed) / 65535.0) and maximum X and Y positions. */ sfnt_fixed xmin, ymin, xmax, ymax; + /* The origin point of the outline on the X axis. Value defaults to + 0. */ + sfnt_fixed origin; + /* Reference count. Initially zero. */ short refcount; }; @@ -739,7 +746,8 @@ #define sfnt_coerce_fixed(fixed) ((sfnt_fixed) (fixed) / 65535.0) /* Basic dimensions of the raster. */ unsigned short width, height; - /* Integer offset to apply to positions in the raster. */ + /* Integer offset to apply to positions in the raster so that they + start from the origin point of the glyph. */ short offx, offy; /* The raster stride. */ @@ -966,6 +974,357 @@ #define sfnt_coerce_fixed(fixed) ((sfnt_fixed) (fixed) / 65535.0) +/* Unicode Variation Sequence (UVS) support. */ + +struct sfnt_default_uvs_table +{ + /* Number of ranges that follow. */ + uint32_t num_unicode_value_ranges; + + /* Variable length data. */ + struct sfnt_unicode_value_range *ranges; +}; + +struct sfnt_unicode_value_range +{ + /* First value in this range. */ + unsigned int start_unicode_value; + + /* Number of additional values in this range. */ + unsigned char additional_count; +}; + +struct sfnt_nondefault_uvs_table +{ + /* Number of UVS mappings which follow. */ + uint32_t num_uvs_mappings; + + /* Variable length data. */ + struct sfnt_uvs_mapping *mappings; +}; + +struct sfnt_uvs_mapping +{ + /* Base character value. */ + unsigned int unicode_value; + + /* Glyph ID of the base character value. */ + uint16_t base_character_value; +}; + +struct sfnt_mapped_variation_selector_record +{ + /* The variation selector. */ + unsigned int selector; + + /* Its default UVS table. */ + struct sfnt_default_uvs_table *default_uvs; + + /* Its nondefault UVS table. */ + struct sfnt_nondefault_uvs_table *nondefault_uvs; +}; + +/* Structure describing a single offset to load into a variation + selection context. */ + +struct sfnt_table_offset_rec +{ + /* The offset from the start of the font file. */ + off_t offset; + + /* Whether or not the offset points to a non-default UVS table. */ + bool is_nondefault_table; + + /* Pointer to the UVS table. */ + void *table; +}; + +struct sfnt_uvs_context +{ + /* Number of records and tables. */ + size_t num_records, nmemb; + + /* Array of UVS tables. */ + struct sfnt_table_offset_rec *tables; + + /* Array of variation selector records mapped to + their corresponding tables. */ + struct sfnt_mapped_variation_selector_record *records; +}; + + + +#if defined HAVE_MMAP && !defined TEST + +/* Memory mapping support. */ + +struct sfnt_mapped_table +{ + /* Pointer to table data. */ + void *data; + + /* Pointer to table mapping. */ + void *mapping; + + /* Size of mapped data and size of mapping. */ + size_t length, size; +}; + +#endif /* HAVE_MMAP && !TEST */ + + + +/* Glyph variation support. */ + +/* 2.14 fixed point type used to represent versors of unit + vectors. */ +typedef int16_t sfnt_f2dot14; + +/* Forward declaration used only for the distortable font stuff. */ +struct sfnt_cvt_table; + +struct sfnt_variation_axis +{ + /* The axis tag. */ + uint32_t axis_tag; + + /* The minimum style coordinate for the axis. */ + sfnt_fixed min_value; + + /* The default style coordinate for the axis. */ + sfnt_fixed default_value; + + /* The maximum style coordinate for the axis. */ + sfnt_fixed max_value; + + /* Set to zero. */ + uint16_t flags; + + /* Identifier under which this axis's name will be found in the + `name' table. */ + uint16_t name_id; +}; + +struct sfnt_instance +{ + /* The instance name ID. */ + uint16_t name_id; + + /* Flags. */ + uint16_t flags; + + /* Optional PostScript name. */ + uint16_t ps_name_id; + + /* Coordinates of each defined instance. */ + sfnt_fixed *coords; +}; + +struct sfnt_fvar_table +{ + /* Major version; should be 1. */ + uint16_t major_version; + + /* Minor version; should be 0. */ + uint16_t minor_version; + + /* Offset in bytes from the beginning of the table to the beginning + of the first axis data. */ + uint16_t offset_to_data; + + /* Reserved field; always 2. */ + uint16_t count_size_pairs; + + /* Number of style axes in this font. */ + uint16_t axis_count; + + /* The number of bytes in each variation axis record. Currently 20 + bytes. */ + uint16_t axis_size; + + /* The number of named instances for the font found in the + instance array. */ + uint16_t instance_count; + + /* The size of each instance record. */ + uint16_t instance_size; + + /* Variable length data. */ + struct sfnt_variation_axis *axis; + struct sfnt_instance *instance; +}; + +struct sfnt_short_frac_correspondence +{ + /* Value in normalized user space. */ + sfnt_f2dot14 from_coord; + + /* Value in normalized axis space. */ + sfnt_f2dot14 to_coord; +}; + +struct sfnt_short_frac_segment +{ + /* The number of pairs for this axis. */ + uint16_t pair_count; + + /* Variable length data. */ + struct sfnt_short_frac_correspondence *correspondence; +}; + +struct sfnt_avar_table +{ + /* The version of the table. Should be 1.0. */ + sfnt_fixed version; + + /* Number of variation axes defined in this table. + XXX: why is this signed? */ + int32_t axis_count; + + /* Variable length data. */ + struct sfnt_short_frac_segment *segments; +}; + +struct sfnt_tuple_variation +{ + /* Tuple point numbers. */ + uint16_t *points; + + /* Deltas. */ + sfnt_fword *deltas; + + /* Tuple coordinates. One for each axis specified in the [gaf]var + tables. */ + sfnt_f2dot14 *coordinates; + + /* Intermediate start and end coordinates. */ + sfnt_f2dot14 *restrict intermediate_start; + + /* Intermediate start and end coordinates. */ + sfnt_f2dot14 *restrict intermediate_end; + + /* The number of points and deltas present. + + UINT16_MAX and POINTS set to NULL means there are deltas for each + CVT entry. */ + uint16_t num_points; +}; + +struct sfnt_cvar_table +{ + /* The version of this CVT variations table. */ + sfnt_fixed version; + + /* Flags. */ + uint16_t tuple_count; + + /* Offset from the beginning of the table to the tuple data. */ + uint16_t data_offset; + + /* Variable length data. */ + struct sfnt_tuple_variation *variation; +}; + +struct sfnt_gvar_table +{ + /* Version of the glyph variations table. */ + uint16_t version; + + /* Reserved, currently 0. */ + uint16_t reserved; + + /* The number of style axes for this font. This must be the same + number as axisCount in the 'fvar' table. */ + uint16_t axis_count; + + /* The number of shared coordinates. */ + uint16_t shared_coord_count; + + /* Byte offset from the beginning of this table to the list of + shared style coordinates. */ + uint32_t offset_to_coord; + + /* The number of glyphs in this font; this should match the number + of the glyphs store elsewhere in the font. */ + uint16_t glyph_count; + + /* Bit-field that gives the format of the offset array that + follows. If the flag is 0, the type is uint16. If the flag is 1, + the type is unit 32. */ + uint16_t flags; + + /* Byte offset from the beginning of this table to the first glyph + glyphVariationData. */ + uint32_t offset_to_data; + + /* Number of bytes in the glyph variation data. */ + size_t data_size; + + /* Byte offsets from the beginning of the glyphVariationData array + to the glyphVariationData for each glyph in the font. The format + of this field is set by the flags field. */ + union { + uint16_t *offset_word; + uint32_t *offset_long; + } u; + + /* Other variable length data. */ + sfnt_f2dot14 *global_coords; + unsigned char *glyph_variation_data; +}; + +/* Structure repesenting a set of axis coordinates and their + normalized equivalents. + + To use this structure, call + + sfnt_init_blend (&blend, fvar, gvar) + + on a `struct sfnt_blend *', with an appropriate fvar and gvar + table. + + Then, fill in blend.coords with the un-normalized coordinates, + and call + + sfnt_normalize_blend (&blend) + + finally, call sfnt_vary_simple_glyph and related functions. */ + +struct sfnt_blend +{ + /* The fvar table. This determines the number of elements in each + of the arrays below. */ + struct sfnt_fvar_table *fvar; + + /* The gvar table. This provides the glyph variation data. */ + struct sfnt_gvar_table *gvar; + + /* The avar table. This provides adjustments to normalized axis + values, and may be NULL. */ + struct sfnt_avar_table *avar; + + /* The cvar table. This provides adjustments to CVT values, and may + be NULL. */ + struct sfnt_cvar_table *cvar; + + /* Un-normalized coordinates. */ + sfnt_fixed *coords; + + /* Normalized coordinates. */ + sfnt_fixed *norm_coords; +}; + +struct sfnt_metrics_distortion +{ + /* Distortion applied to the origin point. */ + sfnt_fword origin; + + /* Distortion applied to the advance point. */ + sfnt_fword advance; +}; + + + #define SFNT_CEIL_FIXED(fixed) \ (!((fixed) & 0177777) ? (fixed) \ : ((fixed) + 0200000) & 037777600000) @@ -1012,7 +1371,9 @@ #define PROTOTYPE int, struct sfnt_offset_subtable * #define PROTOTYPE \ struct sfnt_glyph *, \ struct sfnt_head_table *, \ - int, sfnt_get_glyph_proc, \ + int, \ + struct sfnt_glyph_metrics *, \ + sfnt_get_glyph_proc, \ sfnt_free_glyph_proc, \ void * extern struct sfnt_glyph_outline *sfnt_build_glyph_outline (PROTOTYPE); @@ -1042,6 +1403,8 @@ #define PROTOTYPE \ extern void sfnt_scale_metrics (struct sfnt_glyph_metrics *, sfnt_fixed); +extern void sfnt_scale_metrics_to_pixel_size (struct sfnt_glyph_metrics *, + int, struct sfnt_head_table *); #define PROTOTYPE int, struct sfnt_offset_subtable * extern struct sfnt_name_table *sfnt_read_name_table (PROTOTYPE); @@ -1061,14 +1424,82 @@ #define PROTOTYPE int, struct sfnt_offset_subtable * extern struct sfnt_ttc_header *sfnt_read_ttc_header (int); + + +#define PROTOTYPE struct sfnt_cmap_format_14 *, int + +extern struct sfnt_uvs_context *sfnt_create_uvs_context (PROTOTYPE); + +#undef PROTOTYPE + +extern void sfnt_free_uvs_context (struct sfnt_uvs_context *); + +#define PROTOTYPE struct sfnt_nondefault_uvs_table *, sfnt_char + +extern sfnt_glyph sfnt_variation_glyph_for_char (PROTOTYPE); + +#undef PROTOTYPE + + + +#ifdef HAVE_MMAP + +extern int sfnt_map_table (int, struct sfnt_offset_subtable *, + uint32_t, struct sfnt_mapped_table *); +extern int sfnt_unmap_table (struct sfnt_mapped_table *); + +#endif /* HAVE_MMAP */ + + + +extern void *sfnt_read_table (int, struct sfnt_offset_subtable *, + uint32_t, size_t *); + + + +#define PROTOTYPE int, struct sfnt_offset_subtable * + +extern struct sfnt_fvar_table *sfnt_read_fvar_table (PROTOTYPE); +extern struct sfnt_gvar_table *sfnt_read_gvar_table (PROTOTYPE); +extern struct sfnt_avar_table *sfnt_read_avar_table (PROTOTYPE); + +#undef PROTOTYPE + +#define PROTOTYPE \ + int, \ + struct sfnt_offset_subtable *, \ + struct sfnt_fvar_table *, \ + struct sfnt_cvt_table * + +extern struct sfnt_cvar_table *sfnt_read_cvar_table (PROTOTYPE); + +#undef PROTOTYPE + + + +extern void sfnt_init_blend (struct sfnt_blend *, + struct sfnt_fvar_table *, + struct sfnt_gvar_table *, + struct sfnt_avar_table *, + struct sfnt_cvar_table *); +extern void sfnt_free_blend (struct sfnt_blend *); +extern void sfnt_normalize_blend (struct sfnt_blend *); + + + +extern int sfnt_vary_simple_glyph (struct sfnt_blend *, sfnt_glyph, + struct sfnt_glyph *, + struct sfnt_metrics_distortion *); +extern int sfnt_vary_compound_glyph (struct sfnt_blend *, sfnt_glyph, + struct sfnt_glyph *, + struct sfnt_metrics_distortion *); + #endif /* TEST */ /* TrueType hinting support. */ -#ifdef SFNT_ENABLE_HINTING - /* Structure definitions for tables used by the TrueType interpreter. */ @@ -1107,10 +1538,6 @@ #define PROTOTYPE int, struct sfnt_offset_subtable * /* 26.6 fixed point type used within the interpreter. */ typedef int32_t sfnt_f26dot6; -/* 2.14 fixed point type used to represent versors of unit - vectors. */ -typedef int16_t sfnt_f2dot14; - /* 18.14 fixed point type used to calculate rounding details. */ typedef int32_t sfnt_f18dot14; @@ -1479,106 +1906,6 @@ #define PROTOTYPE int, struct sfnt_offset_subtable * -/* Unicode Variation Sequence (UVS) support. */ - -struct sfnt_default_uvs_table -{ - /* Number of ranges that follow. */ - uint32_t num_unicode_value_ranges; - - /* Variable length data. */ - struct sfnt_unicode_value_range *ranges; -}; - -struct sfnt_unicode_value_range -{ - /* First value in this range. */ - unsigned int start_unicode_value; - - /* Number of additional values in this range. */ - unsigned char additional_count; -}; - -struct sfnt_nondefault_uvs_table -{ - /* Number of UVS mappings which follow. */ - uint32_t num_uvs_mappings; - - /* Variable length data. */ - struct sfnt_uvs_mapping *mappings; -}; - -struct sfnt_uvs_mapping -{ - /* Base character value. */ - unsigned int unicode_value; - - /* Glyph ID of the base character value. */ - uint16_t base_character_value; -}; - -struct sfnt_mapped_variation_selector_record -{ - /* The variation selector. */ - unsigned int selector; - - /* Its default UVS table. */ - struct sfnt_default_uvs_table *default_uvs; - - /* Its nondefault UVS table. */ - struct sfnt_nondefault_uvs_table *nondefault_uvs; -}; - -/* Structure describing a single offset to load into a variation - selection context. */ - -struct sfnt_table_offset_rec -{ - /* The offset from the start of the font file. */ - off_t offset; - - /* Whether or not the offset points to a non-default UVS table. */ - bool is_nondefault_table; - - /* Pointer to the UVS table. */ - void *table; -}; - -struct sfnt_uvs_context -{ - /* Number of records and tables. */ - size_t num_records, nmemb; - - /* Array of UVS tables. */ - struct sfnt_table_offset_rec *tables; - - /* Array of variation selector records mapped to - their corresponding tables. */ - struct sfnt_mapped_variation_selector_record *records; -}; - - - -#if defined HAVE_MMAP && !defined TEST - -/* Memory mapping support. */ - -struct sfnt_mapped_table -{ - /* Pointer to table data. */ - void *data; - - /* Pointer to table mapping. */ - void *mapping; - - /* Size of mapped data and size of mapping. */ - size_t length, size; -}; - -#endif /* HAVE_MMAP && !TEST */ - - - /* Functions used to read tables used by the TrueType interpreter. */ #ifndef TEST @@ -1653,39 +1980,11 @@ #define PROTOTYPE \ -#define PROTOTYPE struct sfnt_cmap_format_14 *, int - -extern struct sfnt_uvs_context *sfnt_create_uvs_context (PROTOTYPE); - -#undef PROTOTYPE - -extern void sfnt_free_uvs_context (struct sfnt_uvs_context *); - -#define PROTOTYPE struct sfnt_nondefault_uvs_table *, sfnt_char - -extern sfnt_glyph sfnt_variation_glyph_for_char (PROTOTYPE); - -#undef PROTOTYPE - - - -#ifdef HAVE_MMAP - -extern int sfnt_map_table (int, struct sfnt_offset_subtable *, - uint32_t, struct sfnt_mapped_table *); -extern int sfnt_unmap_table (struct sfnt_mapped_table *); - -#endif /* HAVE_MMAP */ - - - -extern void *sfnt_read_table (int, struct sfnt_offset_subtable *, - uint32_t, size_t *); +extern void sfnt_vary_interpreter (struct sfnt_interpreter *, + struct sfnt_blend *); #endif /* TEST */ -#endif /* SFNT_ENABLE_HINTING */ - #endif /* _SFNT_H_ */ diff --git a/src/sfntfont.c b/src/sfntfont.c index 500256d6fb4..e4579d62154 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -1688,6 +1688,8 @@ sfntfont_dereference_outline (struct sfnt_glyph_outline *outline) If INTERPRETER is non-NULL, then possibly use the unscaled glyph metrics in METRICS and the interpreter STATE to instruct the glyph. + Otherwise, METRICS must contain scaled glyph metrics used to + compute the origin point of the outline. Return the outline with an incremented reference count and enter the generated outline into CACHE upon success, possibly discarding @@ -1714,6 +1716,7 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, struct sfntfont_get_glyph_outline_dcontext dcontext; struct sfnt_instructed_outline *value; const char *error; + struct sfnt_glyph_metrics temp; start = cache->next; @@ -1784,10 +1787,31 @@ sfntfont_get_glyph_outline (sfnt_glyph glyph_code, } if (!outline) - outline = sfnt_build_glyph_outline (glyph, head, pixel_size, - sfntfont_get_glyph, - sfntfont_free_glyph, - &dcontext); + { + /* If INTERPRETER is NULL, METRICS contains scaled metrics. */ + + if (!interpreter) + outline = sfnt_build_glyph_outline (glyph, head, pixel_size, + metrics, + sfntfont_get_glyph, + sfntfont_free_glyph, + &dcontext); + else + { + /* But otherwise, they are unscaled, and must be scaled + before being used. */ + + temp = *metrics; + sfnt_scale_metrics_to_pixel_size (&temp, pixel_size, + head); + outline = sfnt_build_glyph_outline (glyph, head, pixel_size, + &temp, + sfntfont_get_glyph, + sfntfont_free_glyph, + &dcontext); + } + } + xfree (glyph); if (!outline) @@ -2689,8 +2713,8 @@ sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph, font->hmtx, font->hhea, font->maxp, font->loca_short, - font->loca_long, NULL, NULL, - NULL); + font->loca_long, NULL, + &metrics, NULL); if (!outline) return 1; commit d7457e1ce4eed211915a6666cc2e7f61516ed343 Merge: b78ef9bcd1b 4566a0c6b82 Author: Po Lu Date: Fri Mar 24 10:46:14 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit b78ef9bcd1bf34bec877b4fd30f2bbb97cb0919c Author: Po Lu Date: Fri Mar 24 10:43:34 2023 +0800 Update Android port * src/sfnt.c (sfnt_table_names): Add fvar, gvar, cvar. (sfnt_read_maxp_table): Call xmalloc, not malloc. (sfnt_read_simple_glyph): Avoid use-after-free if simple is invalid. (sfnt_fill_span): Fix max coverage. (sfnt_normalize_vector): Fail if magnitude is zero. (sfnt_measure_distance): Fix opcode order. (sfnt_dot_fix_14): Fix implementation. (struct sfnt_variation_axis, struct sfnt_instance) (struct sfnt_fvar_table, struct sfnt_gvar_table) (sfnt_read_fvar_table, sfnt_read_gvar_table, struct sfnt_blend) (sfnt_init_blend, sfnt_free_blend, sfnt_normalize_blend) (struct sfnt_tuple_header, struct sfnt_gvar_glyph_header) (sfnt_read_packed_points, sfnt_read_packed_deltas) (sfnt_compute_tuple_scale, sfnt_infer_deltas_1, sfnt_infer_deltas) (sfnt_vary_glyph): Add WIP variation glyph implementation. * src/sfnt.h (enum sfnt_table, struct sfnt_simple_glyph): Likewise. diff --git a/src/sfnt.c b/src/sfnt.c index bdd713aa016..21b2ea96e1c 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -150,6 +150,9 @@ #define MAX(a, b) ((a) > (b) ? (a) : (b)) [SFNT_TABLE_CVT ] = 0x63767420, [SFNT_TABLE_FPGM] = 0x6670676d, [SFNT_TABLE_PREP] = 0x70726570, + [SFNT_TABLE_FVAR] = 0x66766172, + [SFNT_TABLE_GVAR] = 0x67766172, + [SFNT_TABLE_CVAR] = 0x63766172, }; /* Swap values from TrueType to system byte order. */ @@ -1614,7 +1617,7 @@ sfnt_read_maxp_table (int fd, struct sfnt_offset_subtable *subtable) directory->length or sizeof *maxp bytes into it, whichever is smaller. */ - maxp = malloc (sizeof *maxp); + maxp = xmalloc (sizeof *maxp); size = MIN (directory->length, sizeof *maxp); rc = read (fd, maxp, size); @@ -1917,6 +1920,7 @@ sfnt_read_simple_glyph (struct sfnt_glyph *glyph, { glyph->simple = NULL; xfree (simple); + return; } /* Repeat the current flag until @@ -4227,8 +4231,10 @@ sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y, } /* Clear coverage info for first pixel. Compute coverage for center - pixels. */ - w = coverage[SFNT_POLY_MASK]; + pixels. Note that SFNT_POLY_SAMPLE is used and not + SFNT_POLY_MASK, because coverage has a blank column at the + start. */ + w = coverage[SFNT_POLY_SAMPLE]; /* Fill pixels between left and right. */ while (left + SFNT_POLY_MASK < right) @@ -8080,6 +8086,7 @@ sfnt_normalize_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy, /* If vx and vy are both zero, then just project horizontally. */ + fail: vector->x = 04000; vector->y = 0; return; @@ -8113,6 +8120,10 @@ sfnt_normalize_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy, /* Get hypotenuse of the triangle from vx, 0, to 0, vy. */ magnitude = sfnt_sqrt_fixed (n); + /* Avoid division by zero. */ + if (!magnitude) + goto fail; + /* Long division.. eek! */ vector->x = (sfnt_div_fixed (vx * 1024, magnitude) >> 2); vector->y = (sfnt_div_fixed (vy * 1024, magnitude) >> 2); @@ -8169,8 +8180,8 @@ sfnt_line_to_vector (struct sfnt_interpreter *interpreter, relative to the projection or dual projection vector. Return the distance of P1 and P2 relative to their original - un-instructed positions should OPCODE be 0x49, and to their - instructed positions should OPCODE be 0x4A. */ + un-instructed positions should OPCODE be 0x4A, and to their + instructed positions should OPCODE be 0x49. */ static sfnt_f26dot6 sfnt_measure_distance (struct sfnt_interpreter *interpreter, @@ -8188,25 +8199,9 @@ sfnt_measure_distance (struct sfnt_interpreter *interpreter, sfnt_address_zp1 (interpreter, p2, &p2x, &p2y, &p2_original_x, &p2_original_y); - if (opcode == 0x49) - { - /* When measuring in the glyph zone, measure the distance using - the dual projection vector, relative to the ``original - original outlines''. - - This is not written down anywhere, leading you to believe - that the distance is measured using the scaled outline prior - to instructing. */ - - if (interpreter->state.zp0 == 1 - && interpreter->state.zp1 == 1) - return sfnt_div_fixed (DUAL_PROJECT (sfnt_sub (p1x, p2x), - sfnt_sub (p1y, p2y)), - interpreter->scale); - - return DUAL_PROJECT (sfnt_sub (p1x, p2x), - sfnt_sub (p1y, p2y)); - } + if (opcode == 0x4A) + return DUAL_PROJECT (sfnt_sub (p1_original_x, p2_original_x), + sfnt_sub (p1_original_y, p2_original_y)); return PROJECT (sfnt_sub (p1x, p2x), sfnt_sub (p1y, p2y)); @@ -9238,7 +9233,7 @@ sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by) yy = xx >> 63; xx += 0x2000 + yy; - return (int32_t) (yy / (2 << 14)); + return (int32_t) (xx / (2 << 14)); #endif } @@ -11454,6 +11449,8 @@ sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph, struct sfnt_instructed_outline *value; struct sfnt_glyph_metrics sub_metrics; + error = NULL; + /* Set up the base index. This is the index from where on point renumbering starts. @@ -12431,278 +12428,1928 @@ sfnt_read_table (int fd, struct sfnt_offset_subtable *subtable, #endif /* !TEST */ +#ifdef TEST + -#ifdef TEST +/* Glyph variations. Instead of defining separate fonts for each + combination of weight, width and slant (bold, condensed, italic, + etc), some fonts specify a list of ``variation axes'', each of + which determines one delta to apply to each point in every + glyph. -struct sfnt_test_dcontext + This optional information is specified in the `fvar' (font + variation), `gvar' (glyph variation) and `cvar' (CVT variation) + tables in a font file. */ + +struct sfnt_variation_axis { - /* Context for sfnt_test_get_glyph. */ - struct sfnt_glyf_table *glyf; - struct sfnt_loca_table_short *loca_short; - struct sfnt_loca_table_long *loca_long; -}; + /* The axis tag. */ + uint32_t axis_tag; -/* Global context for test functions. Height of glyph. */ -static sfnt_fixed sfnt_test_max; + /* The minimum style coordinate for the axis. */ + sfnt_fixed min_value; -static void -sfnt_test_move_to (struct sfnt_point point, void *dcontext) -{ - printf ("move_to: %g, %g\n", sfnt_coerce_fixed (point.x), - sfnt_coerce_fixed (point.y)); -} + /* The default style coordinate for the axis. */ + sfnt_fixed default_value; -static void -sfnt_test_line_to (struct sfnt_point point, void *dcontext) -{ - printf ("line_to: %g, %g\n", sfnt_coerce_fixed (point.x), - sfnt_coerce_fixed (point.y)); -} + /* The maximum style coordinate for the axis. */ + sfnt_fixed max_value; -static void -sfnt_test_curve_to (struct sfnt_point control, - struct sfnt_point endpoint, - void *dcontext) -{ - printf ("curve_to: %g, %g - %g, %g\n", - sfnt_coerce_fixed (control.x), - sfnt_coerce_fixed (control.y), - sfnt_coerce_fixed (endpoint.x), - sfnt_coerce_fixed (endpoint.y)); -} + /* Set to zero. */ + uint16_t flags; -static struct sfnt_glyph * -sfnt_test_get_glyph (sfnt_glyph glyph, void *dcontext, - bool *need_free) + /* Identifier under which this axis's name will be found in the + `name' table. */ + uint16_t name_id; +}; + +struct sfnt_instance { - struct sfnt_test_dcontext *tables; + /* The instance name ID. */ + uint16_t name_id; - tables = dcontext; - *need_free = true; + /* Flags. */ + uint16_t flags; - return sfnt_read_glyph (glyph, tables->glyf, - tables->loca_short, - tables->loca_long); -} + /* Optional PostScript name. */ + uint16_t ps_name_id; -static void -sfnt_test_free_glyph (struct sfnt_glyph *glyph, void *dcontext) -{ - sfnt_free_glyph (glyph); -} + /* Coordinates of each defined instance. */ + sfnt_fixed *coords; +}; -static void -sfnt_test_span (struct sfnt_edge *edge, sfnt_fixed y, - void *dcontext) +struct sfnt_fvar_table { -#if 1 - printf ("/* span at %g */\n", sfnt_coerce_fixed (y)); - for (; edge; edge = edge->next) - { - if (y >= edge->bottom && y < edge->top) - printf ("ctx.fillRect (%g, %g, 1, 1); " - "/* %g top: %g bot: %g stepx: %g winding: %d */\n", - sfnt_coerce_fixed (edge->x), - sfnt_coerce_fixed (sfnt_test_max - y), - sfnt_coerce_fixed (y), - sfnt_coerce_fixed (edge->top), - sfnt_coerce_fixed (edge->bottom), - sfnt_coerce_fixed (edge->step_x), - edge->winding); - else - printf ("STRIPPED BAD SPAN!!! %g %g %"PRIi32 - " %"PRIi32" (winding: %d)\n", - sfnt_coerce_fixed (edge->top), - sfnt_coerce_fixed (edge->bottom), - edge->top, y, edge->winding); - } -#elif 0 - int winding; - short x, dx; - - winding = 0; - x = 0; + /* Major version; should be 1. */ + uint16_t major_version; - for (; edge; edge = edge->next) - { - dx = (edge->x >> 16) - x; - x = edge->x >> 16; + /* Minor version; should be 0. */ + uint16_t minor_version; - for (; dx > 0; --dx) - putc (winding ? '.' : ' ', stdout); + /* Offset in bytes from the beginning of the table to the beginning + of the first axis data. */ + uint16_t offset_to_data; - winding = !winding; - } + /* Reserved field; always 2. */ + uint16_t count_size_pairs; - putc ('\n', stdout); -#elif 0 - for (; edge; edge = edge->next) - printf ("%g-", sfnt_coerce_fixed (edge->x)); - puts (""); -#endif -} + /* Number of style axes in this font. */ + uint16_t axis_count; -static void -sfnt_test_edge_ignore (struct sfnt_edge *edges, size_t num_edges, - void *dcontext) -{ + /* The number of bytes in each variation axis record. Currently 20 + bytes. */ + uint16_t axis_size; -} + /* The number of named instances for the font found in the + instance array. */ + uint16_t instance_count; -/* The same debugger stuff is used here. */ -static void sfnt_setup_debugger (void); + /* The size of each instance record. */ + uint16_t instance_size; -/* The debugger's X display. */ -static Display *display; + /* Variable length data. */ + struct sfnt_variation_axis *axis; + struct sfnt_instance *instance; +}; -/* The debugger window. */ -static Window window; +/* Read an fvar table from the given font FD. Use the table directory + specified in SUBTABLE. -/* The GC. */ -static GC point_gc, background_gc; + Return the fvar table upon success, else NULL. */ -static void -sfnt_test_edges (struct sfnt_edge *edges, size_t num_edges) +static struct sfnt_fvar_table * +sfnt_read_fvar_table (int fd, struct sfnt_offset_subtable *subtable) { - static sfnt_fixed y; - size_t i; + struct sfnt_table_directory *directory; + struct sfnt_fvar_table *fvar; + ssize_t rc; + size_t min_bytes, ps_size, non_ps_size, temp, pad; + off_t offset; + int i, j; + char *buffer; + sfnt_fixed *coords; - for (i = 0; i < num_edges; ++i) - { - if (y >= edges[i].bottom && y < edges[i].top) - { - XDrawPoint (display, window, point_gc, - edges[i].x / 65536, 100 - (y / 65536)); - printf ("sfnt_test_edges: %d %d\n", - edges[i].x / 65536, 100 - (y / 65536)); - } - } + /* Find the table in the directory. */ - y += SFNT_POLY_STEP; + directory = sfnt_find_table (subtable, SFNT_TABLE_FVAR); - for (i = 0; i < num_edges; ++i) - sfnt_step_edge (&edges[i]); -} + if (!directory) + return NULL; -static void -sfnt_debug_edges (struct sfnt_edge *edges, size_t num_edges) -{ - XEvent event; + min_bytes = SFNT_ENDOF (struct sfnt_fvar_table, + instance_size, uint16_t); - sfnt_setup_debugger (); + /* Check that the length is at least min_bytes. */ + if (directory->length < min_bytes) + return NULL; - while (true) - { - XNextEvent (display, &event); + /* Seek to the location given in the directory. */ + if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1) + return NULL; - switch (event.type) - { - case KeyPress: - XDestroyWindow (display, window); - XCloseDisplay (display); - exit (0); - break; + /* Allocate enough to hold the fvar table header. */ + fvar = xmalloc (sizeof *fvar); - case Expose: + /* Read the fvar table header. */ + buffer = NULL; + rc = read (fd, fvar, min_bytes); + if (rc != min_bytes) + goto bail; - while (true) - { - sfnt_test_edges (edges, num_edges); - XFlush (display); - usleep (50000); - } + /* Swap what was read. */ + sfnt_swap16 (&fvar->major_version); + sfnt_swap16 (&fvar->minor_version); + sfnt_swap16 (&fvar->offset_to_data); + sfnt_swap16 (&fvar->count_size_pairs); + sfnt_swap16 (&fvar->axis_count); + sfnt_swap16 (&fvar->axis_size); + sfnt_swap16 (&fvar->instance_count); + sfnt_swap16 (&fvar->instance_size); - break; - } - } -} + /* major_version should be 1, and minor_version 0. */ -static void -sfnt_test_edge (struct sfnt_edge *edges, size_t num_edges, - void *dcontext) -{ - size_t i; + if (fvar->major_version != 1 || fvar->minor_version) + goto bail; - printf ("built %zu edges\n", num_edges); + /* count_size_pairs should be more than 2. */ - for (i = 0; i < num_edges; ++i) - { - printf ("/* edge x, top, bot: %g, %g - %g. winding: %d */\n" - "/* edge step_x: %g */\n", - sfnt_coerce_fixed (edges[i].x), - sfnt_coerce_fixed (edges[i].top), - sfnt_coerce_fixed (edges[i].bottom), - edges[i].winding, - sfnt_coerce_fixed (edges[i].step_x)); -#ifdef TEST_VERTEX - printf ("ctx.fillRect (%g, %g, 1, 1);\n", - sfnt_coerce_fixed (edges[i].x), - sfnt_coerce_fixed (sfnt_test_max - - edges[i].y)); -#else - printf ("ctx.fillRect (%g, %g, 1, 1);\n", - sfnt_coerce_fixed (edges[i].x), - sfnt_coerce_fixed (sfnt_test_max - - edges[i].bottom)); -#endif - } + if (fvar->count_size_pairs < 2) + goto bail; - if (getenv ("SFNT_DEBUG_STEP")) - { - if (!fork ()) - sfnt_debug_edges (edges, num_edges); - } + /* Don't try to read tables where the axis format differs. */ - printf ("==end of edges==\n"); + if (fvar->axis_size != 20) + goto bail; - sfnt_poly_edges (edges, num_edges, sfnt_test_span, NULL); -} + /* The instance size must either be 2 * sizeof (uint16_t) + + axisCount * sizeof (sfnt_fixed), meaning there is no PostScript + name identifier, or 3 * sizeof (uint16_t) + axisCount * sizeof + (sfnt_fixed), meaning there is. */ -static void -sfnt_x_raster (struct sfnt_raster **rasters, - int *advances, - int nrasters, - struct sfnt_hhea_table *hhea, - sfnt_fixed scale) -{ - Display *display; - Window window; - Pixmap *pixmaps; - Picture *glyphs, drawable, solid; - int event_base, error_base; - int major, minor, *depths, count; - XRenderPictFormat *format, *glyph_format; - Visual *visual; - XImage image; - GC gc; - XGCValues gcvalues; - XEvent event; - XRenderColor white, black; - int i, ascent, origin, x, y; - Font font; + if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof (sfnt_fixed), + &temp) + || INT_ADD_WRAPV (2 * sizeof (uint16_t), temp, &non_ps_size)) + goto bail; - if (!nrasters) - exit (0); + if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof (sfnt_fixed), + &temp) + || INT_ADD_WRAPV (3 * sizeof (uint16_t), temp, &ps_size)) + goto bail; - display = XOpenDisplay (NULL); + if (fvar->instance_size != non_ps_size + && fvar->instance_size != ps_size) + goto bail; - if (!display) - exit (0); + /* Now compute the offset of the axis data from the start of the + font file. */ - if (!XRenderQueryExtension (display, &event_base, &error_base) - || !XRenderQueryVersion (display, &major, &minor)) - exit (0); + if (INT_ADD_WRAPV (fvar->offset_to_data, directory->offset, + &offset)) + goto bail; - if (major == 0 && minor < 10) - exit (0); + /* Seek there. */ - window = XCreateSimpleWindow (display, DefaultRootWindow (display), - 0, 0, 100, 150, 0, 0, - WhitePixel (display, - DefaultScreen (display))); - XSelectInput (display, window, ExposureMask); - XMapWindow (display, window); + if (lseek (fd, offset, SEEK_SET) != offset) + goto bail; + + min_bytes = sizeof *fvar; + + /* Now, read each axis and instance. Calculate how much extra data + needs to be allocated for the axes and instances: this is + fvar->axis_count * sizeof (struct sfnt_variation_axis), some + padding, and finally fvar->instance_count * sizeof (struct + sfnt_instance) + sizeof (sfnt_fixed) * fvar->instance_count * + fvar->axis_count. */ + + if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof *fvar->axis, + &temp) + || INT_ADD_WRAPV (min_bytes, temp, &min_bytes)) + goto bail; + + pad = alignof (struct sfnt_variation_axis); + pad -= min_bytes & (pad - 1); + + if (INT_ADD_WRAPV (min_bytes, pad, &min_bytes)) + goto bail; + + if (INT_MULTIPLY_WRAPV (fvar->instance_count, + sizeof *fvar->instance, + &temp) + || INT_ADD_WRAPV (min_bytes, temp, &min_bytes)) + goto bail; + + if (INT_MULTIPLY_WRAPV (fvar->instance_count, + sizeof *fvar->instance->coords, + &temp) + || INT_MULTIPLY_WRAPV (temp, fvar->axis_count, &temp) + || INT_ADD_WRAPV (min_bytes, temp, &min_bytes)) + goto bail; + + /* Reallocate fvar. */ + fvar = xrealloc (fvar, min_bytes); + + /* Fill in offsets. */ + fvar->axis = (struct sfnt_variation_axis *) (fvar + 1); + fvar->instance + = (struct sfnt_instance *) (((char *) (fvar->axis + + fvar->axis_count)) + + pad); + + /* Read axes. */ + + if (directory->length - SFNT_ENDOF (struct sfnt_fvar_table, + instance_size, uint16_t) + < sizeof *fvar->axis * fvar->axis_count) + goto bail; + + rc = read (fd, fvar->axis, sizeof *fvar->axis * fvar->axis_count); + if (rc != sizeof *fvar->axis * fvar->axis_count) + goto bail; + + /* Swap each axis. */ + + for (i = 0; i < fvar->axis_count; ++i) + { + sfnt_swap32 (&fvar->axis[i].axis_tag); + sfnt_swap32 (&fvar->axis[i].min_value); + sfnt_swap32 (&fvar->axis[i].default_value); + sfnt_swap32 (&fvar->axis[i].max_value); + sfnt_swap16 (&fvar->axis[i].flags); + sfnt_swap16 (&fvar->axis[i].name_id); + } + + /* Read each instance. */ + + if (fvar->instance_size < 1024 * 16) + buffer = alloca (fvar->instance_size); + else + buffer = xmalloc (fvar->instance_size); + + coords = (sfnt_fixed *) (fvar->instance + fvar->instance_count); + + for (i = 0; i < fvar->instance_count; ++i) + { + rc = read (fd, buffer, fvar->instance_size); + if (rc != fvar->instance_size) + goto bail; + + /* Fill in various fields. */ + + fvar->instance[i].name_id = *((uint16_t *) buffer); + fvar->instance[i].flags = *((uint16_t *) buffer + 1); + fvar->instance[i].ps_name_id = 0; + + sfnt_swap16 (&fvar->instance[i].name_id); + sfnt_swap16 (&fvar->instance[i].flags); + + /* Read coordinates. */ + + fvar->instance[i].coords = coords; + coords += fvar->axis_count; + + memcpy (fvar->instance[i].coords, buffer + 4, + sizeof *fvar->instance[i].coords * fvar->axis_count); + + /* Swap coordinates. */ + + for (j = 0; j < fvar->axis_count; ++j) + sfnt_swap32 (&fvar->instance[i].coords[j]); + + /* Read the PostScript name ID if necessary. If not, set it to + nil. */ + + if (fvar->instance_size == ps_size) + { + fvar->instance[i].ps_name_id + = *(uint16_t *) (buffer + 4 + (sizeof *fvar->instance[i].coords + * fvar->axis_count)); + sfnt_swap16 (&fvar->instance[i].ps_name_id); + } + } + + /* Free the temporary buffer. */ + if (buffer && fvar->instance_size >= 1024 * 16) + xfree (buffer); + + /* Return the fvar table. */ + return fvar; + + bail: + if (buffer && fvar->instance_size >= 1024 * 16) + xfree (buffer); + + xfree (fvar); + return NULL; +} + + + +struct sfnt_gvar_table +{ + /* Version of the glyph variations table. */ + uint16_t version; + + /* Reserved, currently 0. */ + uint16_t reserved; + + /* The number of style axes for this font. This must be the same + number as axisCount in the 'fvar' table. */ + uint16_t axis_count; + + /* The number of shared coordinates. */ + uint16_t shared_coord_count; + + /* Byte offset from the beginning of this table to the list of + shared style coordinates. */ + uint32_t offset_to_coord; + + /* The number of glyphs in this font; this should match the number + of the glyphs store elsewhere in the font. */ + uint16_t glyph_count; + + /* Bit-field that gives the format of the offset array that + follows. If the flag is 0, the type is uint16. If the flag is 1, + the type is unit 32. */ + uint16_t flags; + + /* Byte offset from the beginning of this table to the first glyph + glyphVariationData. */ + uint32_t offset_to_data; + + /* Number of bytes in the glyph variation data. */ + size_t data_size; + + /* Byte offsets from the beginning of the glyphVariationData array + to the glyphVariationData for each glyph in the font. The format + of this field is set by the flags field. */ + union { + uint16_t *offset_word; + uint32_t *offset_long; + } u; + + /* Other variable length data. */ + sfnt_f2dot14 *global_coords; + unsigned char *glyph_variation_data; +}; + +/* Read a gvar table from the given font FD. Use the table directory + specified in SUBTABLE. + + Return the gvar table upon success, else NULL. */ + +static struct sfnt_gvar_table * +sfnt_read_gvar_table (int fd, struct sfnt_offset_subtable *subtable) +{ + struct sfnt_table_directory *directory; + struct sfnt_gvar_table *gvar; + ssize_t rc; + size_t min_bytes, off_size, coordinate_size, data_size; + int i; + off_t offset; + + /* Find the table in the directory. */ + + directory = sfnt_find_table (subtable, SFNT_TABLE_GVAR); + + if (!directory) + return NULL; + + min_bytes = SFNT_ENDOF (struct sfnt_gvar_table, + offset_to_data, uint32_t); + + /* Check that the length is at least min_bytes. */ + if (directory->length < min_bytes) + return NULL; + + /* Seek to the location given in the directory. */ + if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1) + return NULL; + + /* Allocate enough to hold the gvar table header. */ + gvar = xmalloc (sizeof *gvar); + + /* Read the gvar table header. */ + rc = read (fd, gvar, min_bytes); + if (rc != min_bytes) + goto bail; + + /* Swap what was read. */ + sfnt_swap16 (&gvar->version); + sfnt_swap16 (&gvar->reserved); + sfnt_swap16 (&gvar->axis_count); + sfnt_swap16 (&gvar->shared_coord_count); + sfnt_swap32 (&gvar->offset_to_coord); + sfnt_swap16 (&gvar->glyph_count); + sfnt_swap16 (&gvar->flags); + sfnt_swap32 (&gvar->offset_to_data); + + if (gvar->version != 1) + goto bail; + + if (gvar->offset_to_data > directory->length) + goto bail; + + /* Figure out the size required for the offset array. Note that + there is one extra offset at the end of the array to mark the + size of the last glyph. */ + + if (gvar->flags & 1) + /* Offsets are long words. */ + off_size = sizeof (uint32_t) * (gvar->glyph_count + 1); + else + /* Offsets are words. */ + off_size = sizeof (uint16_t) * (gvar->glyph_count + 1); + + /* Now figure out the size of the shared coordinates. */ + coordinate_size = (gvar->shared_coord_count * gvar->axis_count + * sizeof (uint16_t)); + + /* And the size of the glyph variation data. */ + data_size = directory->length - gvar->offset_to_data; + + /* Wraparound. */ + if (data_size > directory->length) + goto bail; + + /* Figure out how big gvar needs to be. */ + if (INT_ADD_WRAPV (sizeof *gvar, coordinate_size, &min_bytes) + || INT_ADD_WRAPV (min_bytes, off_size, &min_bytes) + || INT_ADD_WRAPV (min_bytes, data_size, &min_bytes)) + goto bail; + + /* Now allocate enough for all of this extra data. */ + gvar = xrealloc (gvar, min_bytes); + + /* Start reading offsets. */ + + if (!(gvar->flags & 1)) + { + gvar->u.offset_word = (uint16_t *) (gvar + 1); + rc = read (fd, gvar->u.offset_word, off_size); + if (rc != off_size) + goto bail; + + for (i = 0; i <= gvar->glyph_count; ++i) + sfnt_swap16 (&gvar->u.offset_word[i]); + } + else + { + gvar->u.offset_long = (uint32_t *) (gvar + 1); + rc = read (fd, gvar->u.offset_long, off_size); + if (rc != off_size) + goto bail; + + for (i = 0; i <= gvar->glyph_count; ++i) + sfnt_swap32 (&gvar->u.offset_long[i]); + } + + /* Start reading shared coordinates. */ + + gvar->global_coords = ((sfnt_f2dot14 *) ((char *) gvar + off_size)); + + if (gvar->shared_coord_count) + { + if (INT_ADD_WRAPV (gvar->offset_to_coord, directory->offset, + &offset)) + goto bail; + + if (lseek (fd, offset, SEEK_SET) != offset) + goto bail; + + if (read (fd, gvar->global_coords, coordinate_size) + != coordinate_size) + goto bail; + + for (i = 0; i <= coordinate_size / sizeof *gvar->global_coords; ++i) + sfnt_swap16 (&gvar->global_coords[i]); + } + + /* Finally, read the rest of the glyph variation data. */ + gvar->data_size = data_size; + gvar->glyph_variation_data + = (unsigned char *) (gvar->global_coords + + (coordinate_size + / sizeof *gvar->global_coords)); + + if (gvar->data_size) + { + if (INT_ADD_WRAPV (gvar->offset_to_data, directory->offset, + &offset)) + goto bail; + + if (lseek (fd, offset, SEEK_SET) != offset) + goto bail; + + if (read (fd, gvar->glyph_variation_data, + gvar->data_size) != gvar->data_size) + goto bail; + } + + /* Return the read gvar table. */ + return gvar; + + bail: + xfree (gvar); + return NULL; +} + + + +/* Structure repesenting a set of axis coordinates and their + normalized equivalents. + + To use this structure, call + + sfnt_init_blend (&blend, fvar, gvar) + + on a `struct sfnt_blend *', with an appropriate fvar and gvar + table. + + Then, fill in blend.coords with the un-normalized coordinates, + and call + + sfnt_normalize_blend (&blend) + + finally, call sfnt_vary_glyph and related functions. */ + +struct sfnt_blend +{ + /* The fvar table. This determines the number of elements in each + of the arrays below. */ + struct sfnt_fvar_table *fvar; + + /* The gvar table. This provides the glyph variation data. */ + struct sfnt_gvar_table *gvar; + + /* Un-normalized coordinates. */ + sfnt_fixed *coords; + + /* Normalized coordinates. */ + sfnt_fixed *norm_coords; +}; + +/* Initialize the specified BLEND with the given FVAR and GVAR + tables. */ + +static void +sfnt_init_blend (struct sfnt_blend *blend, struct sfnt_fvar_table *fvar, + struct sfnt_gvar_table *gvar) +{ + size_t size; + + blend->fvar = fvar; + blend->gvar = gvar; + + /* Allocate a single array to hold both coords and norm_coords. */ + size = (fvar->axis_count * sizeof *blend->coords * 2); + blend->coords = xmalloc (size); + blend->norm_coords = blend->coords + fvar->axis_count; +} + +/* Free what was initialized in the specified BLEND. */ + +static void +sfnt_free_blend (struct sfnt_blend *blend) +{ + xfree (blend->coords); +} + +/* Normalize BLEND->fvar->axis_count coordinates in BLEND->coords and + place the result in BLEND->norm_coords. */ + +static void +sfnt_normalize_blend (struct sfnt_blend *blend) +{ + struct sfnt_variation_axis *axis; + int i; + sfnt_fixed coord; + + /* For each axis... */ + for (i = 0; i < blend->fvar->axis_count; ++i) + { + /* Normalize based on [min, default, max], into [-1, 0, 1]. */ + axis = &blend->fvar->axis[i]; + + /* Load the current design coordinate. */ + coord = blend->coords[i]; + + /* Keep it within bounds. */ + + if (coord > axis->max_value) + coord = axis->max_value; + else if (coord < axis->min_value) + coord = axis->min_value; + + if (coord > axis->default_value) + { + /* Avoid division by 0. */ + if (axis->max_value != axis->default_value) + blend->norm_coords[i] + = sfnt_div_fixed (sfnt_sub (coord, axis->default_value), + sfnt_sub (axis->max_value, + axis->default_value)); + else + blend->norm_coords[i] = 0; + } + else if (coord < axis->default_value) + { + if (axis->default_value != axis->min_value) + blend->norm_coords[i] + = sfnt_div_fixed (sfnt_sub (coord, axis->default_value), + sfnt_sub (axis->default_value, + axis->min_value)); + else + blend->norm_coords[i] = 0; + } + else + blend->norm_coords[i] = 0; + } + + /* TODO: process avar tables. */ +} + + + +struct sfnt_tuple_header +{ + /* The size in bytes of the serialized data for this tuple variation + table. */ + uint16_t variation_data_size; + + /* A packed field. The high 4 bits are flags (see below). The low 12 + bits are an index into a shared tuple records array. */ + uint16_t tuple_index; + + /* Embedded coordinate tuples, if any. */ + sfnt_f2dot14 *embedded_coord; + + /* Intermediate coordinate tuples, if any. */ + sfnt_f2dot14 *intermediate_coord; + + /* Number of points associated with this tuple. + Times two, the number of deltas associated with this tuple. */ + uint16_t npoints; + + /* Points associated with this tuple. */ + uint16_t *points; + + /* Deltas associated with this tuple. */ + sfnt_fword *deltas; +}; + +struct sfnt_gvar_glyph_header +{ + /* A packed field. The high 4 bits are flags and the low 12 bits are + the number of tuples for this glyph. The number of tuples can be + any number between 1 and 4095. */ + uint16_t tuple_count; + + /* Offset from the start of the GlyphVariationData table to the + serialized data. */ + uint16_t data_offset; +}; + +/* Read a sequence of packed points starting from DATA. Return the + number of points read in *NPOINTS_RETURN and the array of unpacked + points, or NULL upon failure. + + If non-NULL, set LOCATION to DATA plus the number of bytes read + upon success. + + Return (uint16_t *) -1 if there are no points at all. + In this case, deltas will apply to all points in the glyph, + and *NPOINTS_RETURN will be UINT16_MAX. + + END is one byte past the last byte in DATA. */ + +static uint16_t * +sfnt_read_packed_points (unsigned char *restrict data, + uint16_t *npoints_return, + unsigned char *restrict end, + unsigned char *restrict *location) +{ + int npoints; + uint16_t *points; + int i, first, control; + + points = NULL; + npoints = 0; + + if (data >= end) + return NULL; + + /* Load the control byte. */ + control = *data++; + + if (!control) + { + *npoints_return = UINT16_MAX; + *location = data; + return (uint16_t *) -1; + } + + /* Now figure out the number of points within. */ + + if (control & 0x80) + { + npoints = control & 0x7f; + npoints <<= 8; + + if (data >= end) + return NULL; + + npoints |= *data++; + } + else + npoints = control; + + /* Start reading points. */ + first = 0; + i = 0; + points = xmalloc (sizeof *points * npoints); + + while (i < npoints) + { + if (data >= end) + goto bail; + + control = *data++; + + if (control & 0x80) + { + /* Next control & 0x7f words are points. */ + + control &= 0x7f; + + while (control != -1 && i < npoints) + { + if (data >= end || data + 1 >= end) + goto bail; + + first += *data++ << 8u; + first += *data++; + points[i] = first; + control -= 1, ++i; + } + } + else + { + /* Next control bytes are points. */ + + while (control != -1 && i < npoints) + { + if (data >= end) + goto bail; + + first += *data++; + points[i] = first; + control -= 1, ++i; + } + } + } + + /* Return the points read. */ + *npoints_return = npoints; + *location = data; + return points; + + bail: + xfree (points); + return NULL; +} + +/* Read and return N packed deltas from DATA. Set *DATA_RETURN to + DATA plus the number of bytes read. + + END is the end of the glyph variation data. Value is an array of N + deltas upon success, and NULL upon failure. */ + +static sfnt_fword * +sfnt_read_packed_deltas (unsigned char *restrict data, + unsigned char *restrict end, + int n, + unsigned char *restrict *data_return) +{ + sfnt_fword *deltas; + int i, count; + unsigned char control; + + if (data >= end) + return NULL; + + deltas = xmalloc (sizeof *deltas * n); + i = 0; + + while (i < n) + { + if (data >= end) + goto fail; + + control = *data++; + count = control & 0x3f; + + while (count != -1 && i < n) + { + if (control & 0x80) + deltas[i++] = 0; + else if (control & 0x40) + { + if (data + 1 >= end) + goto fail; + + deltas[i] = (signed char) *data++; + deltas[i] *= 65536; + deltas[i++] |= *data++; + } + else + { + if (data >= end) + goto fail; + + deltas[i++] = (signed char) *data++; + } + + --count; + } + } + + *data_return = data; + return deltas; + + fail: + xfree (deltas); + return NULL; +} + +/* Given a BLEND containing normalized coordinates, an array of + BLEND->gvar->axis_count tuple coordinates, and, if INTERMEDIATE_P, + a range of tuple coordinates from INTERMEDIATE_START to + INTERMEDIATE_END, return the scaling factor to apply to deltas for + each corresponding point. */ + +static sfnt_fixed +sfnt_compute_tuple_scale (struct sfnt_blend *blend, bool intermediate_p, + sfnt_f2dot14 *coords, + sfnt_f2dot14 *intermediate_start, + sfnt_f2dot14 *intermediate_end) +{ + int i; + sfnt_fixed coord, start, end; + sfnt_fixed scale; + + /* scale is initially 1.0. */ + scale = 0200000; + + for (i = 0; i < blend->gvar->axis_count; ++i) + { + /* Load values for this axis, scaled up to sfnt_fixed. */ + coord = coords[i] * 4; + + if (intermediate_p) + { + start = intermediate_start[i] * 4; + end = intermediate_start[i] * 4; + } + + /* Ignore tuples that can be skipped. */ + + if (!coord) + continue; + + /* If the coordinate is set to 0, then deltas should not be + applied. Return 0. */ + + if (!blend->norm_coords[i]) + return 0; + + /* If no scaling need take place, continue. */ + + if (blend->norm_coords[i] == coord) + continue; + + if (!intermediate_p) + { + /* Not an intermediate tuple; if coord is less than 0 and + blend->norm_coords[i] < coord, or coord is more than 0 + and blend->norm_coords[i] > coord, then it doesn't fit, + so return. */ + + if (blend->norm_coords[i] < MIN (0, coord) + || blend->norm_coords[i] > MAX (0, coord)) + return 0; + + scale = sfnt_multiply_divide_signed (scale, + blend->norm_coords[i], + coord); + } + else + { + /* Otherwise, renormalize between start and end. */ + + if (blend->norm_coords[i] < start + || blend->norm_coords[i] > end) + return 0; + + if (blend->norm_coords[i] < coord) + scale = sfnt_multiply_divide (scale, + blend->norm_coords[i] - start, + coord - start); + else + scale = sfnt_multiply_divide (scale, + end - blend->norm_coords[i], + end - coord); + } + } + + return scale; +} + +/* Infer point positions for points that have been partially moved + within the contour in GLYPH denoted by START and END. */ + +static void +sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start, + size_t end, bool *touched, sfnt_fword *x, + sfnt_fword *y) +{ + size_t i, pair_start, pair_end, pair_first, j; + sfnt_fword min_pos, max_pos, position; + sfnt_fixed ratio, delta; + + pair_start = pair_first = -1; + + /* Look for pairs of touched points. */ + + for (i = start; i <= end; ++i) + { + if (!touched[i]) + continue; + + if (pair_start == -1) + { + pair_first = i; + goto next; + } + + pair_end = i; + + /* pair_start to pair_end are now a pair of points, where points + in between should be interpolated. */ + + for (j = pair_start + 1; j < pair_end; ++j) + { + /* Consider the X axis. Set min_pos and max_pos to the + smallest and greatest values along that axis. */ + min_pos = MIN (x[pair_start], x[pair_end]); + max_pos = MAX (x[pair_start], x[pair_end]); + + /* Now see if the current point lies between min and + max... */ + if (x[j] >= min_pos && x[j] <= max_pos) + { + /* If min_pos and max_pos are the same, apply + pair_start's delta if it is identical to that of + pair_end, or apply nothing at all otherwise. */ + + if (min_pos == max_pos) + { + if ((glyph->simple->x_coordinates[pair_start] + - x[pair_start]) + == (glyph->simple->x_coordinates[pair_end] + - x[pair_end])) + glyph->simple->x_coordinates[j] + += (glyph->simple->x_coordinates[pair_start] + - x[pair_start]); + + continue; + } + + /* Interpolate between min_pos and max_pos. */ + ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos) + * 65536), + (sfnt_sub (max_pos, min_pos) + * 65536)); + + /* Load the current positions of pair_start and pair_end + along this axis. */ + min_pos = MIN (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + max_pos = MAX (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + + /* Lerp in between. */ + delta = sfnt_sub (max_pos, min_pos); + delta = sfnt_mul_fixed (ratio, delta); + glyph->simple->x_coordinates[j] = min_pos + delta; + } + else + { + /* ... otheriwse, move point j by the delta of the + nearest touched point. */ + + if (x[j] >= max_pos) + { + position = MAX (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + delta = position - max_pos; + } + else + { + position = MIN (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + delta = position - min_pos; + } + + glyph->simple->x_coordinates[j] = x[j] + delta; + } + + /* Now, consider the Y axis. */ + min_pos = MIN (y[pair_start], y[pair_end]); + max_pos = MAX (y[pair_start], y[pair_end]); + + /* Now see if the current point lies between min and + max... */ + if (y[j] >= min_pos && y[j] <= max_pos) + { + /* If min_pos and max_pos are the same, apply + pair_start's delta if it is identical to that of + pair_end, or apply nothing at all otherwise. */ + + if (min_pos == max_pos) + { + if ((glyph->simple->y_coordinates[pair_start] + - y[pair_start]) + == (glyph->simple->y_coordinates[pair_end] + - y[pair_end])) + glyph->simple->y_coordinates[j] + += (glyph->simple->y_coordinates[pair_start] + - y[pair_start]); + + continue; + } + + /* Interpolate between min_pos and max_pos. */ + ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos) + * 65536), + (sfnt_sub (max_pos, min_pos) + * 65536)); + + /* Load the current positions of pair_start and pair_end + along this axis. */ + min_pos = MIN (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + max_pos = MAX (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + + /* Lerp in between. */ + delta = sfnt_sub (max_pos, min_pos); + delta = sfnt_mul_fixed (ratio, delta); + glyph->simple->y_coordinates[j] = min_pos + delta; + } + else + { + /* ... otheriwse, move point j by the delta of the + nearest touched point. */ + + if (y[j] >= max_pos) + { + position = MAX (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + delta = position - max_pos; + } + else + { + position = MIN (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + delta = position - min_pos; + } + + glyph->simple->y_coordinates[j] = y[j] + delta; + } + } + + next: + pair_start = i; + } + + /* If pair_start is set, then lerp points between it and + pair_first. */ + + if (pair_start != (size_t) -1) + { + j = pair_start + 1; + + if (j > end) + j = start; + + pair_end = pair_first; + + while (j != pair_first) + { + /* Consider the X axis. Set min_pos and max_pos to the + smallest and greatest values along that axis. */ + min_pos = MIN (x[pair_start], x[pair_end]); + max_pos = MAX (x[pair_start], x[pair_end]); + + /* Now see if the current point lies between min and + max... */ + if (x[j] >= min_pos && x[j] <= max_pos) + { + /* If min_pos and max_pos are the same, apply + pair_start's delta if it is identical to that of + pair_end, or apply nothing at all otherwise. */ + + if (min_pos == max_pos) + { + if ((glyph->simple->x_coordinates[pair_start] + - x[pair_start]) + == (glyph->simple->x_coordinates[pair_end] + - x[pair_end])) + glyph->simple->x_coordinates[j] + += (glyph->simple->x_coordinates[pair_start] + - x[pair_start]); + + goto next_1; + } + + /* Interpolate between min_pos and max_pos. */ + ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos) + * 65536), + (sfnt_sub (max_pos, min_pos) + * 65536)); + + /* Load the current positions of pair_start and pair_end + along this axis. */ + min_pos = MIN (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + max_pos = MAX (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + + /* Lerp in between. */ + delta = sfnt_sub (max_pos, min_pos); + delta = sfnt_mul_fixed (ratio, delta); + glyph->simple->x_coordinates[j] = min_pos + delta; + } + else + { + /* ... otheriwse, move point j by the delta of the + nearest touched point. */ + + if (x[j] >= max_pos) + { + position = MAX (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + delta = position - max_pos; + } + else + { + position = MIN (glyph->simple->x_coordinates[pair_start], + glyph->simple->x_coordinates[pair_end]); + delta = position - min_pos; + } + + glyph->simple->x_coordinates[j] = x[j] + delta; + } + + /* Now, consider the Y axis. */ + min_pos = MIN (y[pair_start], y[pair_end]); + max_pos = MAX (y[pair_start], y[pair_end]); + + /* Now see if the current point lies between min and + max... */ + if (y[j] >= min_pos && y[j] <= max_pos) + { + /* If min_pos and max_pos are the same, apply + pair_start's delta if it is identical to that of + pair_end, or apply nothing at all otherwise. */ + + if (min_pos == max_pos) + { + if ((glyph->simple->y_coordinates[pair_start] + - y[pair_start]) + == (glyph->simple->y_coordinates[pair_end] + - y[pair_end])) + glyph->simple->y_coordinates[j] + += (glyph->simple->y_coordinates[pair_start] + - y[pair_start]); + + goto next_1; + } + + /* Interpolate between min_pos and max_pos. */ + ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos) + * 65536), + (sfnt_sub (max_pos, min_pos) + * 65536)); + + /* Load the current positions of pair_start and pair_end + along this axis. */ + min_pos = MIN (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + max_pos = MAX (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + + /* Lerp in between. */ + delta = sfnt_sub (max_pos, min_pos); + delta = sfnt_mul_fixed (ratio, delta); + glyph->simple->y_coordinates[j] = min_pos + delta; + } + else + { + /* ... otheriwse, move point j by the delta of the + nearest touched point. */ + + if (y[j] >= max_pos) + { + position = MAX (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + delta = position - max_pos; + } + else + { + position = MIN (glyph->simple->y_coordinates[pair_start], + glyph->simple->y_coordinates[pair_end]); + delta = position - min_pos; + } + + glyph->simple->y_coordinates[j] = y[j] + delta; + } + + next_1: + j++; + if (j > end) + j = start; + } + } +} + +/* Infer point positions for contours that have been partially moved + by variation. For each contour in GLYPH, find pairs of points + which have had deltas applied. For each such pair, interpolate + points between the first point in the pair and the second by + considering each point along every one of the two axes (X and Y) + like so: + + - For each point that lies between the first point and the last + on the axis currently being considered, interpolate its + position in that axis so that the ratio between the first + point and the last in the original outline still holds. + + - For each point that lies to the left or top of the first point + on the axis being considered, use the delta of the first point. + + - And finally, for each point that lies to the right or bottom of + the last point on that axis, use the delta of the last + point. + + X and Y contain the original positions positions of each point. + TOUCHED contains whether or not each point has been changed by + an explicitly specified delta. + + Apply the inferred deltas back to GLYPH. */ + +static void +sfnt_infer_deltas (struct sfnt_glyph *glyph, bool *touched, + sfnt_fword *x, sfnt_fword *y) +{ + size_t i; + int point, first, end; + + point = 0; + for (i = 0; i < glyph->number_of_contours; ++i) + { + first = point; + end = glyph->simple->end_pts_of_contours[i]; + + /* Return if the glyph is invalid. */ + + if (first >= glyph->simple->number_of_points + || end >= glyph->simple->number_of_points + || first > end) + return; + + sfnt_infer_deltas_1 (glyph, first, end, touched, x, y); + point = end + 1; + } +} + +/* Read the glyph variation data for the specified glyph ID from + BLEND's gvar table. Apply the offsets to each point in the + specified simple GLYPH, based on the specified BLEND. + + Value is 0 upon success, else 1. + + The glyph variation data consists of a number of elements, each of + which has its own associated point numbers and deltas, and a list + of one or two coordinates for each axis. Each such list is + referred to as a ``tuple''. + + The deltas, one for each point, are multipled by the normalized + value of each axis and applied to those points for each tuple that + is found to be applicable. + + Each element of the glyph variation data is applicable to an axis + if its list of coordinates: + + - contains one element for each axis, and its axis has a value + between 0 and that element. + + - contains two elements for each axis, and its axis has a value + between the first element and the second. + + After the deltas are applied, any points without deltas must be + interpolated similar to an IUP instruction. In addition, deltas + may also be applied to phantom points within a glyph. */ + +static int +sfnt_vary_glyph (struct sfnt_blend *blend, sfnt_glyph id, + struct sfnt_glyph *glyph) +{ + uint32_t offset; + struct sfnt_gvar_glyph_header header; + uint16_t *points, npoints; + int i, ntuples, j, point_count; + unsigned char *tuple, *end, *data; + uint16_t data_size, index, *glyph_points; + sfnt_f2dot14 *restrict coords; + sfnt_f2dot14 *restrict intermediate_start; + sfnt_f2dot14 *restrict intermediate_end; + sfnt_fword *dx, *dy, fword; + struct sfnt_gvar_table *gvar; + uint16_t *local_points, n_local_points; + sfnt_fixed scale; + ptrdiff_t data_offset; + bool *touched; + sfnt_fword *restrict original_x, *restrict original_y; + + gvar = blend->gvar; + + if (gvar->axis_count != blend->fvar->axis_count) + return 1; + + if (gvar->glyph_count <= id) + return 1; + + if (gvar->flags & 1) + offset = gvar->u.offset_long[id]; + else + offset = gvar->u.offset_word[id] * 2u; + + if (offset >= gvar->data_size) + return 1; + + end = gvar->glyph_variation_data + gvar->data_size; + + /* Start reading the header. */ + + if (offset + sizeof header > gvar->data_size) + return 1; + + memcpy (&header, gvar->glyph_variation_data + offset, + sizeof header); + + /* Swap the header. */ + sfnt_swap16 (&header.tuple_count); + sfnt_swap16 (&header.data_offset); + + /* Prepare to read each tuple. */ + ntuples = header.tuple_count & 0x0fff; + + /* Initialize the data offset. This is incremented with each tuple + read. */ + data_offset = header.data_offset; + + /* If gvar->flags & tuples_share_point_numbers, read the shared + point numbers. */ + + npoints = 0; + + if (header.tuple_count & 0x8000) + { + data = gvar->glyph_variation_data + offset + data_offset; + points = sfnt_read_packed_points (data, &npoints, end, + &tuple); + + if (!points) + return 1; + + /* Shared point numbers are part of the data after the tuple + array. Thus, increment data_offset by tuple - data. `tuple' + here holds no relation to a pointer to the current part of + the tuple array that is being read later on. */ + data_offset += tuple - data; + } + else + points = NULL; + + /* Start reading each tuple. */ + tuple = gvar->glyph_variation_data + offset + sizeof header; + + coords = xmalloc (gvar->axis_count * sizeof *coords * 3); + intermediate_start = coords + gvar->axis_count; + intermediate_end = coords + gvar->axis_count; + + /* Allocate arrays of booleans and fwords to keep track of which + points have been touched. */ + touched = NULL; + original_x = NULL; + original_y = NULL; + + for (i = 0; i < ntuples; ++i) + { + data = gvar->glyph_variation_data + offset + data_offset; + + if (tuple + 3 >= end) + goto fail1; + + memcpy (&data_size, tuple, sizeof data_size); + tuple += sizeof data_size; + memcpy (&index, tuple, sizeof index); + tuple += sizeof index; + sfnt_swap16 (&data_size); + sfnt_swap16 (&index); + + /* Increment the offset to the data by the data size specified + here. */ + data_offset += data_size; + + if (index & 0x8000) + { + /* Embedded coordinates are present. Read each + coordinate and add it to the tuple. */ + for (j = 0; j < gvar->axis_count; ++j) + { + if (tuple + 1 >= end) + goto fail1; + + memcpy (&coords[j], tuple, sizeof *coords); + tuple += sizeof *coords; + sfnt_swap16 (&coords[j]); + } + } + else if ((index & 0xfff) > gvar->shared_coord_count) + /* index exceeds the number of shared tuples present. */ + goto fail1; + else + /* index points into gvar->axis_count coordinates making up + the tuple. */ + memcpy (coords, (gvar->global_coords + + ((index & 0xfff) * gvar->axis_count)), + gvar->axis_count * sizeof *coords); + + /* Now read indeterminate tuples if required. */ + if (index & 0x1000) + { + for (j = 0; j < gvar->axis_count; ++j) + { + if (tuple + 1 >= end) + goto fail1; + + memcpy (&intermediate_start[j], tuple, + sizeof *intermediate_start); + tuple += sizeof *intermediate_start; + sfnt_swap16 (&intermediate_start[j]); + } + + for (j = 0; j < gvar->axis_count; ++j) + { + if (tuple + 1 >= end) + goto fail1; + + memcpy (&intermediate_end[j], tuple, + sizeof *intermediate_end); + tuple += sizeof *intermediate_end; + sfnt_swap16 (&intermediate_end[j]); + } + } + + /* See whether or not the tuple applies to the current variation + configuration, and how much to scale them by. */ + + scale = sfnt_compute_tuple_scale (blend, index & 0x1000, + coords, intermediate_start, + intermediate_end); + + if (!scale) + continue; + + local_points = NULL; + + /* Finally, read private point numbers. + Set local_points to those numbers; it will be freed + once the loop ends. */ + + if (index & 0x2000) + { + local_points = sfnt_read_packed_points (data, &n_local_points, + end, &data); + if (!local_points) + goto fail1; + + point_count = n_local_points; + glyph_points = local_points; + } + else + { + /* If there are no private point numbers, use global + points. */ + point_count = npoints; + glyph_points = points; + } + + /* Now, read packed deltas. */ + + dx = NULL; + dy = NULL; + + switch (point_count) + { + case UINT16_MAX: + /* Deltas are provided for all points in the glyph. + No glyph should have more than 65535 points. */ + + if (glyph->simple->number_of_points > 65535) + abort (); + + /* Add 4 phantom points to each end. */ + dx = sfnt_read_packed_deltas (data, end, + glyph->simple->number_of_points + 4, + &data); + dy = sfnt_read_packed_deltas (data, end, + glyph->simple->number_of_points + 4, + &data); + + if (!dx || !dy) + goto fail3; + + /* Apply each delta to the simple glyph. */ + + for (i = 0; i < glyph->simple->number_of_points; ++i) + { + fword = sfnt_mul_fixed (dx[i], scale); + glyph->simple->x_coordinates[i] += fword; + fword = sfnt_mul_fixed (dy[i], scale); + glyph->simple->y_coordinates[i] += fword; + } + + /* TODO: apply metrics variations. */ + break; + + default: + dx = sfnt_read_packed_deltas (data, end, point_count, &data); + dy = sfnt_read_packed_deltas (data, end, point_count, &data); + + if (!dx || !dy) + goto fail3; + + /* Deltas are only applied for each point number read. */ + + if (!original_x) + { + touched = xmalloc (sizeof *touched + * glyph->simple->number_of_points); + original_x = xmalloc (sizeof *original_x * 2 + * glyph->simple->number_of_points); + original_y = original_x + glyph->simple->number_of_points; + memcpy (original_x, glyph->simple->x_coordinates, + (sizeof *original_x + * glyph->simple->number_of_points)); + memcpy (original_y, glyph->simple->y_coordinates, + (sizeof *original_y + * glyph->simple->number_of_points)); + } + + memset (touched, 0, (sizeof *touched + * glyph->simple->number_of_points)); + + for (i = 0; i < point_count; ++i) + { + /* Make sure the point doesn't end up out of bounds. */ + if (glyph_points[i] >= glyph->simple->number_of_points) + continue; + + fword = sfnt_mul_fixed (dx[i], scale); + glyph->simple->x_coordinates[glyph_points[i]] += fword; + fword = sfnt_mul_fixed (dy[i], scale); + glyph->simple->y_coordinates[glyph_points[i]] += fword; + touched[glyph_points[i]] = true; + } + + sfnt_infer_deltas (glyph, touched, original_x, + original_y); + + /* TODO: apply metrics variations. */ + break; + } + + xfree (dx); + xfree (dy); + + if (local_points != (uint16_t *) -1) + xfree (local_points); + } + + /* Return success. */ + + xfree (touched); + xfree (coords); + xfree (original_x); + + if (points != (uint16_t *) -1) + xfree (points); + + return 0; + + fail3: + xfree (dx); + xfree (dy); + xfree (local_points); + fail1: + xfree (touched); + xfree (coords); + xfree (original_x); + + if (points != (uint16_t *) -1) + xfree (points); + + return 1; +} + +#endif /* TEST */ + + + +#ifdef TEST + +struct sfnt_test_dcontext +{ + /* Context for sfnt_test_get_glyph. */ + struct sfnt_glyf_table *glyf; + struct sfnt_loca_table_short *loca_short; + struct sfnt_loca_table_long *loca_long; +}; + +/* Global context for test functions. Height of glyph. */ +static sfnt_fixed sfnt_test_max; + +static void +sfnt_test_move_to (struct sfnt_point point, void *dcontext) +{ + printf ("move_to: %g, %g\n", sfnt_coerce_fixed (point.x), + sfnt_coerce_fixed (point.y)); +} + +static void +sfnt_test_line_to (struct sfnt_point point, void *dcontext) +{ + printf ("line_to: %g, %g\n", sfnt_coerce_fixed (point.x), + sfnt_coerce_fixed (point.y)); +} + +static void +sfnt_test_curve_to (struct sfnt_point control, + struct sfnt_point endpoint, + void *dcontext) +{ + printf ("curve_to: %g, %g - %g, %g\n", + sfnt_coerce_fixed (control.x), + sfnt_coerce_fixed (control.y), + sfnt_coerce_fixed (endpoint.x), + sfnt_coerce_fixed (endpoint.y)); +} + +static struct sfnt_glyph * +sfnt_test_get_glyph (sfnt_glyph glyph, void *dcontext, + bool *need_free) +{ + struct sfnt_test_dcontext *tables; + + tables = dcontext; + *need_free = true; + + return sfnt_read_glyph (glyph, tables->glyf, + tables->loca_short, + tables->loca_long); +} + +static void +sfnt_test_free_glyph (struct sfnt_glyph *glyph, void *dcontext) +{ + sfnt_free_glyph (glyph); +} + +static void +sfnt_test_span (struct sfnt_edge *edge, sfnt_fixed y, + void *dcontext) +{ +#if 1 + printf ("/* span at %g */\n", sfnt_coerce_fixed (y)); + for (; edge; edge = edge->next) + { + if (y >= edge->bottom && y < edge->top) + printf ("ctx.fillRect (%g, %g, 1, 1); " + "/* %g top: %g bot: %g stepx: %g winding: %d */\n", + sfnt_coerce_fixed (edge->x), + sfnt_coerce_fixed (sfnt_test_max - y), + sfnt_coerce_fixed (y), + sfnt_coerce_fixed (edge->top), + sfnt_coerce_fixed (edge->bottom), + sfnt_coerce_fixed (edge->step_x), + edge->winding); + else + printf ("STRIPPED BAD SPAN!!! %g %g %"PRIi32 + " %"PRIi32" (winding: %d)\n", + sfnt_coerce_fixed (edge->top), + sfnt_coerce_fixed (edge->bottom), + edge->top, y, edge->winding); + } +#elif 0 + int winding; + short x, dx; + + winding = 0; + x = 0; + + for (; edge; edge = edge->next) + { + dx = (edge->x >> 16) - x; + x = edge->x >> 16; + + for (; dx > 0; --dx) + putc (winding ? '.' : ' ', stdout); + + winding = !winding; + } + + putc ('\n', stdout); +#elif 0 + for (; edge; edge = edge->next) + printf ("%g-", sfnt_coerce_fixed (edge->x)); + puts (""); +#endif +} + +static void +sfnt_test_edge_ignore (struct sfnt_edge *edges, size_t num_edges, + void *dcontext) +{ + +} + +/* The same debugger stuff is used here. */ +static void sfnt_setup_debugger (void); + +/* The debugger's X display. */ +static Display *display; + +/* The debugger window. */ +static Window window; + +/* The GC. */ +static GC point_gc, background_gc; + +static void +sfnt_test_edges (struct sfnt_edge *edges, size_t num_edges) +{ + static sfnt_fixed y; + size_t i; + + for (i = 0; i < num_edges; ++i) + { + if (y >= edges[i].bottom && y < edges[i].top) + { + XDrawPoint (display, window, point_gc, + edges[i].x / 65536, 100 - (y / 65536)); + printf ("sfnt_test_edges: %d %d\n", + edges[i].x / 65536, 100 - (y / 65536)); + } + } + + y += SFNT_POLY_STEP; + + for (i = 0; i < num_edges; ++i) + sfnt_step_edge (&edges[i]); +} + +static void +sfnt_debug_edges (struct sfnt_edge *edges, size_t num_edges) +{ + XEvent event; + + sfnt_setup_debugger (); + + while (true) + { + XNextEvent (display, &event); + + switch (event.type) + { + case KeyPress: + XDestroyWindow (display, window); + XCloseDisplay (display); + exit (0); + break; + + case Expose: + + while (true) + { + sfnt_test_edges (edges, num_edges); + XFlush (display); + usleep (50000); + } + + break; + } + } +} + +static void +sfnt_test_edge (struct sfnt_edge *edges, size_t num_edges, + void *dcontext) +{ + size_t i; + + printf ("built %zu edges\n", num_edges); + + for (i = 0; i < num_edges; ++i) + { + printf ("/* edge x, top, bot: %g, %g - %g. winding: %d */\n" + "/* edge step_x: %g */\n", + sfnt_coerce_fixed (edges[i].x), + sfnt_coerce_fixed (edges[i].top), + sfnt_coerce_fixed (edges[i].bottom), + edges[i].winding, + sfnt_coerce_fixed (edges[i].step_x)); +#ifdef TEST_VERTEX + printf ("ctx.fillRect (%g, %g, 1, 1);\n", + sfnt_coerce_fixed (edges[i].x), + sfnt_coerce_fixed (sfnt_test_max + - edges[i].y)); +#else + printf ("ctx.fillRect (%g, %g, 1, 1);\n", + sfnt_coerce_fixed (edges[i].x), + sfnt_coerce_fixed (sfnt_test_max + - edges[i].bottom)); +#endif + } + + if (getenv ("SFNT_DEBUG_STEP")) + { + if (!fork ()) + sfnt_debug_edges (edges, num_edges); + } + + printf ("==end of edges==\n"); + + sfnt_poly_edges (edges, num_edges, sfnt_test_span, NULL); +} + +static void +sfnt_x_raster (struct sfnt_raster **rasters, + int *advances, + int nrasters, + struct sfnt_hhea_table *hhea, + sfnt_fixed scale) +{ + Display *display; + Window window; + Pixmap *pixmaps; + Picture *glyphs, drawable, solid; + int event_base, error_base; + int major, minor, *depths, count; + XRenderPictFormat *format, *glyph_format; + Visual *visual; + XImage image; + GC gc; + XGCValues gcvalues; + XEvent event; + XRenderColor white, black; + int i, ascent, origin, x, y; + Font font; + + if (!nrasters) + exit (0); + + display = XOpenDisplay (NULL); + + if (!display) + exit (0); + + if (!XRenderQueryExtension (display, &event_base, &error_base) + || !XRenderQueryVersion (display, &major, &minor)) + exit (0); + + if (major == 0 && minor < 10) + exit (0); + + window = XCreateSimpleWindow (display, DefaultRootWindow (display), + 0, 0, 100, 150, 0, 0, + WhitePixel (display, + DefaultScreen (display))); + XSelectInput (display, window, ExposureMask); + XMapWindow (display, window); visual = DefaultVisual (display, DefaultScreen (display)); format = XRenderFindVisualFormat (display, visual); @@ -16262,15 +17909,22 @@ main (int argc, char **argv) struct sfnt_prep_table *prep; struct sfnt_graphics_state state; struct sfnt_instructed_outline *value; + struct sfnt_fvar_table *fvar; + struct sfnt_gvar_table *gvar; sfnt_fixed scale; char *fancy; int *advances; struct sfnt_raster **rasters; size_t length; + char *axis_name; + struct sfnt_instance *instance; + struct sfnt_blend blend; if (argc < 2) return 1; + instance = NULL; + if (!strcmp (argv[1], "--check-interpreter")) { interpreter = sfnt_make_test_interpreter (); @@ -16324,7 +17978,7 @@ main (int argc, char **argv) font = sfnt_read_table_directory (fd); } - if (!font) + if (!font || font == (struct sfnt_offset_subtable *) -1) { close (fd); return 1; @@ -16375,8 +18029,8 @@ main (int argc, char **argv) return 1; } -#define FANCY_PPEM 25 -#define EASY_PPEM 25 +#define FANCY_PPEM 12 +#define EASY_PPEM 12 interpreter = NULL; head = sfnt_read_head_table (fd, font); @@ -16388,16 +18042,18 @@ #define EASY_PPEM 25 cvt = sfnt_read_cvt_table (fd, font); fpgm = sfnt_read_fpgm_table (fd, font); prep = sfnt_read_prep_table (fd, font); + fvar = sfnt_read_fvar_table (fd, font); + gvar = sfnt_read_gvar_table (fd, font); hmtx = NULL; exec_prep = prep; exec_fpgm = fpgm; - - if (getenv ("SFNT_FANCY_TEST")) + fancy = getenv ("SFNT_FANCY_TEST"); + + if (fancy) { loca_long = NULL; loca_short = NULL; - fancy = getenv ("SFNT_FANCY_TEST"); length = strlen (fancy); scale = sfnt_div_fixed (FANCY_PPEM, head->units_per_em); @@ -16659,6 +18315,82 @@ #define EASY_PPEM 25 (int) hhea->caret_slope_rise, (int) hhea->caret_slope_run); + if (fvar) + { + fprintf (stderr, "FVAR table found!\n" + "version: %"PRIu16".%"PRIu16"\n" + "axis_count: %"PRIu16"\n" + "axis_size: %"PRIu16"\n" + "instance_count: %"PRIu16"\n" + "instance_size: %"PRIu16"\n", + fvar->major_version, + fvar->minor_version, + fvar->axis_count, + fvar->axis_size, + fvar->instance_count, + fvar->instance_size); + + for (i = 0; i < fvar->axis_count; ++i) + { + if (name) + { + axis_name + = (char *) sfnt_find_name (name, fvar->axis[i].name_id, + &record); + + if (axis_name) + fprintf (stderr, "axis no: %d; name: %.*s\n", + i, record.length, axis_name); + } + + fprintf (stderr, " axis: %"PRIx32" %g %g %g\n", + fvar->axis[i].axis_tag, + sfnt_coerce_fixed (fvar->axis[i].min_value), + sfnt_coerce_fixed (fvar->axis[i].default_value), + sfnt_coerce_fixed (fvar->axis[i].max_value)); + } + + for (i = 0; i < fvar->instance_count; ++i) + { + if (name) + { + axis_name + = (char *) sfnt_find_name (name, fvar->instance[i].name_id, + &record); + + if (axis_name) + fprintf (stderr, "instance no: %d; name: %.*s\n", + i, record.length, axis_name); + } + } + + if (fvar->instance_count > 1) + { + printf ("instance? "); + + if (scanf ("%d", &i) == EOF) + goto free_lab; + + if (i < 0 || i >= fvar->instance_count) + goto free_lab; + + instance = &fvar->instance[i]; + } + } + + if (gvar) + fprintf (stderr, "gvar table found\n"); + + if (instance && gvar) + { + sfnt_init_blend (&blend, fvar, gvar); + + for (i = 0; i < fvar->axis_count; ++i) + blend.coords[i] = instance->coords[i]; + + sfnt_normalize_blend (&blend); + } + while (true) { printf ("table, character? "); @@ -16696,6 +18428,14 @@ #define EASY_PPEM 25 dcontext.loca_short = loca_short; dcontext.loca_long = loca_long; + if (glyph->simple && instance && gvar) + { + printf ("applying variations to simple glyph...\n"); + + if (sfnt_vary_glyph (&blend, code, glyph)) + printf ("variation failed!\n"); + } + if (sfnt_decompose_glyph (glyph, sfnt_test_move_to, sfnt_test_line_to, sfnt_test_curve_to, @@ -16871,6 +18611,8 @@ #define FG sfnt_test_free_glyph } } + free_lab: + xfree (font); for (i = 0; i < table->num_subtables; ++i) @@ -16893,6 +18635,11 @@ #define FG sfnt_test_free_glyph xfree (fpgm); xfree (interpreter); xfree (prep); + xfree (fvar); + xfree (gvar); + + if (instance && gvar) + sfnt_free_blend (&blend); return 0; } diff --git a/src/sfnt.h b/src/sfnt.h index 83d34bfb757..82fa343f51d 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -53,6 +53,9 @@ #define SFNT_ENABLE_HINTING SFNT_TABLE_CVT , SFNT_TABLE_FPGM, SFNT_TABLE_PREP, + SFNT_TABLE_FVAR, + SFNT_TABLE_GVAR, + SFNT_TABLE_CVAR, }; #define SFNT_ENDOF(type, field, type1) \ @@ -61,7 +64,14 @@ #define SFNT_ENDOF(type, field, type1) \ /* Each of these structures must be aligned so that no compiler will ever generate padding bytes on platforms where the alignment requirements for uint32_t and uint16_t are no larger than 4 and 2 - bytes respectively. */ + bytes respectively. + + Pointer types are assumed to impose an alignmnent requirement no + less than that of uint32_t. + + If a table has more than one kind of variable-length subtable array + at the end, make sure to pad subsequent subtables + appropriately. */ struct sfnt_offset_subtable { @@ -568,25 +578,25 @@ #define sfnt_coerce_fixed(fixed) ((sfnt_fixed) (fixed) / 65535.0) size_t number_of_points; /* Array containing the last points of each contour. */ - uint16_t *end_pts_of_contours; + uint16_t *restrict end_pts_of_contours; /* Total number of bytes needed for instructions. */ uint16_t instruction_length; /* Instruction data. */ - uint8_t *instructions; + uint8_t *restrict instructions; /* Array of flags. */ - uint8_t *flags; + uint8_t *restrict flags; /* Array of X coordinates. */ - int16_t *x_coordinates; + int16_t *restrict x_coordinates; /* Array of Y coordinates. */ - int16_t *y_coordinates; + int16_t *restrict y_coordinates; /* Pointer to the end of that array. */ - int16_t *y_coordinates_end; + int16_t *restrict y_coordinates_end; }; struct sfnt_compound_glyph_component commit b26b4c537bb031027dafd4dccda6a8cc5874ce4d Author: Po Lu Date: Mon Mar 20 15:37:07 2023 +0800 ; * java/INSTALL: Fix typo. diff --git a/java/INSTALL b/java/INSTALL index 87d8979eb47..4bab7d5a2da 100644 --- a/java/INSTALL +++ b/java/INSTALL @@ -325,6 +325,9 @@ unpack the following tar archive in that site: and add the resulting folder to ``--with-ndk-build''. + +IMAGEMAGICK + There is a third party port of ImageMagick to Android. Unfortunately, the port also uses its own patched versions of libpng, libjpeg, libtiff and libwebp, which conflict with those used by Emacs. Its commit a7902e9d8051cb977518e3cfe9a44b1e0fd0420c Merge: 6bd1cfa24fd 42fba8f36b1 Author: Po Lu Date: Mon Mar 20 14:48:13 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 6bd1cfa24fd04de855e53e74b46cdf4047bced4c Author: Po Lu Date: Mon Mar 20 14:47:39 2023 +0800 Update Android port * configure.ac: Add support for HarfBuzz on Android. * java/INSTALL: Document where to get Emacs with HarfBuzz. * lisp/subr.el (overriding-text-conversion-style, y-or-n-p): Correctly set text conversion style if y-or-n-p is called inside the minibuffer. * src/sfnt.c (sfnt_read_cmap_format_8) (sfnt_read_cmap_format_12): Fix typos. (sfnt_read_24, sfnt_read_cmap_format_14): New function. (sfnt_read_cmap_table_1, sfnt_read_cmap_table): Handle format 14 cmap tables. (sfnt_read_default_uvs_table, sfnt_read_nondefault_uvs_table) (sfnt_compare_table_offsets, sfnt_create_uvs_context) (sfnt_free_uvs_context, sfnt_compare_uvs_mapping) (sfnt_variation_glyph_for_char, sfnt_map_table, sfnt_unmap_table) (sfnt_read_table, sfnt_test_uvs): New functions. (main): Add UVS tests. * src/sfnt.h (struct sfnt_cmap_format_14) (struct sfnt_variation_selector_record) (struct sfnt_default_uvs_table, struct sfnt_unicode_value_range) (struct sfnt_nondefault_uvs_table, struct sfnt_uvs_mapping) (struct sfnt_mapped_variation_selector_record) (struct sfnt_table_offset_rec, struct sfnt_uvs_context) (struct sfnt_mapped_table): New structures. Update prototypes. * src/sfntfont-android.c (android_sfntfont_driver): Register HarfBuzz callbacks where required. * src/sfntfont.c (sfntfont_select_cmap): Look for a format 14 table. Save it in new arg FORMAT14. (sfntfont_read_cmap): Adjust accordingly. (struct sfnt_font_info): New field `uvs'. New fields `hb_font', `fd' and `directory'. (sfntfont_open): Open uvs context. Under HarfBuzz, don't close the fd or subtable, but save them in the font info instead. (sfntfont_close): Free UVS context. Close font fd and table directory and HarfBuzz font. (sfntfont_draw): Handle case where s->padding_p. (sfntfont_get_variation_glyphs): New function. (sfntfont_unmap_blob, sfntfont_get_font_table) (sfntfont_begin_hb_font): New functions. * src/sfntfont.h: Update prototypes. * src/textconv.c (Fset_text_conversion_style): Fix doc string. diff --git a/configure.ac b/configure.ac index 44dbf60f938..47e5227a148 100644 --- a/configure.ac +++ b/configure.ac @@ -1171,6 +1171,7 @@ AC_DEFUN passthrough="$passthrough --with-lcms2=$with_lcms2" passthrough="$passthrough --with-mailutils=$with_mailutils" passthrough="$passthrough --with-pop=$with_pop" + passthrough="$passthrough --with-harfbuzz=$with_harfbuzz" AS_IF([test "x$with_mailutils" = "xyes"], [emacs_use_mailutils=yes]) AC_SUBST([emacs_use_mailutils]) @@ -1255,13 +1256,13 @@ AC_DEFUN with_lcms2=no with_mailutils=no with_pop=no + with_harfbuzz=no fi with_rsvg=no with_libsystemd=no with_cairo=no with_xft=no - with_harfbuzz=no with_libotf=no with_gpm=no with_dbus=no @@ -4581,7 +4582,8 @@ AC_DEFUN fi if test "${HAVE_X11}" = "yes" && test "${HAVE_FREETYPE}" = "yes" \ || test "$window_system" = "pgtk" \ - || test "${HAVE_W32}" = "yes"; then + || test "${HAVE_W32}" = "yes" \ + || test "$REALLY_ANDROID" = "yes"; then if test "${with_harfbuzz}" != "no"; then EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= $harfbuzz_required_ver]) if test "$HAVE_HARFBUZZ" = "yes"; then diff --git a/java/INSTALL b/java/INSTALL index 676c63a3cda..87d8979eb47 100644 --- a/java/INSTALL +++ b/java/INSTALL @@ -265,6 +265,8 @@ systems: (Extract and point ``--with-ndk-path'' to tiff-4.5.0-emacs.tar.gz.) tree-sitter - https://sourceforge.net/projects/android-ports-for-gnu-emacs (Please see the section TREE-SITTER near the end of this file.) + harfbuzz - https://sourceforge.net/projects/android-ports-for-gnu-emacs + (Please see the section HARFBUZZ near the end of this file.) And other developers have ported the following dependencies to Android systems: @@ -305,15 +307,23 @@ should not try to build these packages separately using any TREE-SITTER A copy of tree-sitter modified to build with the ndk-build system can -also find that URL. To build Emacs with tree-sitter, you must unpack -the following tar archive in that site: +also be found that URL. To build Emacs with tree-sitter, you must +unpack the following tar archive in that site: tree-sitter-0.20.7-emacs.tar.gz and add the resulting folder to ``--with-ndk-build''. -IMAGEMAGICK +HARFBUZZ + +A copy of HarfBuzz modified to build with the ndk-build system can +also be found at that URL. To build Emacs with HarfBuzz, you must +unpack the following tar archive in that site: + + harfbuzz-7.1.0-emacs.tar.gz + +and add the resulting folder to ``--with-ndk-build''. There is a third party port of ImageMagick to Android. Unfortunately, the port also uses its own patched versions of libpng, libjpeg, diff --git a/lisp/subr.el b/lisp/subr.el index e035ce51217..2f72945789b 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -3598,6 +3598,7 @@ use-dialog-box-p ;; Actually in textconv.c. (defvar overriding-text-conversion-style) +(declare-function set-text-conversion-style "textconv.c") (defun y-or-n-p (prompt) "Ask user a \"y or n\" question. @@ -3674,6 +3675,9 @@ y-or-n-p (while (let* ((scroll-actions '(recenter scroll-up scroll-down scroll-other-window scroll-other-window-down)) + ;; Disable text conversion so that real key events + ;; are sent. + (overriding-text-conversion-style nil) (key (let ((cursor-in-echo-area t)) (when minibuffer-auto-raise @@ -3721,9 +3725,15 @@ y-or-n-p map)) ;; Protect this-command when called from pre-command-hook (bug#45029) (this-command this-command) - (str (read-from-minibuffer - prompt nil keymap nil - (or y-or-n-p-history-variable t)))) + (str (progn + (when (active-minibuffer-window) + ;; If the minibuffer is already active, the + ;; selected window might not change. Disable + ;; text conversion by hand. + (set-text-conversion-style text-conversion-style)) + (read-from-minibuffer + prompt nil keymap nil + (or y-or-n-p-history-variable t))))) (setq answer (if (member str '("y" "Y")) 'act 'skip))))) (let ((ret (eq answer 'act))) (unless noninteractive diff --git a/src/sfnt.c b/src/sfnt.c index 5b219bf6369..bdd713aa016 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -601,7 +601,7 @@ sfnt_read_cmap_format_8 (int fd, ssize_t rc; uint32_t length, i; - /* Read the 32-bit lenth field. */ + /* Read the 32-bit length field. */ if (read (fd, &length, sizeof (length)) < sizeof (length)) return (struct sfnt_cmap_format_8 *) -1; @@ -693,7 +693,7 @@ sfnt_read_cmap_format_12 (int fd, ssize_t rc; uint32_t length, i; - /* Read the 32-bit lenth field. */ + /* Read the 32-bit length field. */ if (read (fd, &length, sizeof (length)) < sizeof (length)) return (struct sfnt_cmap_format_12 *) -1; @@ -773,6 +773,98 @@ sfnt_read_cmap_format_12 (int fd, return format12; } +/* Read a 3-byte big endian number from BYTES. */ + +static unsigned int +sfnt_read_24 (unsigned char *bytes) +{ + return (bytes[0] << 16u) | (bytes[1] << 8u) | bytes[2]; +} + +/* Read a format 14 cmap table from FD. HEADER->format will be 14 and + HEADER->length will be 0; the 16-bit length field is not read. + OFFSET is the offset of the table's header in the font file. + + Only variation selector records will be read. UVS tables will + not. */ + +static struct sfnt_cmap_format_14 * +sfnt_read_cmap_format_14 (int fd, + struct sfnt_cmap_encoding_subtable_data *header, + off_t offset) +{ + struct sfnt_cmap_format_14 *format14; + uint32_t length; + uint32_t num_records; + uint32_t buffer1[2]; + size_t size, temp; + char buffer[3 + 4 + 4]; + int i; + + /* Read the length field and number of variation selector + records. */ + + if (read (fd, buffer1, sizeof buffer1) < sizeof buffer1) + return NULL; + + length = buffer1[0]; + num_records = buffer1[1]; + + sfnt_swap32 (&length); + sfnt_swap32 (&num_records); + + /* Now, the number of records present is known. Allocate the format + 14 cmap table. */ + + size = sizeof *format14; + if (INT_MULTIPLY_WRAPV (num_records, sizeof *format14->records, + &temp) + || INT_ADD_WRAPV (size, temp, &size)) + return NULL; + + format14 = xmalloc (size); + + /* Fill in the data already read. */ + format14->format = header->format; + format14->length = length; + format14->num_var_selector_records = num_records; + format14->offset = offset; + + /* Set the pointer to the remaining record data. */ + format14->records + = (struct sfnt_variation_selector_record *) (format14 + 1); + + /* Read each variation selector record. */ + + for (i = 0; i < num_records; ++i) + { + if (read (fd, buffer, sizeof buffer) < sizeof buffer) + { + xfree (format14); + return NULL; + } + + /* First, read the 24 bit variation selector. */ + format14->records[i].var_selector + = sfnt_read_24 ((unsigned char *) buffer); + + /* Next, read the two unaligned longs. */ + memcpy (&format14->records[i].default_uvs_offset, + buffer + 3, + sizeof format14->records[i].default_uvs_offset); + memcpy (&format14->records[i].nondefault_uvs_offset, + buffer + 7, + sizeof format14->records[i].nondefault_uvs_offset); + + /* And swap them. */ + sfnt_swap32 (&format14->records[i].default_uvs_offset); + sfnt_swap32 (&format14->records[i].nondefault_uvs_offset); + } + + /* Return the format 14 character mapping table. */ + return format14; +} + /* Read the CMAP subtable data from a given file FD at TABLE_OFFSET bytes from DIRECTORY_OFFSET. Return the subtable data if it is supported. Else, value is NULL if the format is unsupported, or -1 @@ -791,11 +883,26 @@ sfnt_read_cmap_table_1 (int fd, uint32_t directory_offset, if (lseek (fd, offset, SEEK_SET) == (off_t) -1) return (struct sfnt_cmap_encoding_subtable_data *) -1; - if (read (fd, &header, sizeof header) < sizeof header) + if (read (fd, &header.format, sizeof header.format) + < sizeof header.format) return (struct sfnt_cmap_encoding_subtable_data *) -1; sfnt_swap16 (&header.format); - sfnt_swap16 (&header.length); + + /* Format 14 tables are rather special: they do not have a 16-bit + `length' field. When these tables are encountered, leave reading + the rest of the header to `sfnt_read_cmap_table_14'. */ + + if (header.format != 14) + { + if (read (fd, &header.length, sizeof header.length) + < sizeof header.length) + return (struct sfnt_cmap_encoding_subtable_data *) -1; + + sfnt_swap16 (&header.length); + } + else + header.length = 0; switch (header.format) { @@ -828,6 +935,10 @@ sfnt_read_cmap_table_1 (int fd, uint32_t directory_offset, return ((struct sfnt_cmap_encoding_subtable_data *) sfnt_read_cmap_format_12 (fd, &header)); + case 14: + return ((struct sfnt_cmap_encoding_subtable_data *) + sfnt_read_cmap_format_14 (fd, &header, offset)); + default: return NULL; } @@ -909,8 +1020,7 @@ sfnt_read_cmap_table (int fd, struct sfnt_offset_subtable *subtable, return cmap; /* Second, read each encoding subtable itself. */ - *data = xmalloc (cmap->num_subtables - * sizeof *data); + *data = xmalloc (cmap->num_subtables * sizeof *data); for (i = 0; i < cmap->num_subtables; ++i) { @@ -1199,7 +1309,10 @@ sfnt_lookup_glyph_12 (sfnt_char character, /* Look up the glyph index corresponding to the character CHARACTER, which must be in the correct encoding for the cmap table pointed to - by DATA. */ + by DATA. + + DATA must be either a format 0, 2, 4, 6, 8 or 12 cmap table, else + behavior is undefined. */ TEST_STATIC sfnt_glyph sfnt_lookup_glyph (sfnt_char character, @@ -11775,6 +11888,551 @@ sfnt_interpret_compound_glyph (struct sfnt_glyph *glyph, +/* Unicode Variation Sequence (UVS) support. + + Unicode defines a mechanism by which a two-codepoint sequence + consisting of a ``base character'' and ``variation selector'' is + able to produce a glyph that is a variant of the glyph that would + conventionally have been mapped to the ``base character''. + + TrueType describes variation selector sequences through a type of + character mapping table that is given the format 14. The character + mapping table consists of an array of variation selectors, each of + which have a corresponding ``default UVS table'', which describes + ranges of ``base characters'' having no special variant glyphs, and + a ``non-default UVS table'', which is a map of ``base characters'' + to their corresponding variant glyphs. */ + +/* Read a default UVS table from the font file FD, at the specified + OFFSET. Value is the default UVS table upon success, else + NULL. */ + +static struct sfnt_default_uvs_table * +sfnt_read_default_uvs_table (int fd, off_t offset) +{ + struct sfnt_default_uvs_table *uvs; + uint32_t num_ranges, i, j; + size_t size, temp; + char data[512]; + + /* First, seek to the given offset. */ + + if (lseek (fd, offset, SEEK_SET) != offset) + return NULL; + + /* Next, read the number of ranges present. */ + + if (read (fd, &num_ranges, sizeof num_ranges) != sizeof num_ranges) + return NULL; + + /* Swap the number of ranges present. */ + sfnt_swap32 (&num_ranges); + + /* Now, allocate enough to hold the UVS table. */ + + size = sizeof *uvs; + if (INT_MULTIPLY_WRAPV (sizeof *uvs->ranges, num_ranges, + &temp) + || INT_ADD_WRAPV (temp, size, &size)) + return NULL; + + uvs = xmalloc (size); + + /* Fill in the data which was already read. */ + uvs->num_unicode_value_ranges = num_ranges; + + /* Fill in the pointer to the ranges. */ + uvs->ranges = (struct sfnt_unicode_value_range *) (uvs + 1); + i = 0; + + /* Read each default UVS range in multiples of 512 bytes. Then, + fill in uvs->ranges. */ + + while (num_ranges) + { + size = (num_ranges > 128 ? 512 : num_ranges * 4); + + if (read (fd, data, size) != size) + { + xfree (uvs); + return NULL; + } + + for (j = 0; j < size / 4; ++j) + { + uvs->ranges[i + j].start_unicode_value + = sfnt_read_24 ((unsigned char *) data + j * 4); + uvs->ranges[i + j].additional_count = data[j * 4 + 1]; + } + + i += j; + num_ranges -= size / 4; + } + + /* Return the resulting default UVS table. */ + return uvs; +} + +/* Read a non-default UVS table from the font file FD, at the + specified OFFSET. Value is the non-default UVS table upon success, + else NULL. */ + +static struct sfnt_nondefault_uvs_table * +sfnt_read_nondefault_uvs_table (int fd, off_t offset) +{ + struct sfnt_nondefault_uvs_table *uvs; + uint32_t num_mappings, i, j; + size_t size, temp; + char data[500]; + + /* First, seek to the given offset. */ + + if (lseek (fd, offset, SEEK_SET) != offset) + return NULL; + + /* Next, read the number of mappings present. */ + + if (read (fd, &num_mappings, sizeof num_mappings) + != sizeof num_mappings) + return NULL; + + /* Swap the number of mappings present. */ + sfnt_swap32 (&num_mappings); + + /* Now, allocate enough to hold the UVS table. */ + + size = sizeof *uvs; + if (INT_MULTIPLY_WRAPV (sizeof *uvs->mappings, num_mappings, + &temp) + || INT_ADD_WRAPV (temp, size, &size)) + return NULL; + + uvs = xmalloc (size); + + /* Fill in the data which was already read. */ + uvs->num_uvs_mappings = num_mappings; + + /* Fill in the pointer to the mappings. */ + uvs->mappings = (struct sfnt_uvs_mapping *) (uvs + 1); + + i = 0; + + /* Read each nondefault UVS mapping in multiples of 500 bytes. + Then, fill in uvs->ranges. */ + + while (num_mappings) + { + size = (num_mappings > 100 ? 500 : num_mappings * 5); + + if (read (fd, data, size) != size) + { + xfree (uvs); + return NULL; + } + + for (j = 0; j < size / 5; ++j) + { + uvs->mappings[i + j].unicode_value + = sfnt_read_24 ((unsigned char *) data + j * 5); + memcpy (&uvs->mappings[i + j].base_character_value, + data + j * 5 + 3, + sizeof uvs->mappings[i + j].base_character_value); + sfnt_swap16 (&uvs->mappings[i + j].base_character_value); + } + + i += j; + num_mappings -= size / 5; + } + + /* Return the nondefault UVS table. */ + return uvs; +} + +/* Perform comparison of A and B, two table offsets. */ + +static int +sfnt_compare_table_offsets (const void *a, const void *b) +{ + const struct sfnt_table_offset_rec *rec_a, *rec_b; + + rec_a = a; + rec_b = b; + + if (rec_a->offset < rec_b->offset) + return -1; + else if (rec_a->offset > rec_b->offset) + return 1; + + return 0; +} + +/* Create a variation selection context based on the format 14 cmap + subtable CMAP. + + FD is the font file to which the table belongs. + + Value is the variation selection context upon success, else NULL. + The context contains each variation selector record and their + associated default and nondefault UVS tables. Free the context + with `sfnt_free_uvs_context'. */ + +TEST_STATIC struct sfnt_uvs_context * +sfnt_create_uvs_context (struct sfnt_cmap_format_14 *cmap, int fd) +{ + struct sfnt_table_offset_rec *table_offsets, *rec, template; + size_t size, i, nmemb, j; + off_t offset; + struct sfnt_uvs_context *context; + + if (INT_MULTIPLY_WRAPV (cmap->num_var_selector_records, + sizeof *table_offsets, &size) + || INT_MULTIPLY_WRAPV (size, 2, &size)) + return NULL; + + context = NULL; + + /* First, record and sort the UVS and nondefault UVS table offsets + in ascending order. */ + + table_offsets = xmalloc (size); + memset (table_offsets, 0, size); + nmemb = cmap->num_var_selector_records * 2; + j = 0; + + for (i = 0; i < cmap->num_var_selector_records; ++i) + { + /* Note that either offset may be 0, meaning there is no such + table. */ + + if (cmap->records[i].default_uvs_offset) + { + if (INT_ADD_WRAPV (cmap->offset, + cmap->records[i].default_uvs_offset, + &table_offsets[j].offset)) + goto bail; + + table_offsets[j++].is_nondefault_table = false; + } + + if (cmap->records[i].nondefault_uvs_offset) + { + if (INT_ADD_WRAPV (cmap->offset, + cmap->records[i].nondefault_uvs_offset, + &table_offsets[j].offset)) + goto bail; + + table_offsets[j++].is_nondefault_table = true; + } + } + + /* Make nmemb the number of offsets actually looked up. */ + nmemb = j; + + qsort (table_offsets, nmemb, sizeof *table_offsets, + sfnt_compare_table_offsets); + + /* Now go through table_offsets, and read everything. nmemb is the + number of elements in table_offsets[i]; it is kept up to date + when duplicate members are removed. */ + offset = -1; + + for (i = 0; i < nmemb; ++i) + { + /* Skip past duplicate tables. */ + + while (table_offsets[i].offset == offset && i < nmemb) + { + nmemb--; + table_offsets[i] = table_offsets[i + 1]; + } + + /* If the last element of the array is a duplicate, break out of + the loop. */ + + if (i == nmemb) + break; + + /* Read the correct type of table depending on + table_offsets[i].is_nondefault_table. Then skip past + duplicate tables. Don't handle the case where two different + kind of tables share the same offset, because that is not + possible in a valid variation selector record. */ + + offset = table_offsets[i].offset; + + if (table_offsets[i].is_nondefault_table) + table_offsets[i].table + = sfnt_read_nondefault_uvs_table (fd, offset); + else + table_offsets[i].table + = sfnt_read_default_uvs_table (fd, offset); + } + + /* Now make the context. */ + context = xmalloc (sizeof *context); + context->num_records = cmap->num_var_selector_records; + context->nmemb = nmemb; + context->records = xmalloc (sizeof *context->records + * cmap->num_var_selector_records); + + for (i = 0; i < cmap->num_var_selector_records; ++i) + { + context->records[i].selector = cmap->records[i].var_selector; + + /* Either offset may be 0, meaning no such table exists. Also, + the code below will lose if more than one kind of table + shares the same offset, because that is impossible. */ + + if (cmap->records[i].default_uvs_offset) + { + /* Resolve the default table. */ + template.offset = (cmap->records[i].default_uvs_offset + + cmap->offset); + rec = bsearch (&template, table_offsets, + nmemb, sizeof *table_offsets, + sfnt_compare_table_offsets); + + /* Make sure this record is the right type. */ + if (!rec || rec->is_nondefault_table || !rec->table) + goto bail; + + context->records[i].default_uvs = rec->table; + } + else + context->records[i].default_uvs = NULL; + + if (cmap->records[i].nondefault_uvs_offset) + { + /* Resolve the nondefault table. */ + template.offset = (cmap->records[i].nondefault_uvs_offset + + cmap->offset); + rec = bsearch (&template, table_offsets, + nmemb, sizeof *table_offsets, + sfnt_compare_table_offsets); + + if (!rec) + goto bail; + + /* Make sure this record is the right type. */ + if (!rec || !rec->is_nondefault_table || !rec->table) + goto bail; + + context->records[i].nondefault_uvs = rec->table; + } + else + context->records[i].nondefault_uvs = NULL; + } + + context->tables = table_offsets; + return context; + + bail: + + if (context) + { + xfree (context->records); + xfree (context); + } + + /* Loop through and free any tables that might have been read + already. */ + + for (i = 0; i < nmemb; ++i) + xfree (table_offsets[i].table); + + xfree (table_offsets); + return NULL; +} + +/* Free the specified variation selection context C. */ + +TEST_STATIC void +sfnt_free_uvs_context (struct sfnt_uvs_context *c) +{ + size_t i; + + xfree (c->records); + + for (i = 0; i < c->nmemb; ++i) + xfree (c->tables[i].table); + + xfree (c->tables); + xfree (c); +} + +/* Compare *(sfnt_char *) K to ((struct sfnt_uvs_mapping *) + V)->unicode_value appropriately for bsearch. */ + +static int +sfnt_compare_uvs_mapping (const void *k, const void *v) +{ + const sfnt_char *key; + const struct sfnt_uvs_mapping *value; + + key = k; + value = v; + + if (*key < value->unicode_value) + return -1; + else if (*key == value->unicode_value) + return 0; + + return 1; +} + +/* Return the ID of a variation glyph for the character C in the + nondefault UVS mapping table UVS. + + Value is the glyph ID upon success, or 0 if there is no variation + glyph for the base character C. */ + +TEST_STATIC sfnt_glyph +sfnt_variation_glyph_for_char (struct sfnt_nondefault_uvs_table *uvs, + sfnt_char c) +{ + struct sfnt_uvs_mapping *mapping; + + mapping = bsearch (&c, uvs->mappings, uvs->num_uvs_mappings, + sizeof *uvs->mappings, + sfnt_compare_uvs_mapping); + + return mapping ? mapping->base_character_value : 0; +} + + + +#if defined HAVE_MMAP && !defined TEST + +/* Memory mapping support. + It useful to map OpenType layout tables prior to using them in + an external shaping engine such as HarfBuzz. */ + +/* Map a table identified by TAG into the structure *TABLE. + TAG is swapped into host byte order. + + Use the table directory SUBTABLE, which corresponds to the font + file FD. + + Return 0 upon success, and set TABLE->data to the table data, + TABLE->mapping to the start of the mapped area, TABLE->length to + the length of the table contents, and TABLE->size to the size of + the mapping. + + Return 1 upon failure. */ + +int +sfnt_map_table (int fd, struct sfnt_offset_subtable *subtable, + uint32_t tag, struct sfnt_mapped_table *table) +{ + struct sfnt_table_directory *directory; + size_t offset, page, map_offset; + void *data; + int i; + + /* Find the table in the directory. */ + + for (i = 0; i < subtable->num_tables; ++i) + { + if (subtable->subtables[i].tag == tag) + { + directory = &subtable->subtables[i]; + break; + } + } + + if (i == subtable->num_tables) + return 1; + + /* Now try to map the glyph data. Make sure offset is a multiple of + the page size. */ + + page = getpagesize (); + offset = directory->offset & ~(page - 1); + + /* Figure out how much larger the mapping should be. */ + map_offset = directory->offset - offset; + + /* Do the mmap. */ + data = mmap (NULL, directory->length + map_offset, + PROT_READ, MAP_PRIVATE, fd, offset); + + if (data == MAP_FAILED) + return 1; + + /* Fill in *TABLE. */ + table->data = (unsigned char *) data + map_offset; + table->mapping = data; + table->length = directory->length; + table->size = directory->length + map_offset; + return 0; +} + +/* Unmap the table inside *TABLE. + Value is 0 upon success, 1 otherwise. */ + +int +sfnt_unmap_table (struct sfnt_mapped_table *table) +{ + return munmap (table->mapping, table->size) != 0; +} + +#endif /* HAVE_MMAP && !TEST */ + + + +#ifndef TEST + +/* Reading table contents. */ + +/* Read the table with the specified TAG from the font file FD. + Return its length in *LENGTH, and its data upon success, else + NULL. */ + +void * +sfnt_read_table (int fd, struct sfnt_offset_subtable *subtable, + uint32_t tag, size_t *length) +{ + struct sfnt_table_directory *directory; + void *data; + int i; + + /* Find the table in the directory. */ + + for (i = 0; i < subtable->num_tables; ++i) + { + if (subtable->subtables[i].tag == tag) + { + directory = &subtable->subtables[i]; + break; + } + } + + if (i == subtable->num_tables) + return NULL; + + /* Seek to the table. */ + + if (lseek (fd, directory->offset, SEEK_SET) != directory->offset) + return NULL; + + /* Now allocate enough to hold the data and read into it. */ + + data = xmalloc (directory->length); + if (read (fd, data, directory->length) != directory->length) + { + xfree (data); + return NULL; + } + + /* Return the length and table data. */ + *length = directory->length; + return data; +} + +#endif /* !TEST */ + + + #ifdef TEST struct sfnt_test_dcontext @@ -15494,6 +16152,52 @@ sfnt_pop_hook (struct sfnt_interpreter *interpreter, +static void +sfnt_test_uvs (int fd, struct sfnt_cmap_format_14 *format14) +{ + struct sfnt_uvs_context *context; + size_t i, j; + sfnt_glyph glyph; + sfnt_char c; + struct sfnt_nondefault_uvs_table *uvs; + + context = sfnt_create_uvs_context (format14, fd); + + /* Print each variation selector and its associated ranges. */ + + if (!context) + fprintf (stderr, "failed to read uvs data\n"); + else + { + fprintf (stderr, "UVS context with %zu records and %zu tables\n", + context->num_records, context->nmemb); + + for (i = 0; i < context->num_records; ++i) + { + if (!context->records[i].nondefault_uvs) + continue; + + uvs = context->records[i].nondefault_uvs; + + for (j = 0; j < uvs->num_uvs_mappings; ++j) + { + c = uvs->mappings[j].unicode_value; + glyph = sfnt_variation_glyph_for_char (uvs, c); + + if (glyph != uvs->mappings[j].base_character_value) + abort (); + + fprintf (stderr, " UVS: %"PRIx32" (%"PRIx32") -> %"PRIu32"\n", + c, context->records[i].selector, glyph); + } + } + + sfnt_free_uvs_context (context); + } +} + + + /* Main entry point. */ /* Simple tests that were used while developing this file. By the @@ -15564,7 +16268,7 @@ main (int argc, char **argv) struct sfnt_raster **rasters; size_t length; - if (argc != 2) + if (argc < 2) return 1; if (!strcmp (argv[1], "--check-interpreter")) @@ -15654,8 +16358,25 @@ main (int argc, char **argv) data[i]->format); } -#define FANCY_PPEM 12 -#define EASY_PPEM 12 + if (argc >= 3 && !strcmp (argv[2], "--check-variation-selectors")) + { + /* Look for a format 14 cmap table. */ + + for (i = 0; i < table->num_subtables; ++i) + { + if (data[i]->format == 14) + { + fprintf (stderr, "format 14 subtable found\n"); + sfnt_test_uvs (fd, (struct sfnt_cmap_format_14 *) data[i]); + return 0; + } + } + + return 1; + } + +#define FANCY_PPEM 25 +#define EASY_PPEM 25 interpreter = NULL; head = sfnt_read_head_table (fd, font); diff --git a/src/sfnt.h b/src/sfnt.h index 4bf46b62397..83d34bfb757 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -25,6 +25,8 @@ #define _SFNT_H_ #include #include +#include + #if defined emacs || defined TEST #define SFNT_ENABLE_HINTING #endif @@ -422,7 +424,7 @@ #define sfnt_coerce_fixed(fixed) ((sfnt_fixed) (fixed) / 65535.0) struct sfnt_cmap_format_8_or_12_group *groups; }; -/* cmap formats 10, 13 and 14 unsupported. */ +/* cmap formats 10, 13 unsupported. */ struct sfnt_cmap_format_12 { @@ -445,6 +447,36 @@ #define sfnt_coerce_fixed(fixed) ((sfnt_fixed) (fixed) / 65535.0) struct sfnt_cmap_format_8_or_12_group *groups; }; +struct sfnt_cmap_format_14 +{ + /* Format, set to 14. */ + uint16_t format; + + /* The length of the table in bytes. */ + uint32_t length; + + /* Number of variation selector records. */ + uint16_t num_var_selector_records; + + /* The offset of this table in the font file. */ + off_t offset; + + /* Variable length data. */ + struct sfnt_variation_selector_record *records; +}; + +struct sfnt_variation_selector_record +{ + /* 24-bit unsigned variation selector. */ + unsigned int var_selector; + + /* Offset to default UVS table. */ + uint32_t default_uvs_offset; + + /* Offset to non-default UVS table. */ + uint32_t nondefault_uvs_offset; +}; + struct sfnt_maxp_table { /* Table version. */ @@ -1437,6 +1469,106 @@ #define PROTOTYPE int, struct sfnt_offset_subtable * +/* Unicode Variation Sequence (UVS) support. */ + +struct sfnt_default_uvs_table +{ + /* Number of ranges that follow. */ + uint32_t num_unicode_value_ranges; + + /* Variable length data. */ + struct sfnt_unicode_value_range *ranges; +}; + +struct sfnt_unicode_value_range +{ + /* First value in this range. */ + unsigned int start_unicode_value; + + /* Number of additional values in this range. */ + unsigned char additional_count; +}; + +struct sfnt_nondefault_uvs_table +{ + /* Number of UVS mappings which follow. */ + uint32_t num_uvs_mappings; + + /* Variable length data. */ + struct sfnt_uvs_mapping *mappings; +}; + +struct sfnt_uvs_mapping +{ + /* Base character value. */ + unsigned int unicode_value; + + /* Glyph ID of the base character value. */ + uint16_t base_character_value; +}; + +struct sfnt_mapped_variation_selector_record +{ + /* The variation selector. */ + unsigned int selector; + + /* Its default UVS table. */ + struct sfnt_default_uvs_table *default_uvs; + + /* Its nondefault UVS table. */ + struct sfnt_nondefault_uvs_table *nondefault_uvs; +}; + +/* Structure describing a single offset to load into a variation + selection context. */ + +struct sfnt_table_offset_rec +{ + /* The offset from the start of the font file. */ + off_t offset; + + /* Whether or not the offset points to a non-default UVS table. */ + bool is_nondefault_table; + + /* Pointer to the UVS table. */ + void *table; +}; + +struct sfnt_uvs_context +{ + /* Number of records and tables. */ + size_t num_records, nmemb; + + /* Array of UVS tables. */ + struct sfnt_table_offset_rec *tables; + + /* Array of variation selector records mapped to + their corresponding tables. */ + struct sfnt_mapped_variation_selector_record *records; +}; + + + +#if defined HAVE_MMAP && !defined TEST + +/* Memory mapping support. */ + +struct sfnt_mapped_table +{ + /* Pointer to table data. */ + void *data; + + /* Pointer to table mapping. */ + void *mapping; + + /* Size of mapped data and size of mapping. */ + size_t length, size; +}; + +#endif /* HAVE_MMAP && !TEST */ + + + /* Functions used to read tables used by the TrueType interpreter. */ #ifndef TEST @@ -1509,6 +1641,37 @@ #define PROTOTYPE \ #undef PROTOTYPE + + +#define PROTOTYPE struct sfnt_cmap_format_14 *, int + +extern struct sfnt_uvs_context *sfnt_create_uvs_context (PROTOTYPE); + +#undef PROTOTYPE + +extern void sfnt_free_uvs_context (struct sfnt_uvs_context *); + +#define PROTOTYPE struct sfnt_nondefault_uvs_table *, sfnt_char + +extern sfnt_glyph sfnt_variation_glyph_for_char (PROTOTYPE); + +#undef PROTOTYPE + + + +#ifdef HAVE_MMAP + +extern int sfnt_map_table (int, struct sfnt_offset_subtable *, + uint32_t, struct sfnt_mapped_table *); +extern int sfnt_unmap_table (struct sfnt_mapped_table *); + +#endif /* HAVE_MMAP */ + + + +extern void *sfnt_read_table (int, struct sfnt_offset_subtable *, + uint32_t, size_t *); + #endif /* TEST */ diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c index 8324185cc6f..37f43465097 100644 --- a/src/sfntfont-android.c +++ b/src/sfntfont-android.c @@ -657,8 +657,16 @@ sfntfont_android_get_cache (struct frame *f) .encode_char = sfntfont_encode_char, .text_extents = sfntfont_text_extents, .list_family = sfntfont_list_family, - - /* TODO: list_family, shaping. */ + .get_variation_glyphs = sfntfont_get_variation_glyphs, + +#ifdef HAVE_HARFBUZZ + /* HarfBuzz support is enabled transparently on Android without + using a separate font driver. */ + .begin_hb_font = sfntfont_begin_hb_font, + .combining_capability = hbfont_combining_capability, + .shape = hbfont_shape, + .otf_capability = hbfont_otf_capability, +#endif /* HAVE_HARFBUZZ */ }; diff --git a/src/sfntfont.c b/src/sfntfont.c index b8ffce27062..500256d6fb4 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -34,6 +34,11 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include "sfnt.h" #include "sfntfont.h" +#ifdef HAVE_HARFBUZZ +#include +#include +#endif /* HAVE_HARFBUZZ */ + /* For FRAME_FONT. */ #include TERM_HEADER @@ -1038,15 +1043,20 @@ sfntfont_charset_for_cmap (struct sfnt_cmap_encoding_subtable subtable) /* Pick the best character map in the cmap table CMAP. Use the subtables in SUBTABLES and DATA. Return the subtable data and the - subtable in *SUBTABLE upon success, NULL otherwise. */ + subtable in *SUBTABLE upon success, NULL otherwise. + + If FORMAT14 is non-NULL, return any associated format 14 variation + selection context in *FORMAT14 should the selected charcter map be + a Unicode character map. */ static struct sfnt_cmap_encoding_subtable_data * sfntfont_select_cmap (struct sfnt_cmap_table *cmap, struct sfnt_cmap_encoding_subtable *subtables, struct sfnt_cmap_encoding_subtable_data **data, - struct sfnt_cmap_encoding_subtable *subtable) + struct sfnt_cmap_encoding_subtable *subtable, + struct sfnt_cmap_format_14 **format14) { - int i; + int i, j; /* First look for a non-BMP Unicode cmap. */ @@ -1055,6 +1065,24 @@ sfntfont_select_cmap (struct sfnt_cmap_table *cmap, if (data[i] && sfntfont_identify_cmap (subtables[i]) == 2) { *subtable = subtables[i]; + + if (!format14) + return data[i]; + + /* Search for a correspoinding format 14 character map. + This is used in conjunction with the selected character + map to map variation sequences. */ + + for (j = 0; j < cmap->num_subtables; ++j) + { + if (data[j] + && subtables[j].platform_id == SFNT_PLATFORM_UNICODE + && (subtables[j].platform_specific_id + == SFNT_UNICODE_VARIATION_SEQUENCES) + && data[j]->format == 14) + *format14 = (struct sfnt_cmap_format_14 *) data[j]; + } + return data[i]; } } @@ -1066,6 +1094,24 @@ sfntfont_select_cmap (struct sfnt_cmap_table *cmap, if (data[i] && sfntfont_identify_cmap (subtables[i]) == 1) { *subtable = subtables[i]; + + if (!format14) + return data[i]; + + /* Search for a correspoinding format 14 character map. + This is used in conjunction with the selected character + map to map variation sequences. */ + + for (j = 0; j < cmap->num_subtables; ++j) + { + if (data[j] + && subtables[j].platform_id == SFNT_PLATFORM_UNICODE + && (subtables[j].platform_specific_id + == SFNT_UNICODE_VARIATION_SEQUENCES) + && data[j]->format == 14) + *format14 = (struct sfnt_cmap_format_14 *) data[j]; + } + return data[i]; } } @@ -1128,7 +1174,7 @@ sfntfont_read_cmap (struct sfnt_font_desc *desc, /* Now pick the best character map. */ *cmap = sfntfont_select_cmap (table, subtables, data, - subtable); + subtable, NULL); /* Free the cmap data. */ @@ -1960,6 +2006,9 @@ sfntfont_free_raster_cache (struct sfnt_raster_cache *cache) /* Data identifying that character map. */ struct sfnt_cmap_encoding_subtable cmap_subtable; + /* The UVS context. */ + struct sfnt_uvs_context *uvs; + /* Outline cache. */ struct sfnt_outline_cache outline_cache; @@ -1983,6 +2032,17 @@ sfntfont_free_raster_cache (struct sfnt_raster_cache *cache) /* Whether or not the glyph table has been mmapped. */ bool glyf_table_mapped; #endif /* HAVE_MMAP */ + +#ifdef HAVE_HARFBUZZ + /* HarfBuzz font object. */ + hb_font_t *hb_font; + + /* File descriptor associated with this font. */ + int fd; + + /* The table directory of the font file. */ + struct sfnt_offset_subtable *directory; +#endif /* HAVE_HARFBUZZ */ }; #ifdef HAVE_MMAP @@ -2198,6 +2258,7 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, struct charset *charset; int point_size; Display_Info *dpyinfo; + struct sfnt_cmap_format_14 *format14; if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0) pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)); @@ -2240,6 +2301,7 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, font_info->prep = NULL; font_info->fpgm = NULL; font_info->cvt = NULL; + font_info->uvs = NULL; font_info->outline_cache.next = &font_info->outline_cache; font_info->outline_cache.last = &font_info->outline_cache; @@ -2251,6 +2313,11 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, #ifdef HAVE_MMAP font_info->glyf_table_mapped = false; #endif /* HAVE_MMAP */ +#ifdef HAVE_HARFBUZZ + font_info->hb_font = NULL; + font_info->fd = -1; + font_info->directory = NULL; +#endif /* HAVE_HARFBUZZ */ /* Open the font. */ fd = emacs_open (desc->path, O_RDONLY, 0); @@ -2280,14 +2347,29 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, if (!font_info->cmap) goto bail2; + format14 = NULL; font_info->cmap_data = sfntfont_select_cmap (font_info->cmap, subtables, data, - &font_info->cmap_subtable); + &font_info->cmap_subtable, + &format14); + + if (format14) + { + /* Build a UVS context from this format 14 mapping table. A UVS + context contains each variation selector supported by the + font, and a list of ``non-default'' mappings between base + characters and variation glyph IDs. */ + + font_info->uvs = sfnt_create_uvs_context (format14, fd); + xfree (format14); + } for (i = 0; i < font_info->cmap->num_subtables; ++i) { - if (data[i] != font_info->cmap_data) + if (data[i] != font_info->cmap_data + /* format14 has already been freed. */ + && data[i] != (struct sfnt_cmap_encoding_subtable_data *) format14) xfree (data[i]); } @@ -2432,11 +2514,19 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, sfntfont_setup_interpreter (fd, font_info, subtable, point_size); +#ifndef HAVE_HARFBUZZ /* Close the font file descriptor. */ emacs_close (fd); /* Free the offset subtable. */ xfree (subtable); +#else /* HAVE_HARFBUZZ */ + /* HarfBuzz will potentially read font tables after the font has + been opened by Emacs. Keep the font open, and record its offset + subtable. */ + font_info->fd = fd; + font_info->directory = subtable; +#endif /* !HAVE_HARFBUZZ */ #ifdef HAVE_MMAP /* Link the font onto the font table. */ @@ -2483,6 +2573,10 @@ sfntfont_open (struct frame *f, Lisp_Object font_entity, xfree (font_info->cmap_data); font_info->cmap_data = NULL; bail3: + + if (font_info->uvs) + sfnt_free_uvs_context (font_info->uvs); + xfree (font_info->cmap); font_info->cmap = NULL; bail2: @@ -2677,8 +2771,7 @@ sfntfont_close (struct font *font) xfree (info->hmtx); #ifdef HAVE_MMAP - if (info->glyf_table_mapped - && info->glyf) + if (info->glyf_table_mapped && info->glyf) { rc = sfnt_unmap_glyf_table (info->glyf); @@ -2697,6 +2790,12 @@ sfntfont_close (struct font *font) xfree (info->cvt); xfree (info->interpreter); + /* Deallocate any UVS context allocated to look up font variation + sequences. */ + + if (info->uvs) + sfnt_free_uvs_context (info->uvs); + /* Clear these fields. It seems that close can be called twice, once during font driver destruction, and once during GC. */ @@ -2713,6 +2812,7 @@ sfntfont_close (struct font *font) info->fpgm = NULL; info->cvt = NULL; info->interpreter = NULL; + info->uvs = NULL; #ifdef HAVE_MMAP @@ -2728,6 +2828,28 @@ sfntfont_close (struct font *font) #endif /* HAVE_MMAP */ +#ifdef HAVE_HARFBUZZ + /* Close the font file. */ + + if (info->fd != -1) + { + emacs_close (info->fd); + info->fd = -1; + } + + /* Free its table directory. */ + xfree (info->directory); + info->directory = NULL; + + /* Free any hb_font created. */ + + if (info->hb_font) + { + hb_font_destroy (info->hb_font); + info->hb_font = NULL; + } +#endif + sfntfont_free_outline_cache (&info->outline_cache); sfntfont_free_raster_cache (&info->raster_cache); } @@ -2821,7 +2943,11 @@ sfntfont_draw (struct glyph_string *s, int from, int to, /* Now work out where to put the outline. */ x_coords[i - from] = current_x; - current_x += SFNT_CEIL_FIXED (metrics.advance) >> 16; + + if (s->padding_p) + current_x += 1; + else + current_x += SFNT_CEIL_FIXED (metrics.advance) >> 16; } /* Call the window system function to put the glyphs to the @@ -2865,6 +2991,126 @@ sfntfont_list_family (struct frame *f) +/* Unicode Variation Selector (UVS) support. This is typically + required for Harfbuzz. */ + +/* Given a FONT object, a character C, and VARIATIONS, return the + number of non-default variation glyphs, and their glyph ids in + VARIATIONS. + + For each variation selector character K with a non-default glyph in + the variation selector range 0xFE00 to 0xFE0F, set variations[K - + 0xFE0] to its ID. + + For each variation selector character K with a non-default glyph in + the variation selector range 0xE0100 to 0xE01EF, set variations[K - + 0xE0100 + 16] to its ID. + + If value is more than 0, set all other members of VARIATIONS to 0. + Else, the contents of VARIATIONS are undefined. */ + +int +sfntfont_get_variation_glyphs (struct font *font, int c, + unsigned variations[256]) +{ + struct sfnt_font_info *info; + size_t i; + int n; + struct sfnt_mapped_variation_selector_record *record; + + info = (struct sfnt_font_info *) font; + n = 0; + + /* Return 0 if there is no UVS mapping table. */ + + if (!info->uvs) + return 0; + + /* Clear the variations array. */ + + memset (variations, 0, sizeof *variations * 256); + + /* Find the first 0xFExx selector. */ + + i = 0; + while (i < info->uvs->num_records + && info->uvs->records[i].selector < 0xfe00) + ++i; + + /* Fill in selectors 0 to 15. */ + + while (i < info->uvs->num_records + && info->uvs->records[i].selector <= 0xfe0f) + { + record = &info->uvs->records[i]; + + /* If record has no non-default mappings, continue on to the + next selector. */ + + if (!record->nondefault_uvs) + goto next_selector; + + /* Handle invalid unsorted tables. */ + + if (record->selector < 0xfe00) + return 0; + + /* Find the glyph ID associated with C and put it in + VARIATIONS. */ + + variations[info->uvs->records[i].selector - 0xfe00] + = sfnt_variation_glyph_for_char (record->nondefault_uvs, c); + + if (variations[info->uvs->records[i].selector - 0xfe00]) + ++n; + + next_selector: + ++i; + } + + /* Find the first 0xE0100 selector. */ + + i = 0; + while (i < info->uvs->num_records + && info->uvs->records[i].selector < 0xe0100) + ++i; + + /* Fill in selectors 16 to 255. */ + + while (i < info->uvs->num_records + && info->uvs->records[i].selector <= 0xe01ef) + { + record = &info->uvs->records[i]; + + /* If record has no non-default mappings, continue on to the + next selector. */ + + if (!record->nondefault_uvs) + goto next_selector_1; + + /* Handle invalid unsorted tables. */ + + if (record->selector < 0xe0100) + return 0; + + /* Find the glyph ID associated with C and put it in + VARIATIONS. */ + + variations[info->uvs->records[i].selector - 0xe0100 + 16] + = sfnt_variation_glyph_for_char (record->nondefault_uvs, c); + + if (variations[info->uvs->records[i].selector - 0xe0100 + 16]) + ++n; + + next_selector_1: + ++i; + } + + return n; +} + + + /* mmap specific stuff. */ #ifdef HAVE_MMAP @@ -2893,6 +3139,126 @@ sfntfont_detect_sigbus (void *addr) +/* Harfbuzz font support. */ + +#ifdef HAVE_HARFBUZZ + +#ifdef HAVE_MMAP + +/* Unmap the specified table. */ + +static void +sfntfont_unmap_blob (void *ptr) +{ + if (sfnt_unmap_table (ptr)) + emacs_abort (); + + xfree (ptr); +} + +#endif /* HAVE_MMAP */ + +/* Given a font DATA and a tag TAG, return the data of the + corresponding font table as a HarfBuzz blob. */ + +static hb_blob_t * +sfntfont_get_font_table (hb_face_t *face, hb_tag_t tag, void *data) +{ + size_t size; + struct sfnt_font_info *info; +#ifdef HAVE_MMAP + struct sfnt_mapped_table *table; + hb_blob_t *blob; + + info = data; + table = xmalloc (sizeof *table); + + if (!sfnt_map_table (info->fd, info->directory, tag, + table)) + { + /* Create an hb_blob_t and return it. + TODO: record this mapping properly so that SIGBUS can + be handled. */ + + blob = hb_blob_create (table->data, table->length, + HB_MEMORY_MODE_READONLY, + table, sfntfont_unmap_blob); + + /* Note that sfntfont_unmap_blob will be called if the empty + blob is returned. */ + return blob; + } + + xfree (table); +#else /* !HAVE_MMAP */ + + /* Try to read the table conventionally. */ + info = data; +#endif /* HAVE_MMAP */ + + data = sfnt_read_table (info->fd, info->directory, tag, + &size); + + if (!data) + return NULL; + + return hb_blob_create (data, size, HB_MEMORY_MODE_WRITABLE, + data, xfree); +} + +/* Create or return a HarfBuzz font object corresponding to the + specified FONT. Return the scale to convert between fwords and + pixels in POSITION_UNIT. */ + +hb_font_t * +sfntfont_begin_hb_font (struct font *font, double *position_unit) +{ + struct sfnt_font_info *info; + hb_face_t *face; + int factor; + + info = (struct sfnt_font_info *) font; + + if (info->hb_font) + { + /* Calculate the scale factor. */ + *position_unit = 1.0 / 64.0; + return info->hb_font; + } + + /* Create a face and then a font. */ + face = hb_face_create_for_tables (sfntfont_get_font_table, font, + NULL); + + if (hb_face_get_glyph_count (face) > 0) + { + info->hb_font = hb_font_create (face); + if (!info->hb_font) + goto bail; + + factor = font->pixel_size; + + /* Set the scale and PPEM values. */ + hb_font_set_scale (info->hb_font, factor * 64, factor * 64); + hb_font_set_ppem (info->hb_font, factor, factor); + + /* This is needed for HarfBuzz before 2.0.0; it is the default + in later versions. */ + hb_ot_font_set_funcs (info->hb_font); + } + + bail: + hb_face_destroy (face); + + /* Calculate the scale factor. */ + *position_unit = 1.0 / 64.0; + return info->hb_font; +} + +#endif /* HAVE_HARFBUZZ */ + + + void syms_of_sfntfont (void) { diff --git a/src/sfntfont.h b/src/sfntfont.h index dc37883b4a9..df387512d0d 100644 --- a/src/sfntfont.h +++ b/src/sfntfont.h @@ -42,6 +42,7 @@ #define _SFNTFONT_H_ extern int sfntfont_draw (struct glyph_string *, int, int, int, int, bool); extern Lisp_Object sfntfont_list_family (struct frame *); +extern int sfntfont_get_variation_glyphs (struct font *, int, unsigned[256]); /* Initialization functions. */ @@ -65,4 +66,14 @@ #define _SFNTFONT_H_ #endif /* HAVE_MMAP */ + + +/* HarfBuzz specific functions. */ + +#ifdef HAVE_HARFBUZZ + +extern hb_font_t *sfntfont_begin_hb_font (struct font *, double *); + +#endif /* HAVE_HARFBUZZ */ + #endif /* _SFNTFONT_H_ */ diff --git a/src/textconv.c b/src/textconv.c index a4e3116fb68..4fa92f43ecd 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -1723,12 +1723,12 @@ DEFUN ("set-text-conversion-style", Fset_text_conversion_style, Sset_text_conversion_style, 1, 1, 0, doc: /* Set the text conversion style in the current buffer. -Set `text-conversion-mode' to VALUE, then force any input method +Set `text-conversion-style' to VALUE, then force any input method editing frame displaying this buffer to stop itself. This can lead to a significant amount of time being taken by the input method resetting itself, so you should not use this function lightly; -instead, set `text-conversion-mode' before your buffer is displayed, +instead, set `text-conversion-style' before your buffer is displayed, and let redisplay manage the input method appropriately. */) (Lisp_Object value) { commit 739558c36958d9ec9f221ce168e15467b4579111 Merge: f3dd887d184 4234e204ec0 Author: Po Lu Date: Sun Mar 19 09:07:29 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit f3dd887d1848759baf0d91264882509159bb04fc Author: Po Lu Date: Sat Mar 18 20:05:38 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsView.java (onAttachedToWindow): Send measured width and height in exposures again. diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 88d17a255a2..10c1af9e19a 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -574,7 +574,8 @@ else if (child.getVisibility () != GONE) bitmapDirty = true; /* Now expose the view contents again. */ - EmacsNative.sendExpose (this.window.handle, 0, 0, 0, 0); + EmacsNative.sendExpose (this.window.handle, 0, 0, + measuredWidth, measuredHeight); super.onAttachedToWindow (); } commit 84ffda4be5670696259622d03ff7e35799554888 Merge: 90348dabbf1 95d5154feed Author: Po Lu Date: Sat Mar 18 15:31:31 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 90348dabbf165ad221bf6421549236d7e9882503 Author: Po Lu Date: Sat Mar 18 13:07:59 2023 +0800 Remove extraneous debugging code * src/androidterm.c (handle_one_android_event): Don't log expose events. diff --git a/src/androidterm.c b/src/androidterm.c index 825d6da60fa..1bc11e600f7 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -1127,11 +1127,6 @@ handle_one_android_event (struct android_display_info *dpyinfo, if (!FRAME_GARBAGED_P (f)) { - __android_log_print (ANDROID_LOG_VERBOSE, __func__, - "expose: %d %d %d %d\n", - event->xexpose.x, event->xexpose.y, - event->xexpose.width, - event->xexpose.height); expose_frame (f, event->xexpose.x, event->xexpose.y, event->xexpose.width, event->xexpose.height); show_back_buffer (f); commit 6d28b596a9f2da8ed02d71b6815ffc1bda61c11e Author: Po Lu Date: Sat Mar 18 11:59:17 2023 +0800 Work around pselect lossage on Android * src/android.c (android_run_select_thread): New flag. Use it rather than the rc of pselect and errno to determine whether or not it has been interrupted. (android_handle_sigusr1): Set said flag. diff --git a/src/android.c b/src/android.c index efbf8fc95cc..1da8bec316e 100644 --- a/src/android.c +++ b/src/android.c @@ -304,9 +304,6 @@ #define ANDROID_MAX_ASSET_FD 65535 /* Value of pselect. */ static int android_pselect_rc; -/* Whether or not pselect finished. */ -static volatile bool android_pselect_completed; - /* The global event queue. */ static struct android_event_queue event_queue; @@ -318,6 +315,11 @@ #define ANDROID_MAX_ASSET_FD 65535 /* Select self-pipe. */ static int select_pipe[2]; +#else + +/* Whether or not pselect has been interrupted. */ +static volatile sig_atomic_t android_pselect_interrupted; + #endif static void * @@ -388,7 +390,6 @@ android_run_select_thread (void *data) condition variable. */ pthread_mutex_lock (&event_queue.mutex); - android_pselect_completed = true; pthread_cond_broadcast (&event_queue.read_var); pthread_mutex_unlock (&event_queue.mutex); @@ -417,6 +418,10 @@ android_run_select_thread (void *data) while (sem_wait (&android_pselect_start_sem) < 0) ;; + /* Clear the ``pselect interrupted'' flag. This is safe because + right now, SIGUSR1 is blocked. */ + android_pselect_interrupted = 0; + /* Get the select lock and call pselect. */ pthread_mutex_lock (&event_queue.select_mutex); rc = pselect (android_pselect_nfds, @@ -434,11 +439,16 @@ android_run_select_thread (void *data) condition variable. */ pthread_mutex_lock (&event_queue.mutex); - android_pselect_completed = true; pthread_cond_broadcast (&event_queue.read_var); pthread_mutex_unlock (&event_queue.mutex); - if (rc != -1 || errno != EINTR) + /* Check `android_pselect_interrupted' instead of rc and errno. + + This is because `pselect' does not return an rc of -1 upon + being interrupted in some versions of Android, but does set + signal masks correctly. */ + + if (!android_pselect_interrupted) /* Now, wait for SIGUSR1, unless pselect was interrupted and the signal was already delivered. The Emacs thread will always send this signal after read_var is triggered or the @@ -460,8 +470,8 @@ android_run_select_thread (void *data) static void android_handle_sigusr1 (int sig, siginfo_t *siginfo, void *arg) { - /* Nothing to do here, this signal handler is only installed to make - sure the disposition of SIGUSR1 is enough. */ + /* Notice that pselect has been interrupted. */ + android_pselect_interrupted = 1; } #endif @@ -686,7 +696,6 @@ android_select (int nfds, fd_set *readfds, fd_set *writefds, } nfds_return = 0; - android_pselect_completed = false; pthread_mutex_lock (&event_queue.select_mutex); android_pselect_nfds = nfds; commit 634e3fcc20ea9fa5b1af59286f4b846a351f52c8 Author: Po Lu Date: Sat Mar 18 10:54:26 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsView.java (EmacsView) (prepareForLayout): New function. Call this prior to mapping the view. (onGlobalLayout): New function. Register as global layout listener. * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow) (notifyContentRectPosition): New function. Use specified xPosition and yPosition when reporting the offsets of children of the root window. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (registerWindow): Specify activity launch bounds if necessary. * src/androidterm.c (handle_one_android_event): Send MOVE_FRAME_EVENT where necessary. diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 681da98fa16..88d17a255a2 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -28,6 +28,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; @@ -51,6 +52,7 @@ It is also a ViewGroup, as it also lays out children. */ public final class EmacsView extends ViewGroup + implements ViewTreeObserver.OnGlobalLayoutListener { public static final String TAG = "EmacsView"; @@ -136,6 +138,9 @@ public final class EmacsView extends ViewGroup context = getContext (); tem = context.getSystemService (Context.INPUT_METHOD_SERVICE); imManager = (InputMethodManager) tem; + + /* Add this view as its own global layout listener. */ + getViewTreeObserver ().addOnGlobalLayoutListener (this); } private void @@ -238,6 +243,13 @@ public final class EmacsView extends ViewGroup return canvas; } + public void + prepareForLayout (int wantedWidth, int wantedHeight) + { + measuredWidth = wantedWidth; + measuredHeight = wantedWidth; + } + @Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) @@ -562,8 +574,7 @@ else if (child.getVisibility () != GONE) bitmapDirty = true; /* Now expose the view contents again. */ - EmacsNative.sendExpose (this.window.handle, 0, 0, - measuredWidth, measuredHeight); + EmacsNative.sendExpose (this.window.handle, 0, 0, 0, 0); super.onAttachedToWindow (); } @@ -678,4 +689,19 @@ else if (child.getVisibility () != GONE) { return icMode; } + + @Override + public void + onGlobalLayout () + { + int[] locations; + + /* Get the absolute offset of this view and specify its left and + top position in subsequent ConfigureNotify events. */ + + locations = new int[2]; + getLocationInWindow (locations); + window.notifyContentRectPosition (locations[0], + locations[1]); + } }; diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index a8d1beedef7..c14bf16b96e 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -133,6 +133,9 @@ private static class Coordinate creating new bitmaps. */ public volatile int background; + /* The position of this window relative to the root window. */ + public int xPosition, yPosition; + public EmacsWindow (short handle, final EmacsWindow parent, int x, int y, int width, int height, boolean overrideRedirect) @@ -192,18 +195,14 @@ private static class Coordinate background = pixel; } - public Rect + public synchronized Rect getGeometry () { - synchronized (this) - { - /* Huh, this is it. */ - return rect; - } + return new Rect (rect); } @Override - public void + public synchronized void destroyHandle () throws IllegalStateException { if (parent != null) @@ -258,22 +257,28 @@ private static class Coordinate return attached; } - public long + public synchronized long viewLayout (int left, int top, int right, int bottom) { int rectWidth, rectHeight; - synchronized (this) - { - rect.left = left; - rect.top = top; - rect.right = right; - rect.bottom = bottom; - } + rect.left = left; + rect.top = top; + rect.right = right; + rect.bottom = bottom; rectWidth = right - left; rectHeight = bottom - top; + /* If parent is null, use xPosition and yPosition instead of the + geometry rectangle positions. */ + + if (parent == null) + { + left = xPosition; + top = yPosition; + } + return EmacsNative.sendConfigureNotify (this.handle, System.currentTimeMillis (), left, top, rectWidth, @@ -283,8 +288,6 @@ private static class Coordinate public void requestViewLayout () { - /* This is necessary because otherwise subsequent drawing on the - Emacs thread may be lost. */ view.explicitlyDirtyBitmap (); EmacsService.SERVICE.runOnUiThread (new Runnable () { @@ -302,35 +305,29 @@ private static class Coordinate }); } - public void + public synchronized void resizeWindow (int width, int height) { - synchronized (this) - { - rect.right = rect.left + width; - rect.bottom = rect.top + height; + rect.right = rect.left + width; + rect.bottom = rect.top + height; - requestViewLayout (); - } + requestViewLayout (); } - public void + public synchronized void moveWindow (int x, int y) { int width, height; - synchronized (this) - { - width = rect.width (); - height = rect.height (); + width = rect.width (); + height = rect.height (); - rect.left = x; - rect.top = y; - rect.right = x + width; - rect.bottom = y + height; + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; - requestViewLayout (); - } + requestViewLayout (); } private WindowManager.LayoutParams @@ -366,13 +363,17 @@ private static class Coordinate return EmacsService.SERVICE; } - public void + public synchronized void mapWindow () { + final int width, height; + if (isMapped) return; isMapped = true; + width = rect.width (); + height = rect.height (); if (parent == null) { @@ -424,6 +425,7 @@ private static class Coordinate /* Attach the view. */ try { + view.prepareForLayout (width, height); windowManager.addView (view, params); /* Record the window manager being used in the @@ -448,10 +450,14 @@ private static class Coordinate public void run () { + /* Prior to mapping the view, set its measuredWidth and + measuredHeight to some reasonable value, in order to + avoid excessive bitmap dirtying. */ + + view.prepareForLayout (width, height); view.setVisibility (View.VISIBLE); if (!getDontFocusOnMap ()) - /* Eventually this should check no-focus-on-map. */ view.requestFocus (); } }); @@ -512,12 +518,9 @@ private static class Coordinate public void clearWindow () { - synchronized (this) - { - EmacsService.SERVICE.fillRectangle (this, scratchGC, - 0, 0, rect.width (), - rect.height ()); - } + EmacsService.SERVICE.fillRectangle (this, scratchGC, + 0, 0, rect.width (), + rect.height ()); } public void @@ -1001,7 +1004,7 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) return false; } - public void + public synchronized void reparentTo (final EmacsWindow otherWindow, int x, int y) { int width, height; @@ -1017,15 +1020,12 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) parent = otherWindow; /* Move this window to the new location. */ - synchronized (this) - { - width = rect.width (); - height = rect.height (); - rect.left = x; - rect.top = y; - rect.right = x + width; - rect.bottom = y + height; - } + width = rect.width (); + height = rect.height (); + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; /* Now do the work necessary on the UI thread to reparent the window. */ @@ -1081,7 +1081,7 @@ else if (EmacsWindow.this.isMapped) }); } - public void + public synchronized void raise () { /* This does nothing here. */ @@ -1103,7 +1103,7 @@ else if (EmacsWindow.this.isMapped) }); } - public void + public synchronized void lower () { /* This does nothing here. */ @@ -1125,17 +1125,15 @@ else if (EmacsWindow.this.isMapped) }); } - public int[] + public synchronized int[] getWindowGeometry () { int[] array; - Rect rect; array = new int[4]; - rect = getGeometry (); - array[0] = rect.left; - array[1] = rect.top; + array[0] = parent != null ? rect.left : xPosition; + array[1] = parent != null ? rect.top : yPosition; array[2] = rect.width (); array[3] = rect.height (); @@ -1272,4 +1270,31 @@ else if (EmacsWindow.this.isMapped) } }); } + + public synchronized void + notifyContentRectPosition (int xPosition, int yPosition) + { + Rect geometry; + + /* Ignore these notifications if not a child of the root + window. */ + if (parent != null) + return; + + /* xPosition and yPosition are the position of this window + relative to the screen. Set them and request a ConfigureNotify + event. */ + + if (this.xPosition != xPosition + || this.yPosition != yPosition) + { + this.xPosition = xPosition; + this.yPosition = yPosition; + + EmacsNative.sendConfigureNotify (this.handle, + System.currentTimeMillis (), + xPosition, yPosition, + rect.width (), rect.height ()); + } + } }; diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index c0197ab802c..4fda48616f0 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -22,7 +22,9 @@ import java.util.ArrayList; import java.util.List; +import android.app.ActivityOptions; import android.content.Intent; +import android.os.Build; import android.util.Log; /* Code to paper over the differences in lifecycles between @@ -102,6 +104,7 @@ public interface WindowConsumer registerWindow (EmacsWindow window) { Intent intent; + ActivityOptions options; Log.d (TAG, "registerWindow (maybe): " + window); @@ -128,7 +131,18 @@ public interface WindowConsumer intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - EmacsService.SERVICE.startActivity (intent); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) + EmacsService.SERVICE.startActivity (intent); + else + { + /* Specify the desired window size. */ + options = ActivityOptions.makeBasic (); + options.setLaunchBounds (window.getGeometry ()); + EmacsService.SERVICE.startActivity (intent, + options.toBundle ()); + } + Log.d (TAG, "registerWindow: startActivity"); } diff --git a/src/androidterm.c b/src/androidterm.c index 1cf9bc4afde..825d6da60fa 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -787,6 +787,33 @@ handle_one_android_event (struct android_display_info *dpyinfo, cancel_mouse_face (f); } + /* Now change the left and top position of this window. */ + + { + int old_left = f->left_pos; + int old_top = f->top_pos; + Lisp_Object frame; + + XSETFRAME (frame, f); + + { + android_window root; + unsigned int dummy_uint; + + android_get_geometry (FRAME_ANDROID_WINDOW (f), + &root, &f->left_pos, &f->top_pos, + &dummy_uint, &dummy_uint, + &dummy_uint); + } + + if (!FRAME_TOOLTIP_P (f) + && (old_left != f->left_pos || old_top != f->top_pos)) + { + inev.ie.kind = MOVE_FRAME_EVENT; + XSETFRAME (inev.ie.frame_or_window, f); + } + } + goto OTHER; case ANDROID_KEY_PRESS: @@ -1100,6 +1127,11 @@ handle_one_android_event (struct android_display_info *dpyinfo, if (!FRAME_GARBAGED_P (f)) { + __android_log_print (ANDROID_LOG_VERBOSE, __func__, + "expose: %d %d %d %d\n", + event->xexpose.x, event->xexpose.y, + event->xexpose.width, + event->xexpose.height); expose_frame (f, event->xexpose.x, event->xexpose.y, event->xexpose.width, event->xexpose.height); show_back_buffer (f); commit 773bdb15abd5527dfc27d811a70263ac10cf451b Merge: 9d1285883c7 891a37ab36a Author: Po Lu Date: Fri Mar 17 21:39:28 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 9d1285883c7318391e4bc1d50697b02b28ecc89f Author: Po Lu Date: Fri Mar 17 21:39:15 2023 +0800 Include more information in Android bug reports * src/androidfns.c (Fx_server_vendor, Fx_server_version): New functions. (syms_of_androidfns): Define new functions. * src/androidterm.c (android_set_build_fingerprint) (syms_of_androidterm): Set new variable Vandroid_build_manufacturer. * src/xfns.c (Fx_server_vendor, Fx_server_version): Update doc strings. diff --git a/src/androidfns.c b/src/androidfns.c index e1d423ab3eb..3367ebdf755 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -1234,6 +1234,32 @@ DEFUN ("x-display-color-cells", Fx_display_color_cells, Sx_display_color_cells, return make_fixnum (1 << nr_planes); } +DEFUN ("x-server-vendor", Fx_server_vendor, Sx_server_vendor, 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + check_android_display_info (terminal); + return Vandroid_build_manufacturer; +#endif +} + +DEFUN ("x-server-version", Fx_server_version, Sx_server_version, 0, 1, 0, + doc: /* SKIP: real doc in xfns.c. */) + (Lisp_Object terminal) +{ +#ifdef ANDROID_STUBIFY + error ("Android cross-compilation stub called!"); + return Qnil; +#else + check_android_display_info (terminal); + return list3i (android_get_current_api_level (), 0, 0); +#endif +} + DEFUN ("x-display-screens", Fx_display_screens, Sx_display_screens, 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */) (Lisp_Object terminal) @@ -3114,6 +3140,8 @@ syms_of_androidfns (void) defsubr (&Sx_hide_tip); defsubr (&Sandroid_detect_mouse); defsubr (&Sandroid_toggle_on_screen_keyboard); + defsubr (&Sx_server_vendor); + defsubr (&Sx_server_version); #ifndef ANDROID_STUBIFY defsubr (&Sandroid_query_battery); diff --git a/src/androidterm.c b/src/androidterm.c index 0110c4b6dd8..1cf9bc4afde 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5786,7 +5786,8 @@ android_term_init (void) -/* Set Vandroid_build_fingerprint to a reasonable value. */ +/* Set Vandroid_build_fingerprint to a reasonable value, and also + Vandroid_build_manufacturer. */ static void android_set_build_fingerprint (void) @@ -5847,6 +5848,39 @@ android_set_build_fingerprint (void) Vandroid_build_fingerprint = build_string_from_utf8 (data); (*android_java_env)->ReleaseStringUTFChars (android_java_env, string, data); + + /* Now obtain Build.MANUFACTURER. */ + + ANDROID_DELETE_LOCAL_REF (string); + string = NULL; + + field = (*android_java_env)->GetStaticFieldID (android_java_env, + class, + "MANUFACTURER", + "Ljava/lang/String;"); + (*android_java_env)->ExceptionClear (android_java_env); + + if (!field) + goto fail; + + string + = (*android_java_env)->GetStaticObjectField (android_java_env, + class, field); + (*android_java_env)->ExceptionClear (android_java_env); + + if (!string) + goto fail; + + data = (*android_java_env)->GetStringUTFChars (android_java_env, + string, NULL); + (*android_java_env)->ExceptionClear (android_java_env); + + if (!data) + goto fail; + + Vandroid_build_manufacturer = build_string_from_utf8 (data); + (*android_java_env)->ReleaseStringUTFChars (android_java_env, + string, data); } if (string) @@ -5861,6 +5895,7 @@ android_set_build_fingerprint (void) ANDROID_DELETE_LOCAL_REF (class); Vandroid_build_fingerprint = Qnil; + Vandroid_build_manufacturer = Qnil; #endif } @@ -5899,6 +5934,10 @@ syms_of_androidterm (void) Emacs is running on. */); Vandroid_build_fingerprint = Qnil; + DEFVAR_LISP ("android-build-manufacturer", Vandroid_build_manufacturer, + doc: /* Name of the developer of the running version of Android. */); + Vandroid_build_manufacturer = Qnil; + /* Only defined so loadup.el loads scroll-bar.el. */ DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars, doc: /* SKIP: real doc in xterm.c. */); diff --git a/src/xfns.c b/src/xfns.c index 0e4a25de04a..b7000462e84 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -5673,6 +5673,8 @@ DEFUN ("x-server-vendor", Fx_server_vendor, Sx_server_vendor, 0, 1, 0, The optional argument TERMINAL specifies which display to ask about. For GNU and Unix systems, this queries the X server software. +For Android systems, value is the manufacturer who developed the Android +system that is being used. For MS Windows and Nextstep the result is hard-coded. TERMINAL should be a terminal object, a frame or a display name (a string). @@ -5696,7 +5698,8 @@ DEFUN ("x-server-version", Fx_server_version, Sx_server_version, 0, 1, 0, release number. For MS Windows, the 3 numbers report the OS major and minor version and build number. For Nextstep, the first 2 numbers are hard-coded and the 3rd represents the OS version. For Haiku, all 3 -numbers are hard-coded. +numbers are hard-coded. For Android, the first number represents the +Android API level, and the next two numbers are all zero. See also the function `x-server-vendor'. commit 6e83b727061857fb640345fb59b1403e27a8dc23 Author: Po Lu Date: Fri Mar 17 21:21:01 2023 +0800 Fix WINDOWSNT build of fileio.c and image.c * src/fileio.c (emacs_fd_to_int): Don't define on WINDOWSNT. * src/image.c (image_create_bitmap_from_data): Don't abort if !defined HAVE_ANDROID. diff --git a/src/fileio.c b/src/fileio.c index 5153aeae248..1e54f583876 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -128,7 +128,12 @@ #define emacs_fd_read emacs_read_quit #define emacs_fd_lseek lseek #define emacs_fd_fstat sys_fstat #define emacs_fd_valid_p(fd) ((fd) >= 0) + +/* This is not used on MS Windows. */ + +#ifndef WINDOWSNT #define emacs_fd_to_int(fds) (fds) +#endif /* WINDOWSNT */ #else /* HAVE_ANDROID && !defined ANDROID_STUBIFY */ diff --git a/src/image.c b/src/image.c index dc45a2d2419..dc66b1056db 100644 --- a/src/image.c +++ b/src/image.c @@ -517,10 +517,10 @@ image_create_bitmap_from_data (struct frame *f, char *bits, if (!bitmap) return -1; -#else +#elif defined HAVE_ANDROID ((void) dpyinfo); emacs_abort (); -#endif +#endif /* HAVE_ANDROID && !defined ANDROID_STUBIFY */ #ifdef HAVE_NTGUI Lisp_Object frame UNINIT; /* The value is not used. */ commit e7025ed689dcc1640328f7a0e2039c3c4e2d45ee Author: Po Lu Date: Fri Mar 17 19:26:16 2023 +0800 Update Android port * configure.ac: * m4/ndk-build.m4 (ndk_INIT): (ndk_LATE): Avoid AC_REQUIRE magic. diff --git a/configure.ac b/configure.ac index 1dfd880eaa9..44dbf60f938 100644 --- a/configure.ac +++ b/configure.ac @@ -165,7 +165,7 @@ AC_CANONICAL_HOST AC_CANONICAL_BUILD -if test "$XCONFIGURE" = "android"; then +AS_IF([test "$XCONFIGURE" = "android"],[ # Initialize the Android NDK build system. Make sure to use the # passed through NDK path. # Make sure to pass through the CFLAGS, as older versions of the @@ -174,8 +174,7 @@ with_ndk_cxx_shared="$android_ndk_cxx_shared" with_ndk_cxx="$android_ndk_cxx" ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build], - [$ANDROID_CFLAGS]) -fi + [$ANDROID_CFLAGS])]) case $host in *-mingw*) diff --git a/m4/ndk-build.m4 b/m4/ndk-build.m4 index 077781ec38c..9af681a08c8 100644 --- a/m4/ndk-build.m4 +++ b/m4/ndk-build.m4 @@ -38,7 +38,7 @@ # DIR should be a directory containing the Makefile.in actually # implementing the Android NDK build system. -AC_DEFUN_ONCE([ndk_INIT], +AC_DEFUN([ndk_INIT], [ # Look for Android.mk files. ndk_module_files= @@ -347,13 +347,18 @@ AC_DEFUN_ONCE # required C and C++ headers. AC_DEFUN([ndk_LATE], -[ +[dnl +dnl This calls AC_REQUIRE([AC_PROG_CXX]), leading to configure looking +dnl for a C++ compiler. However, the language is not restored +dnl afterwards if not `$ndk_INITIALIZED'. AS_IF([test "$ndk_INITIALIZED" = "yes"],[ AS_IF([test -n "$CXX"], [AC_LANG_PUSH([C++]) AC_CHECK_HEADER([string], [ndk_working_cxx=yes], [AC_MSG_WARN([Your C++ compiler is not properly set up, and\ the standard library headers could not be found.])]) AC_LANG_POP([C++])])]) +dnl Thus, manually switch back to C here. +AC_LANG([C]) ]) # ndk_SEARCH_MODULE(MODULE, NAME, ACTION-IF-FOUND, [ACTION-IF-NOT-FOUND]) commit 2d666daaa3babeda669c39a8cfe36c4d30222512 Merge: 45b5c9b8b72 1565dbcae35 Author: Po Lu Date: Fri Mar 17 15:05:45 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 45b5c9b8b72a9dd561c7e2d43ead8ce64e79b041 Author: Po Lu Date: Fri Mar 17 13:10:23 2023 +0800 Improve radio button appearance in Android menus * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu): New field `lastGroupId'. (Item): New field `isRadio'. (addItem): New arg `isRadio'. (inflateMenuItems): Apply an empty radio button group if required. * src/androidmenu.c (android_init_emacs_context_menu): Adjust accordingly. (android_menu_show): Likewise. diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index d780641ba70..5bae41bd61d 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -55,6 +55,9 @@ public final class EmacsContextMenu /* The serial ID of the last context menu to be displayed. */ public static int lastMenuEventSerial; + /* The last group ID used for a menu item. */ + public int lastGroupId; + private static class Item implements MenuItem.OnMenuItemClickListener { public int itemID; @@ -62,6 +65,7 @@ private static class Item implements MenuItem.OnMenuItemClickListener public EmacsContextMenu subMenu; public boolean isEnabled, isCheckable, isChecked; public EmacsView inflatedView; + public boolean isRadio; @Override public boolean @@ -153,12 +157,14 @@ private static class Item implements MenuItem.OnMenuItemClickListener checkable. Likewise, if ISCHECKED is set, make the item checked. - If TOOLTIP is non-NULL, set the menu item tooltip to TOOLTIP. */ + If TOOLTIP is non-NULL, set the menu item tooltip to TOOLTIP. + + If ISRADIO, then display the check mark as a radio button. */ public void addItem (int itemID, String itemName, boolean isEnabled, boolean isCheckable, boolean isChecked, - String tooltip) + String tooltip, boolean isRadio) { Item item; @@ -169,6 +175,7 @@ private static class Item implements MenuItem.OnMenuItemClickListener item.isCheckable = isCheckable; item.isChecked = isChecked; item.tooltip = tooltip; + item.isRadio = isRadio; menuItems.add (item); } @@ -244,7 +251,11 @@ private static class Item implements MenuItem.OnMenuItemClickListener } else { - menuItem = menu.add (item.itemName); + if (item.isRadio) + menuItem = menu.add (++lastGroupId, Menu.NONE, Menu.NONE, + item.itemName); + else + menuItem = menu.add (item.itemName); menuItem.setOnMenuItemClickListener (item); /* If the item ID is zero, then disable the item. */ @@ -260,6 +271,12 @@ private static class Item implements MenuItem.OnMenuItemClickListener if (item.isChecked) menuItem.setChecked (true); + /* Define an exclusively checkable group if the item is a + radio button. */ + + if (item.isRadio) + menu.setGroupCheckable (lastGroupId, true, true); + /* If the tooltip text is set and the system is new enough to support menu item tooltips, set it on the item. */ diff --git a/src/androidmenu.c b/src/androidmenu.c index 7d9c33e28b1..f74e7ca6d99 100644 --- a/src/androidmenu.c +++ b/src/androidmenu.c @@ -105,7 +105,7 @@ #define FIND_METHOD_STATIC(c_name, name, signature) \ "Lorg/gnu/emacs/EmacsContextMenu;"); FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ" - "Ljava/lang/String;)V"); + "Ljava/lang/String;Z)V"); FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;" "Ljava/lang/String;Ljava/lang/String;)" "Lorg/gnu/emacs/EmacsContextMenu;"); @@ -442,7 +442,9 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, (jboolean) !NILP (enable), (jboolean) checkmark, (jboolean) !NILP (selected), - help_string); + help_string, + (jboolean) (EQ (type, + QCradio))); android_exception_check (); if (title_string) commit da660a1ffa3218f8e6ec4dfd5422ca6c1ded38ae Author: Po Lu Date: Fri Mar 17 10:38:09 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsView.java (cancelPopupMenu): Dismiss context menu correctly. (isOpaque): New function. * java/org/gnu/emacs/EmacsWindowAttachmentManager.java: Make consumer list public. diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 878ef2f3fbf..681da98fa16 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -514,6 +514,17 @@ else if (child.getVisibility () != GONE) contextMenu = null; popupActive = false; + + /* It is not possible to know with 100% certainty which activity + is currently displaying the context menu. Loop through each + activity and call `closeContextMenu' instead. */ + + for (EmacsWindowAttachmentManager.WindowConsumer consumer + : EmacsWindowAttachmentManager.MANAGER.consumers) + { + if (consumer instanceof EmacsActivity) + ((EmacsActivity) consumer).closeContextMenu (); + } } @Override @@ -646,6 +657,16 @@ else if (child.getVisibility () != GONE) return isCurrentlyTextEditor; } + @Override + public boolean + isOpaque () + { + /* Returning true here allows the system to not draw the contents + of windows underneath this view, thereby improving + performance. */ + return true; + } + public synchronized void setICMode (int icMode) { diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index 30f29250970..c0197ab802c 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -67,7 +67,7 @@ public interface WindowConsumer public void destroy (); }; - private List consumers; + public List consumers; public List windows; public commit b37bb4279c7541cac6596747ea0951e8f02462ec Author: Po Lu Date: Fri Mar 17 10:03:43 2023 +0800 ; * configure.ac: Add missing precious variable. diff --git a/configure.ac b/configure.ac index 88c1b6667fa..1dfd880eaa9 100644 --- a/configure.ac +++ b/configure.ac @@ -810,6 +810,7 @@ AC_DEFUN # This is whether or not to package mailutils into the executable. emacs_use_mailutils= +AC_ARG_VAR([ANDROID_CC], [The Android C cross-compiler.]) AC_ARG_VAR([SDK_BUILD_TOOLS], [Name of directory holding Android SDK build-tools.]) AC_ARG_VAR([ANDROID_CFLAGS], [Flags given to the Android C cross-compiler.]) AC_ARG_VAR([JAVAC], [Java compiler path. Used for Android.]) commit 3702389a50314903503beb6d376b5d1af00e4f56 Merge: 584eeb24ebe 0330cff65ae Author: Po Lu Date: Thu Mar 16 19:56:25 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 584eeb24ebe4603fdb0d6dec494ae95ebc128195 Author: Po Lu Date: Thu Mar 16 18:34:53 2023 +0800 Update Android port * lisp/frame.el (android-detect-mouse): * lisp/term/android-win.el (android-get-connection): Add function declarations. diff --git a/lisp/frame.el b/lisp/frame.el index a614d600fd0..b62074d6263 100644 --- a/lisp/frame.el +++ b/lisp/frame.el @@ -2124,6 +2124,7 @@ frame-size-changed-p ;; or in https://debbugs.gnu.org/cgi/bugreport.cgi?bug=35058#17. (declare-function msdos-mouse-p "dosfns.c") +(declare-function android-detect-mouse "androidfns.c") (defun display-mouse-p (&optional display) "Return non-nil if DISPLAY has a mouse available. diff --git a/lisp/term/android-win.el b/lisp/term/android-win.el index fc393681ac4..c7610ae2ca3 100644 --- a/lisp/term/android-win.el +++ b/lisp/term/android-win.el @@ -41,6 +41,8 @@ (add-to-list 'display-format-alist '(".*" . android)) +(declare-function android-get-connection "androidfns.c") + ;; Window system initialization. This is extremely simple because all ;; initialization is done in android_term_init. commit 34e9f7a0eea77ecca3feb1e3f825bb0b5d10c232 Author: Po Lu Date: Thu Mar 16 15:48:26 2023 +0800 ; * configure.ac: Remove unnecessary escape. diff --git a/configure.ac b/configure.ac index 5a8771bf3d7..88c1b6667fa 100644 --- a/configure.ac +++ b/configure.ac @@ -29,8 +29,7 @@ if test "$XCONFIGURE" = "android"; then # configure is being called recursively to configure Emacs for # Android! - AC_MSG_NOTICE([called to recursively configure Emacs \ -for Android.]) + AC_MSG_NOTICE([called to recursively configure Emacs for Android.]) # Set CC to ANDROID_CC and CFLAGS to ANDROID_CFLAGS. CC=$ANDROID_CC # Set -Wno-implicit-function-declaration. Building Emacs for older commit e38758aaf40280b8bda819990af8b0f43c48d5f9 Author: Po Lu Date: Thu Mar 16 15:46:39 2023 +0800 Make ANDROID_CC and SDK_BUILD_TOOLS precious variables * configure.ac (AUTO_DEPEND, ANDROID_STUBIFY, ANDROID_LDFLAGS): * lib/Makefile.in (ANDROID_CFLAGS, ANDROID_BUILD_CFLAGS) (ALL_CFLAGS): * lib/gnulib.mk.in (AM_DEFAULT_VERBOSITY): * msdos/sed1v2.inp: * msdos/sedlibmk.inp: * src/Makefile.in (ANDROID_OBJ, EMACS_CFLAGS): Make those variables precious. Rename ANDROID_CFLAGS substitution to ANDROID_BUILD_CFLAGS. diff --git a/configure.ac b/configure.ac index 0282aa0c201..5a8771bf3d7 100644 --- a/configure.ac +++ b/configure.ac @@ -174,7 +174,7 @@ with_ndk_path="$android_ndk_path" with_ndk_cxx_shared="$android_ndk_cxx_shared" with_ndk_cxx="$android_ndk_cxx" - ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build],\ + ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build], [$ANDROID_CFLAGS]) fi @@ -811,6 +811,8 @@ AC_DEFUN # This is whether or not to package mailutils into the executable. emacs_use_mailutils= +AC_ARG_VAR([SDK_BUILD_TOOLS], [Name of directory holding Android SDK build-tools.]) +AC_ARG_VAR([ANDROID_CFLAGS], [Flags given to the Android C cross-compiler.]) AC_ARG_VAR([JAVAC], [Java compiler path. Used for Android.]) AC_ARG_VAR([JARSIGNER], [Java package signer path. Used for Android.]) AC_ARG_VAR([APKSIGNER], [Android package signer path. Used for Android.]) @@ -2573,7 +2575,9 @@ AC_DEFUN ANDROID_OBJ= ANDROID_LIBS= -ANDROID_CFLAGS= +# ANDROID_CFLAGS is a precious variable used to pass information to +# the cross-compiler. +ANDROID_BUILD_CFLAGS= REALLY_ANDROID= CM_OBJ="cm.o" @@ -2602,11 +2606,11 @@ AC_DEFUN # Emacs will be built as a shared library, and a wrapper around it # will also be built for the benefit of applications. This # requires Emacs be built as a position independent executable. - ANDROID_CFLAGS="-fPIC -fvisibility=hidden" + ANDROID_BUILD_CFLAGS="-fPIC -fvisibility=hidden" # Graphics code in sfntfont-android.c benefits heavily from # vectorization. - ANDROID_CFLAGS="$ANDROID_CFLAGS -ftree-vectorize" + ANDROID_BUILD_CFLAGS="$ANDROID_BUILD_CFLAGS -ftree-vectorize" # Link with libraries required for Android support. # API 9 and later require `-landroid' for the asset manager. @@ -2638,7 +2642,7 @@ AC_DEFUN AC_SUBST([ANDROID_OBJ]) AC_SUBST([ANDROID_LIBS]) AC_SUBST([ANDROID_LDFLAGS]) -AC_SUBST([ANDROID_CFLAGS]) +AC_SUBST([ANDROID_BUILD_CFLAGS]) AC_SUBST([ANDROID_SHARED_USER_ID]) if test "${with_pgtk}" = "yes"; then diff --git a/lib/Makefile.in b/lib/Makefile.in index b84a1bf9741..6752f68c50e 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -27,7 +27,7 @@ XCONFIGURE = # This is required to make sure symbol visibility is correct and # functions like readlinkat do not end up replacing their OS # counterparts. -ANDROID_CFLAGS = @ANDROID_CFLAGS@ +ANDROID_BUILD_CFLAGS = @ANDROID_BUILD_CFLAGS@ # Variables substituted by 'configure', and not autogenerated in gnulib.mk, # or needed before gnulib.mk is included. @@ -46,7 +46,7 @@ ALL_CFLAGS = $(C_SWITCH_SYSTEM) $(C_SWITCH_MACHINE) $(DEPFLAGS) \ $(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS) $(PROFILING_CFLAGS) $(CFLAGS) \ -I. -I../src -I$(srcdir) -I$(top_srcdir)/src \ - $(if $(patsubst e-%,,$(notdir $<)),,-Demacs) $(ANDROID_CFLAGS) + $(if $(patsubst e-%,,$(notdir $<)),,-Demacs) $(ANDROID_BUILD_CFLAGS) ifeq ($(HAVE_NATIVE_COMP),yes) ALL_CFLAGS += -DGL_COMPILE_CRYPTO_STREAM diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 99286852f8b..94fd1d5bec9 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -184,7 +184,7 @@ ALSA_LIBS = @ALSA_LIBS@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ ANDROID = @ANDROID@ ANDROID_ABI = @ANDROID_ABI@ -ANDROID_CFLAGS = @ANDROID_CFLAGS@ +ANDROID_BUILD_CFLAGS = @ANDROID_BUILD_CFLAGS@ ANDROID_DEBUGGABLE = @ANDROID_DEBUGGABLE@ ANDROID_JAR = @ANDROID_JAR@ ANDROID_LDFLAGS = @ANDROID_LDFLAGS@ diff --git a/msdos/sed1v2.inp b/msdos/sed1v2.inp index 32f1a249f21..71aa27afce2 100644 --- a/msdos/sed1v2.inp +++ b/msdos/sed1v2.inp @@ -206,7 +206,7 @@ s/ *@WEBP_LIBS@// /^ANDROID_OBJ *=/s/@ANDROID_OBJ@// /^ANDROID_LIBS *=/s/@ANDROID_LIBS@// /^ANDROID_LDFLAGS *=/s/@ANDROID_LDFLAGS@// -/^ANDROID_CFLAGS *=/s/@ANDROID_CFLAGS@// +/^ANDROID_BUILD_CFLAGS *=/s/@ANDROID_CFLAGS@// /^LIBGMP_CFLAGS *=/s/@LIBGMP_CFLAGS@// /^SQLITE3_CFLAGS *=/s/@SQLITE3_CFLAGS@// /^LIBSELINUX_CFLAGS *=/s/@LIBSELINUX_CFLAGS@// diff --git a/msdos/sedlibmk.inp b/msdos/sedlibmk.inp index 81b2ac44497..cca2b46b018 100644 --- a/msdos/sedlibmk.inp +++ b/msdos/sedlibmk.inp @@ -156,7 +156,7 @@ s/@PACKAGE@/emacs/ /^HYBRID_MALLOC *=/s/@HYBRID_MALLOC@// /^WARN_CFLAGS *=/s/@WARN_CFLAGS@// /^WERROR_CFLAGS *=/s/@WERROR_CFLAGS@// -/^ANDROID_CFLAGS *=/s/@ANDROID_CFLAGS@// +/^ANDROID_BUILD_CFLAGS *=/s/@ANDROID_BUILD_CFLAGS@// /^DEFS *=/s/@[^@\n]*@// /^DEPDIR *=/s/@[^@\n]*@/deps/ /^ECHO_N *=/s/@[^@\n]*@/-n/ diff --git a/src/Makefile.in b/src/Makefile.in index 1e364cd7a10..9ac7983943e 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -391,7 +391,7 @@ HAIKU_CFLAGS = ANDROID_OBJ = @ANDROID_OBJ@ ANDROID_LIBS = @ANDROID_LIBS@ ANDROID_LDFLAGS = @ANDROID_LDFLAGS@ -ANDROID_CFLAGS = @ANDROID_CFLAGS@ +ANDROID_BUILD_CFLAGS = @ANDROID_BUILD_CFLAGS@ LIBGMP_CFLAGS = @LIBGMP_CFLAGS@ @@ -437,7 +437,7 @@ EMACS_CFLAGS= $(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) $(XSYNC_CFLAGS) $(TREE_SITTER_CFLAGS) \ $(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \ $(WERROR_CFLAGS) $(HAIKU_CFLAGS) $(XCOMPOSITE_CFLAGS) $(XSHAPE_CFLAGS) \ - $(ANDROID_CFLAGS) $(GIF_CFLAGS) $(JPEG_CFLAGS) $(SQLITE3_CFLAGS) \ + $(ANDROID_BUILD_CFLAGS) $(GIF_CFLAGS) $(JPEG_CFLAGS) $(SQLITE3_CFLAGS) \ $(LIBGMP_CFLAGS) $(TIFF_CFLAGS) $(LIBSELINUX_CFLAGS) ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS) ALL_OBJC_CFLAGS = $(EMACS_CFLAGS) \ commit 5bdbfba4fc4654c08d0341542176de96359a155b Author: Po Lu Date: Thu Mar 16 15:23:21 2023 +0800 Update Android port * nt/mingw-cfg.site: Suppress build of gnulib printf. diff --git a/nt/mingw-cfg.site b/nt/mingw-cfg.site index 425eaace30d..0e8c3aca3bb 100644 --- a/nt/mingw-cfg.site +++ b/nt/mingw-cfg.site @@ -173,3 +173,18 @@ gl_cv_func_nanosleep=yes # Suppress configure-time diagnostic from unnecessary libxattr check, # as xattr will not be supported here. enable_xattr=no +# Don't build gnulib printf either. +gl_cv_func_printf_sizes_c99=yes +gl_cv_func_printf_long_double=yes +gl_cv_func_printf_infinite_long_double=yes +gl_cv_func_printf_directive_a=yes +gl_cv_func_printf_directive_f=yes +gl_cv_func_printf_directive_n=yes +gl_cv_func_printf_directive_ls=yes +gl_cv_func_printf_positions=yes +gl_cv_func_printf_flag_grouping=yes +gl_cv_func_printf_flag_leftadjust=yes +gl_cv_func_printf_flag_zero=yes +gl_cv_func_printf_precision=yes +gl_cv_func_printf_enomem=yes +ac_cv_func_vasprintf=yes commit ce66228ac538949d9dda2fc759982600344c7a5e Author: Po Lu Date: Thu Mar 16 14:13:21 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsDocumentsProvider.java (queryRoots): Add icon to document root. diff --git a/java/org/gnu/emacs/EmacsDocumentsProvider.java b/java/org/gnu/emacs/EmacsDocumentsProvider.java index f70da6040ff..a92a1a5d330 100644 --- a/java/org/gnu/emacs/EmacsDocumentsProvider.java +++ b/java/org/gnu/emacs/EmacsDocumentsProvider.java @@ -66,6 +66,7 @@ public final class EmacsDocumentsProvider extends DocumentsProvider Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES, Root.COLUMN_FLAGS, + Root.COLUMN_ICON, Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID, @@ -116,6 +117,7 @@ public final class EmacsDocumentsProvider extends DocumentsProvider row.add (Root.COLUMN_FLAGS, (Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_IS_CHILD)); + row.add (Root.COLUMN_ICON, R.drawable.emacs); row.add (Root.FLAG_LOCAL_ONLY); row.add (Root.COLUMN_TITLE, "Emacs"); row.add (Root.COLUMN_DOCUMENT_ID, baseDir.getAbsolutePath ()); commit d04731b5885957150b54efaa40fdf712b30170ff Author: Po Lu Date: Thu Mar 16 09:40:02 2023 +0800 Update Android port * lisp/loadup.el (current-load-list): Set to empty load list after startup. * src/lread.c (build_load_history): Revert earlier changes. diff --git a/lisp/loadup.el b/lisp/loadup.el index 47565f809af..15267cca3be 100644 --- a/lisp/loadup.el +++ b/lisp/loadup.el @@ -666,6 +666,13 @@ (setq load-file-name nil) (eval top-level t) +;; loadup.el is loaded at startup, but clobbers current-load-list. +;; Set current-load-list to a list containing no definitions and only +;; its name, to prevent invalid entries from ending up in +;; Vload_history when running temacs interactively. + +(setq current-load-list (list "loadup.el")) + ;; Local Variables: ;; no-byte-compile: t diff --git a/src/lread.c b/src/lread.c index c29c7ede6ac..fe48e614393 100644 --- a/src/lread.c +++ b/src/lread.c @@ -2317,7 +2317,7 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, build_load_history (Lisp_Object filename, bool entire) { Lisp_Object tail, prev, newelt; - Lisp_Object tem, tem2, association; + Lisp_Object tem, tem2; bool foundit = 0; tail = Vload_history; @@ -2366,16 +2366,8 @@ build_load_history (Lisp_Object filename, bool entire) front of load-history, the most-recently-loaded position. Also do this if we didn't find an existing member for the file. */ if (entire || !foundit) - { - association = Fnreverse (Vcurrent_load_list); - - if (!NILP (association) && STRINGP (XCAR (association))) - /* readevalloop can be called with SOURCENAME set to some - nonsense value, meaning the car of ASSOCIATION will be nil - (or worse something else), leading to an invalid - Vload_history. Ignore such invalid entries. */ - Vload_history = Fcons (association, Vload_history); - } + Vload_history = Fcons (Fnreverse (Vcurrent_load_list), + Vload_history); } static void commit 9a4a7de914f6616867379c8cd0aed68b09dd80f6 Merge: 4e9e72ea480 a066487a0d4 Author: Po Lu Date: Thu Mar 16 08:40:35 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 4e9e72ea4805d323b1e65fbfa97bbfa9a72d2ee1 Author: Po Lu Date: Wed Mar 15 21:17:34 2023 +0800 Update Android port * configure.ac: Improve portability. diff --git a/configure.ac b/configure.ac index cc3ae1af82f..0282aa0c201 100644 --- a/configure.ac +++ b/configure.ac @@ -1179,7 +1179,7 @@ AC_DEFUN android_ndk_path="$with_ndk_path" \ android_ndk_cxx_shared="$with_ndk_cxx_shared" \ android_ndk_cxx="$android_ndk_cxx" \ - $0 $passthrough], [], + $CONFIG_SHELL $0 $passthrough], [], [AC_MSG_ERROR([Failed to cross-configure Emacs for android.])]) # Now set ANDROID to yes. @@ -7527,9 +7527,12 @@ AC_DEFUN AC_DEFINE_UNQUOTED([EMACS_CONFIG_FEATURES], ["${emacs_config_features}"], [Summary of some of the main features enabled by configure.]) +# This is just a printable representation of the shared user ID. +android_shared_user= +AS_IF([test -n "$with_shared_user_id"],[android_shared_user="($with_shared_user_id)"]) + AS_ECHO([" Does Emacs use -lXaw3d? ${HAVE_XAW3D} - Is Emacs being built for Android? ${ANDROID}\ -`AS_IF([test -n "$with_shared_user_id"],[AS_ECHO([" ($with_shared_user_id)"])])` + Is Emacs being built for Android? ${ANDROID} ${android_shared_user} Does Emacs use the X Double Buffer Extension? ${HAVE_XDBE} Does Emacs use -lXpm? ${HAVE_XPM} Does Emacs use -ljpeg? ${HAVE_JPEG} commit 3504c7550d9e2eeeabfe71b5819506ebc7c045b4 Merge: f57c64925bc fe58837bbec Author: Po Lu Date: Wed Mar 15 18:28:05 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit f57c64925bc16fa967a86ffe424396a395e448fb Author: Robert Pluim Date: Wed Mar 15 18:27:29 2023 +0800 Fix typos in Android port * src/fileio.c (Finsert_file_contents): * src/window.c (replace_buffer_in_windows): Call Fboundp, not boundp. diff --git a/src/fileio.c b/src/fileio.c index 99f710ccbf0..5153aeae248 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -5004,7 +5004,7 @@ because (1) it preserves some marker positions (in unchanged portions /* Decode file format. Don't do this if Qformat_decode is not bound, which can happen when called early during loadup. */ - if (inserted > 0 && !NILP (Fboundp (Qformat_decode))) + if (inserted > 0 && !NILP (Ffboundp (Qformat_decode))) { /* Don't run point motion or modification hooks when decoding. */ specpdl_ref count1 = SPECPDL_INDEX (); diff --git a/src/window.c b/src/window.c index 9a29ecb8807..8c42d3cdd0c 100644 --- a/src/window.c +++ b/src/window.c @@ -3516,7 +3516,7 @@ replace_buffer_in_windows (Lisp_Object buffer) { /* When kill-buffer is called early during loadup, this function is undefined. */ - if (!NILP (Fboundp (Qreplace_buffer_in_windows))) + if (!NILP (Ffboundp (Qreplace_buffer_in_windows))) call1 (Qreplace_buffer_in_windows, buffer); } commit af7f0b6f9b3bab0d672953dfe9df9dc667287434 Author: Po Lu Date: Wed Mar 15 16:17:32 2023 +0800 Update Android port * cross/Makefile.in (lib/gnulib.mk): Edit out build-aux stuff. * m4/ndk-build.m4: Also look for cross ranlib. diff --git a/cross/Makefile.in b/cross/Makefile.in index bceb761d769..5976272b253 100644 --- a/cross/Makefile.in +++ b/cross/Makefile.in @@ -96,9 +96,16 @@ lib-src/config.h: $(AM_V_GEN) cp -f -p $(top_builddir)/src/config.h.android \ lib-src/config.h +# Figure out where build-aux is. +# Then, replace the build-aux directory with its actual location, +# in case MKDIR_P points there. + +relative_buildaux_dir := $(subst /,\/,$(top_srcdir)/build-aux) + lib/gnulib.mk: $(top_builddir)/lib/gnulib.mk.android $(AM_V_GEN) \ sed -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' \ + -e 's/$(relative_buildaux_dir)/$(subst /,\/,../$(top_builddir))\/build-aux/g' \ < $(top_builddir)/lib/gnulib.mk.android > $@ lib/Makefile: $(top_builddir)/lib/Makefile.android diff --git a/m4/ndk-build.m4 b/m4/ndk-build.m4 index d5f16248dcd..077781ec38c 100644 --- a/m4/ndk-build.m4 +++ b/m4/ndk-build.m4 @@ -228,10 +228,12 @@ AC_DEFUN_ONCE done } -# Look for a suitable ar in the same directory as the C compiler. +# Look for a suitable ar and ranlib in the same directory as the C +# compiler. ndk_cc_firstword=`AS_ECHO(["$CC"]) | cut -d' ' -f1` ndk_where_cc=`which $ndk_cc_firstword` ndk_ar_search_path=$PATH +ndk_ranlib_search_path=$RANLIB # First, try to find $host_alias-ar in PATH. AC_PATH_PROGS([AR], [$host_alias-ar], [], [$ndk_ar_search_path]) @@ -242,6 +244,16 @@ AC_DEFUN_ONCE ndk_ar_search_path="`AS_DIRNAME([$ndk_where_cc])`:$ndk_ar_search_path" AC_PATH_PROGS([AR], [$host_alias-ar llvm-ar], [], [$ndk_ar_search_path])]) +# First, try to find $host_alias-ranlib in PATH. +AC_PATH_PROGS([RANLIB], [$host_alias-ranlib], [], [$ndk_ranlib_search_path]) + +AS_IF([test -z "$RANLIB"],[ + # Next, try finding either that or llvm-ranlib in the directory + # holding CC. + ndk_ranlib_search_path="`AS_DIRNAME([$ndk_where_cc])`:$ndk_ranlib_search_path" + AC_PATH_PROGS([RANLIB], [$host_alias-ranlib llvm-ranlib], [], + [$ndk_ranlib_search_path])]) + NDK_BUILD_NASM= # Next, try to find nasm on x86. This doesn't ship with the NDK. commit 4e05371fd1da6dd0aa5187b8037948094762a749 Author: Po Lu Date: Wed Mar 15 15:57:59 2023 +0800 ; * src/sfntfont.c (sfntfont_close): Fix warning w/o mmap. diff --git a/src/sfntfont.c b/src/sfntfont.c index ab92def0aff..b8ffce27062 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -2663,8 +2663,9 @@ sfntfont_text_extents (struct font *font, const unsigned int *code, void sfntfont_close (struct font *font) { - struct sfnt_font_info *info, **next; + struct sfnt_font_info *info; #ifdef HAVE_MMAP + struct sfnt_font_info **next; int rc; #endif /* HAVE_MMAP */ commit 8fbac937fbaf0d74ded6eebdf6acd4636d7b00ea Author: Po Lu Date: Wed Mar 15 15:38:19 2023 +0800 Port to systems without endian.h * lib-src/asset-directory-tool.c (main_2): Port to systems without htole32. diff --git a/lib-src/asset-directory-tool.c b/lib-src/asset-directory-tool.c index e53398eceb0..239ab083b66 100644 --- a/lib-src/asset-directory-tool.c +++ b/lib-src/asset-directory-tool.c @@ -22,7 +22,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. #include #include #include -#include +#include #include #include #include @@ -197,7 +197,11 @@ main_2 (int fd, struct directory_tree *tree, size_t *offset) croak ("write"); /* Write the offset. */ - output = htole32 (tree->offset); +#ifdef WORDS_BIGENDIAN + output = bswap_32 (tree->offset); +#else + output = tree->offset; +#endif if (write (fd, &output, 4) < 1) croak ("write"); size += 4; commit 5b9d6738d119d2c450dd8f060793aebe3f64fc72 Author: Po Lu Date: Wed Mar 15 15:07:29 2023 +0800 Update Android port * configure.ac (XCONFIGURE): Disable NS. * cross/Makefile.in (lib-src/config.h): (lib/libgnu.a): (src/android-emacs): Port sed invocation to Mac OS without GNU sed. diff --git a/configure.ac b/configure.ac index a8b1f297bc4..cc3ae1af82f 100644 --- a/configure.ac +++ b/configure.ac @@ -1266,6 +1266,7 @@ AC_DEFUN with_dbus=no with_gsettings=no with_threads=no + with_ns=no # zlib is available in android. fi diff --git a/cross/Makefile.in b/cross/Makefile.in index 2b287816a55..bceb761d769 100644 --- a/cross/Makefile.in +++ b/cross/Makefile.in @@ -97,22 +97,16 @@ lib-src/config.h: lib-src/config.h lib/gnulib.mk: $(top_builddir)/lib/gnulib.mk.android - $(AM_V_GEN) cp -f -p $(top_builddir)/lib/gnulib.mk.android \ - lib/gnulib.mk - $(AM_V_SILENT) \ - sed -i 's/srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' \ - lib/gnulib.mk + $(AM_V_GEN) \ + sed -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' \ + < $(top_builddir)/lib/gnulib.mk.android > $@ lib/Makefile: $(top_builddir)/lib/Makefile.android - $(AM_V_GEN) cp -f -p $(top_builddir)/lib/Makefile.android \ - lib/Makefile - $(AM_V_SILENT) \ - sed -i 's/top_srcdir =.*$$/top_srcdir = $(subst /,\/,$(LIB_TOP_SRCDIR))/g' \ - lib/Makefile \ - && sed -i 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' \ - lib/Makefile \ - && sed -i 's/VPATH =.*$$/VPATH = $(subst /,\/,$(LIB_SRCDIR))/g' \ - lib/Makefile + $(AM_V_GEN) \ + sed -e 's/^top_srcdir =.*$$/top_srcdir = $(subst /,\/,$(LIB_TOP_SRCDIR))/g' \ + -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' \ + -e 's/^VPATH =.*$$/VPATH = $(subst /,\/,$(LIB_SRCDIR))/g' \ + < $(top_builddir)/lib/Makefile.android > $@ # What is needed to build gnulib. LIB_DEPS = lib/config.h lib/gnulib.mk lib/Makefile @@ -121,34 +115,27 @@ .PHONY: lib/libgnu.a: src/verbose.mk config.status $(LIB_DEPS) $(PRE_BUILD_DEPS) $(MAKE) -C lib libgnu.a -src/Makefile src/config.h &: $(top_builddir)/src/config.h.android \ - $(top_builddir)/src/Makefile.android -# Copy config.h to src/ - $(AM_V_GEN) \ - cp -f -p $(top_builddir)/src/config.h.android src/config.h -# And the Makefile. - $(AM_V_SILENT) \ - cp -f -p $(top_builddir)/src/Makefile.android src/Makefile -# Next, edit srcdir and top_srcdir to the right location. - $(AM_V_SILENT) \ - sed -i 's/srcdir =.*$$/srcdir = $(subst /,\/,$(SRC_SRCDIR))/g' src/Makefile - $(AM_V_SILENT) \ - sed -i 's/top_srcdir =.*$$/top_srcdir = $(subst /,\/,$(LIB_TOP_SRCDIR))/g' \ - src/Makefile +# Edit srcdir and top_srcdir to the right locations. # Edit references to ../admin/unidata to read ../../admin/unidata. - $(AM_V_SILENT) \ - sed -i 's/\.\.\/admin\/unidata/..\/..\/admin\/unidata/g' src/Makefile - $(AM_V_SILENT) \ - sed -i 's/\.\.\/admin\/charsets/..\/..\/admin\/charsets/g' src/Makefile # Next, edit libsrc to the location at top_srcdir! It is important # that src/Makefile uses the binaries there, instead of any # cross-compiled binaries at ./lib-src. - $(AM_V_SILENT) \ - sed -i 's/libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' src/Makefile # Edit out anything saying -I($(top_srcdir)/lib) into # -I$../(srcdir)/lib; that should be covered by -I$(lib) - $(AM_V_SILENT) \ - sed -i 's/-I\$$(top_srcdir)\/lib/-I..\/$(subst /,\/,$(srcdir))\/lib/g' src/Makefile + +src/Makefile: $(top_builddir)/src/Makefile.android + $(AM_V_GEN) \ + sed -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(SRC_SRCDIR))/g' \ + -e 's/^top_srcdir =.*$$/top_srcdir = $(subst /,\/,$(LIB_TOP_SRCDIR))/g' \ + -e 's/\.\.\/admin\/unidata/..\/..\/admin\/unidata/g' \ + -e 's/\.\.\/admin\/charsets/..\/..\/admin\/charsets/g' \ + -e 's/^libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' \ + -e 's/libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' \ + -e 's/-I\$$(top_srcdir)\/lib/-I..\/$(subst /,\/,$(srcdir))\/lib/g' \ + < $(top_builddir)/src/Makefile.android > $@ + +src/config.h: $(top_builddir)/src/config.h.android + $(AM_V_GEN) cp -f -p $< $@ .PHONY: src/android-emacs src/libemacs.so @@ -160,22 +147,17 @@ src/android-emacs: $(PRE_BUILD_DEPS) $(MAKE) -C src android-emacs -lib-src/Makefile: $(top_builddir)/lib-src/Makefile.android - $(AM_V_GEN) cp -f -p $< $@ - $(AM_V_SILENT) sed -i 's/-I\$${srcdir}\/\.\.\/lib//g' lib-src/Makefile -# Next, edit srcdir and top_srcdir to the right location. - $(AM_V_SILENT) \ - sed -i 's/srcdir=.*$$/srcdir = $(subst /,\/,$(LIB_SRC_SRCDIR))/g' \ - lib-src/Makefile - $(AM_V_SILENT) \ - sed -i 's/top_srcdir=.*$$/top_srcdir = $(subst /,\/,$(LIB_SRC_TOP_SRCDIR))/g' \ - lib-src/Makefile # Edit out SCRIPTS, it interferes with the build. - $(AM_V_SILENT) \ - sed -i 's/^SCRIPTS=.*$$/SCRIPTS=/g' lib-src/Makefile # Make BASE_CFLAGS also include cross/lib as well as ../lib. - $(AM_V_SILENT) \ - sed -i 's/-I\.\.\/lib/-I..\/lib -I..\/$(subst /,\/,$(srcdir))\/lib/g' lib-src/Makefile + +lib-src/Makefile: $(top_builddir)/lib-src/Makefile.android + $(AM_V_GEN) \ + sed -e 's/-I\$${srcdir}\/\.\.\/lib//g' \ + -e 's/^srcdir=.*$$/srcdir = $(subst /,\/,$(LIB_SRC_SRCDIR))/g' \ + -e 's/^top_srcdir=.*$$/top_srcdir = $(subst /,\/,$(LIB_SRC_TOP_SRCDIR))/g' \ + -e 's/^SCRIPTS=.*$$/SCRIPTS=/g' \ + -e 's/-I\.\.\/lib/-I..\/lib -I..\/$(subst /,\/,$(srcdir))\/lib/g' \ + < $(top_builddir)/lib-src/Makefile.android > $@ .PHONY: $(LIBSRC_BINARIES) $(LIBSRC_BINARIES) &: src/verbose.mk $(top_builddir)/$@ lib/libgnu.a \ commit c74bab6067cb95516b25e5650d5077441541ea1e Author: Po Lu Date: Wed Mar 15 09:46:01 2023 +0800 Update Android port * doc/lispref/commands.texi (Misc Events): Document variable `disable-inhibit-text-conversion'. * java/org/gnu/emacs/EmacsDialog.java (display1): Try an activity that is certain to be focused first. * lisp/touch-screen.el (touch-screen-track-tap) (touch-screen-track-drag): Bind `disable-inhibit-text-conversion'. * src/keyboard.c (read_key_sequence): Only disable text conversion if an actual function or numeric key is found in the key sequence. (syms_of_keyboard): New variable `disable-inhibit-text-conversion'. * src/lread.c (read_filtered_event): Check new variable. * src/textconv.c (textconv_query): Remove unused label. diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi index 271a6d15aa1..4a3ff1b5d25 100644 --- a/doc/lispref/commands.texi +++ b/doc/lispref/commands.texi @@ -2263,6 +2263,12 @@ Misc Events lock up the input method for a significant amount of time, so do not do this lightly! +@vindex disable-inhibit-text-conversion +In addition, text conversion is automatically disabled after a prefix +key is read by the command loop, or through @code{read-key-sequence}. +This can be disabled by setting or binding the variable +@code{disable-inhibit-text-conversion} to a non-@code{nil} value. + @cindex @code{delete-frame} event @item (delete-frame (@var{frame})) This kind of event indicates that the user gave the window manager diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java index de5a37bd5c5..3a5f22021fc 100644 --- a/java/org/gnu/emacs/EmacsDialog.java +++ b/java/org/gnu/emacs/EmacsDialog.java @@ -244,23 +244,26 @@ private class EmacsButton implements View.OnClickListener, AlertDialog dialog; Window window; - /* First, try to display a dialog using the service context. */ - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M - || Settings.canDrawOverlays (EmacsService.SERVICE)) - context = EmacsService.SERVICE; - else if (EmacsActivity.focusedActivities.isEmpty ()) + if (EmacsActivity.focusedActivities.isEmpty ()) { /* If focusedActivities is empty then this dialog may have been displayed immediately after a popup dialog is - dismissed. */ + dismissed. Or Emacs might legitimately be in the + background. Try the service context first if possible. */ - context = EmacsActivity.lastFocusedActivity; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M + || Settings.canDrawOverlays (EmacsService.SERVICE)) + context = EmacsService.SERVICE; + else + context = EmacsActivity.lastFocusedActivity; if (context == null) return false; } else + /* Display using the activity context when Emacs is in the + foreground, as this allows the dialog to be dismissed more + consistently. */ context = EmacsActivity.focusedActivities.get (0); Log.d (TAG, "display1: using context " + context); diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el index 3d542d78b1d..31d46b062ed 100644 --- a/lisp/touch-screen.el +++ b/lisp/touch-screen.el @@ -511,20 +511,21 @@ touch-screen-track-tap Return nil immediately if any other kind of event is received; otherwise, return t once the `touchscreen-end' event arrives." - (catch 'finish - (while t - (let ((new-event (read-event nil))) - (cond - ((eq (car-safe new-event) 'touchscreen-update) - (when (and update (assq (caadr event) (cadr new-event))) - (funcall update new-event data))) - ((eq (car-safe new-event) 'touchscreen-end) - (throw 'finish - ;; Now determine whether or not the `touchscreen-end' - ;; event has the same ID as EVENT. If it doesn't, - ;; then this is another touch, so return nil. - (eq (caadr event) (caadr new-event)))) - (t (throw 'finish nil))))))) + (let ((disable-inhibit-text-conversion t)) + (catch 'finish + (while t + (let ((new-event (read-event nil))) + (cond + ((eq (car-safe new-event) 'touchscreen-update) + (when (and update (assq (caadr event) (cadr new-event))) + (funcall update new-event data))) + ((eq (car-safe new-event) 'touchscreen-end) + (throw 'finish + ;; Now determine whether or not the `touchscreen-end' + ;; event has the same ID as EVENT. If it doesn't, + ;; then this is another touch, so return nil. + (eq (caadr event) (caadr new-event)))) + (t (throw 'finish nil)))))))) (defun touch-screen-track-drag (event update &optional data) "Track a single drag starting from EVENT. @@ -543,7 +544,8 @@ touch-screen-track-drag touch point in EVENT did not move significantly, and t otherwise." (let ((return-value 'no-drag) (start-xy (touch-screen-relative-xy (cdadr event) - 'frame))) + 'frame)) + (disable-inhibit-text-conversion t)) (catch 'finish (while t (let ((new-event (read-event nil))) diff --git a/src/keyboard.c b/src/keyboard.c index 11c37372db7..aa7c81f48f1 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -10227,33 +10227,38 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt, #ifdef HAVE_TEXT_CONVERSION /* When reading a key sequence while text conversion is in - effect, turn it off after the first character read. This - makes input methods send actual key events instead. + effect, turn it off after the first actual character read. + This makes input methods send actual key events instead. Make sure only to do this once. Also, disabling text conversion seems to interact badly with menus, so don't disable text conversion if a menu was displayed. */ - if (!disabled_conversion && t && !used_mouse_menu) + if (!disabled_conversion && t && !used_mouse_menu + && !disable_inhibit_text_conversion) { int i; /* used_mouse_menu isn't set if a menu bar prefix key has - just been stored. It appears necessary to look for the - prefix key itself. */ + just been stored. It appears necessary to look for a + prefix key itself. Don't look through too many keys for + efficiency reasons. */ - for (i = 0; i < t; ++i) + for (i = 0; i < min (t, 10); ++i) { - if (EQ (keybuf[i], Qmenu_bar)) - break; + if (NUMBERP (keybuf[i]) + || (SYMBOLP (keybuf[i]) + && EQ (Fget (keybuf[i], Qevent_kind), + Qfunction_key))) + goto disable_text_conversion; } - if (i == t) - { - disable_text_conversion (); - record_unwind_protect_void (resume_text_conversion); - disabled_conversion = true; - } + goto replay_key; + + disable_text_conversion: + disable_text_conversion (); + record_unwind_protect_void (resume_text_conversion); + disabled_conversion = true; } #endif @@ -13377,9 +13382,16 @@ syms_of_keyboard (void) DEFVAR_LISP ("post-select-region-hook", Vpost_select_region_hook, doc: /* Abnormal hook run after the region is selected. This usually happens as a result of `select-active-regions'. The hook -is called with one argument, the string that was selected. */);; +is called with one argument, the string that was selected. */); Vpost_select_region_hook = Qnil; + DEFVAR_LISP ("disable-inhibit-text-conversion", + disable_inhibit_text_conversion, + doc: /* Don't disable text conversion inside `read-key-sequence'. +If non-nil, text conversion will continue to happen after a prefix +key has been read inside `read-key-sequence'. */); + disable_inhibit_text_conversion = false; + pdumper_do_now_and_after_load (syms_of_keyboard_for_pdumper); } diff --git a/src/lread.c b/src/lread.c index 1e6e306a851..c29c7ede6ac 100644 --- a/src/lread.c +++ b/src/lread.c @@ -798,7 +798,7 @@ read_emacs_mule_char (int c, int (*readbyte) (int, Lisp_Object), Lisp_Object rea If text conversion is enabled and ASCII_REQUIRED && ERROR_NONASCII, temporarily disable any input method which wants to perform - edits. */ + edits, unless `disable-inhibit-text-conversion'. */ static Lisp_Object read_filtered_event (bool no_switch_frame, bool ascii_required, @@ -821,7 +821,8 @@ read_filtered_event (bool no_switch_frame, bool ascii_required, /* Don't use text conversion when trying to just read a character. */ - if (ascii_required && error_nonascii) + if (ascii_required && error_nonascii + && !disable_inhibit_text_conversion) { disable_text_conversion (); record_unwind_protect_void (resume_text_conversion); diff --git a/src/textconv.c b/src/textconv.c index 91f6e861c60..a4e3116fb68 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -231,8 +231,6 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query, } } - escape: - /* If pos is outside the accessible part of the buffer or if it overflows, move back to point or to the extremes of the accessible region. */ commit aec73dba8f955da8ae5937309a4e7f88a6b1b3c2 Author: Po Lu Date: Wed Mar 15 08:20:59 2023 +0800 Omit gnulib modules added by Android port on MinGW * nt/gnulib-cfg.mk: Omit new gnulib modules. diff --git a/nt/gnulib-cfg.mk b/nt/gnulib-cfg.mk index eca3778f203..d7241976f83 100644 --- a/nt/gnulib-cfg.mk +++ b/nt/gnulib-cfg.mk @@ -44,34 +44,55 @@ OMIT_GNULIB_MODULE_acl-permissions = true OMIT_GNULIB_MODULE_allocator = true OMIT_GNULIB_MODULE_at-internal = true +OMIT_GNULIB_MODULE_canonicalize-lgpl = true OMIT_GNULIB_MODULE_careadlinkat = true OMIT_GNULIB_MODULE_dirent = true OMIT_GNULIB_MODULE_dirfd = true +OMIT_GNULIB_MODULE_fchmodat = true OMIT_GNULIB_MODULE_fcntl = true OMIT_GNULIB_MODULE_fcntl-h = true +OMIT_GNULIB_MODULE_file-has-acl = true +OMIT_GNULIB_MODULE_float = true +OMIT_GNULIB_MODULE_fpucw = true OMIT_GNULIB_MODULE_free-posix = true +OMIT_GNULIB_MODULE_frexp-nolibm = true +OMIT_GNULIB_MODULE_frexpl-nolibm = true +OMIT_GNULIB_MODULE_fseterr = true OMIT_GNULIB_MODULE_fsusage = true +OMIT_GNULIB_MODULE_futimens = true +OMIT_GNULIB_MODULE_getdelim = true +OMIT_GNULIB_MODULE_getline = true OMIT_GNULIB_MODULE_inttypes-incomplete = true +OMIT_GNULIB_MODULE_isnand-nolibm = true +OMIT_GNULIB_MODULE_isnanf-nolibm = true +OMIT_GNULIB_MODULE_isnanl-nolibm = true +OMIT_GNULIB_MODULE_lchmod = true OMIT_GNULIB_MODULE_malloc-posix = true +OMIT_GNULIB_MODULE_math = true +OMIT_GNULIB_MODULE_nanosleep = true +OMIT_GNULIB_MODULE_nproc = true OMIT_GNULIB_MODULE_open = true OMIT_GNULIB_MODULE_pipe2 = true +OMIT_GNULIB_MODULE_printf-frexp = true +OMIT_GNULIB_MODULE_printf-frexpl = true +OMIT_GNULIB_MODULE_printf-posix = true OMIT_GNULIB_MODULE_realloc-gnu = true OMIT_GNULIB_MODULE_realloc-posix = true OMIT_GNULIB_MODULE_secure_getenv = true OMIT_GNULIB_MODULE_signal-h = true +OMIT_GNULIB_MODULE_signbit = true +OMIT_GNULIB_MODULE_size_max = true OMIT_GNULIB_MODULE_stdio = true OMIT_GNULIB_MODULE_stdlib = true +OMIT_GNULIB_MODULE_stpncpy = true OMIT_GNULIB_MODULE_sys_select = true OMIT_GNULIB_MODULE_sys_stat = true OMIT_GNULIB_MODULE_sys_time = true OMIT_GNULIB_MODULE_sys_types = true OMIT_GNULIB_MODULE_unistd = true -OMIT_GNULIB_MODULE_canonicalize-lgpl = true OMIT_GNULIB_MODULE_utimens = true -OMIT_GNULIB_MODULE_fchmodat = true -OMIT_GNULIB_MODULE_lchmod = true -OMIT_GNULIB_MODULE_futimens = true OMIT_GNULIB_MODULE_utimensat = true -OMIT_GNULIB_MODULE_file-has-acl = true -OMIT_GNULIB_MODULE_nproc = true -OMIT_GNULIB_MODULE_nanosleep = true +OMIT_GNULIB_MODULE_vasnprintf = true +OMIT_GNULIB_MODULE_vasprintf = true +OMIT_GNULIB_MODULE_vfprintf-posix = true +OMIT_GNULIB_MODULE_xsize = true commit a39ca9bf8e34e7cf6760e2fa3b7d644bef09ce91 Author: Po Lu Date: Tue Mar 14 19:51:42 2023 +0800 Update Android port * lisp/minibuffer.el (minibuffer-setup-on-screen-keyboard): Handle cases where last-event-frame is a kbd macro. * src/keyboard.c (lispy_function_keys): Remove duplicates. diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index ef0eb1ca108..0055914105d 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -4598,9 +4598,10 @@ minibuffer-setup-on-screen-keyboard (cancel-timer minibuffer-on-screen-keyboard-timer) (setq minibuffer-on-screen-keyboard-timer nil)) (setq minibuffer-on-screen-keyboard-displayed nil) - (when (not (memq (device-class last-event-frame - last-event-device) - '(keyboard core-keyboard))) + (when (and (framep last-event-frame) + (not (memq (device-class last-event-frame + last-event-device) + '(keyboard core-keyboard)))) (setq minibuffer-on-screen-keyboard-displayed (frame-toggle-on-screen-keyboard (selected-frame) nil)))) diff --git a/src/keyboard.c b/src/keyboard.c index e376fa299dd..11c37372db7 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4996,8 +4996,6 @@ #define FUNCTION_KEY_OFFSET 0 [19] = "up", [20] = "down", [213] = "muhenkan", - [213] = "muhenkan", - [214] = "henkan", [214] = "henkan", [215] = "hiragana-katakana", [218] = "kana", commit aa24854e7ffa29ad5f1bb49dc794f9fdf5ef44dd Author: Po Lu Date: Tue Mar 14 15:14:38 2023 +0800 Fix the MS-DOS build * msdos/sed1v2.inp: * msdos/sed3v2.inp: * msdos/sedlibcf.inp: * msdos/sedlibmk.inp: Update for Android port and new Gnulib modules. diff --git a/msdos/sed1v2.inp b/msdos/sed1v2.inp index 162ccb3e8d8..32f1a249f21 100644 --- a/msdos/sed1v2.inp +++ b/msdos/sed1v2.inp @@ -56,6 +56,9 @@ s/ *@LIBPNG@// s/ *@LIBGIF@// s/ *@LIBXPM@// s/ *@WEBP_LIBS@// +/^GIF_CFLAGS *=/s/@GIF_CFLAGS@// +/^JPEG_CFLAGS *=/s/@JPEG_CFLAGS@// +/^TIFF_CFLAGS *=/s/@TIFF_CFLAGS@// /^HAVE_NATIVE_COMP *=/s/@HAVE_NATIVE_COMP@/no/ /^HAVE_PDUMPER *=/s/@HAVE_PDUMPER@/no/ /^HAVE_BE_APP *=/s/@HAVE_BE_APP@/no/ @@ -200,6 +203,14 @@ s/ *@WEBP_LIBS@// /^PAXCTL_dumped *=/s/=.*$/=/ /^PAXCTL_notdumped *=/s/=.*$/=/ /^DUMPING *=/s/@DUMPING@/unexec/ +/^ANDROID_OBJ *=/s/@ANDROID_OBJ@// +/^ANDROID_LIBS *=/s/@ANDROID_LIBS@// +/^ANDROID_LDFLAGS *=/s/@ANDROID_LDFLAGS@// +/^ANDROID_CFLAGS *=/s/@ANDROID_CFLAGS@// +/^LIBGMP_CFLAGS *=/s/@LIBGMP_CFLAGS@// +/^SQLITE3_CFLAGS *=/s/@SQLITE3_CFLAGS@// +/^LIBSELINUX_CFLAGS *=/s/@LIBSELINUX_CFLAGS@// +/^XCONFIGURE *=/s/@XCONFIGURE@// /^[ \t]*MAKE_PDUMPER_FINGERPRINT = *$/c\ MAKE_PDUMPER_FINGERPRINT = /^lisp\.mk:/,/^$/c\ @@ -283,3 +294,4 @@ s| -I\. -I\$(srcdir)| -I.| /^ *test "X/d /\$(CC) -o \$@.tmp/s/\$@.tmp/\$@/ /mv \$@.tmp \$@/d +/^top_builddir =*/s/@top_builddir@/../ diff --git a/msdos/sed3v2.inp b/msdos/sed3v2.inp index 9688a27b066..0699fb68b02 100644 --- a/msdos/sed3v2.inp +++ b/msdos/sed3v2.inp @@ -57,3 +57,4 @@ /^GETOPT_H *=/s!@GETOPT_H@!getopt.h! /^GETOPTOBJS *=/s!@GETOPTOBJS@!getopt.o getopt1.o! /^INSTALLABLES/s/emacsclient[^ ]* *// +/^XCONFIGURE *=/s/@XCONFIGURE@// diff --git a/msdos/sedlibcf.inp b/msdos/sedlibcf.inp index 931ceb8f044..8966e799a38 100644 --- a/msdos/sedlibcf.inp +++ b/msdos/sedlibcf.inp @@ -20,3 +20,4 @@ # ---------------------------------------------------------------------- s/c++defs/cxxdefs/g s/\([a-zA-Z0-9_]*\)\.in\.h/\1.in-h/g +/^XCONFIGURE *=/s/@XCONFIGURE@// diff --git a/msdos/sedlibmk.inp b/msdos/sedlibmk.inp index c3f410bd74d..81b2ac44497 100644 --- a/msdos/sedlibmk.inp +++ b/msdos/sedlibmk.inp @@ -156,6 +156,7 @@ s/@PACKAGE@/emacs/ /^HYBRID_MALLOC *=/s/@HYBRID_MALLOC@// /^WARN_CFLAGS *=/s/@WARN_CFLAGS@// /^WERROR_CFLAGS *=/s/@WERROR_CFLAGS@// +/^ANDROID_CFLAGS *=/s/@ANDROID_CFLAGS@// /^DEFS *=/s/@[^@\n]*@// /^DEPDIR *=/s/@[^@\n]*@/deps/ /^ECHO_N *=/s/@[^@\n]*@/-n/ @@ -299,8 +300,10 @@ s/@PACKAGE@/emacs/ /^NEXT_DIRENT_H *=/s/@[^@\n]*@// /^NEXT_ERRNO_H *=/s/@[^@\n]*@// /^NEXT_FCNTL_H *=/s/@[^@\n]*@// +/^NEXT_FLOAT_H *=/s/@[^@\n]*@// /^NEXT_GETOPT_H *=/s/@[^@\n]*@// /^NEXT_LIMITS_H *=/s/@[^@\n]*@// +/^NEXT_MATH_H *=/s/@[^@\n]*@// /^NEXT_SIGNAL_H *=/s/@[^@\n]*@// /^NEXT_STDDEF_H *=/s/@[^@\n]*@// /^NEXT_STDIO_H *=/s/@[^@\n]*@// @@ -309,9 +312,11 @@ s/@PACKAGE@/emacs/ /^NEXT_STRING_H *=/s/@[^@\n]*@// /^NEXT_SYS_SELECT_H *=/s/@[^@\n]*@// /^NEXT_SYS_STAT_H *=/s!@[^@\n]*@!! +/^NEXT_SYS_RANDOM_H *=/s/@[^@\n]*@// /^NEXT_SYS_TIME_H *=/s/@[^@\n]*@// /^NEXT_SYS_TYPES_H *=/s!@[^@\n]*@!! /^NEXT_TIME_H *=/s/@[^@\n]*@// +/^NEXT_INTTYPES_H *=/s/@[^@\n]*@// /^NEXT_UNISTD_H *=/s/@[^@\n]*@// /^OBJEXT *=/s/@[^@\n]*@/o/ /^PRAGMA_COLUMNS *=/s/@[^@\n]*@// @@ -331,6 +336,7 @@ s/@PACKAGE@/emacs/ /^DIRENT_H *=/s/@[^@\n]*@// /^ERRNO_H *=/s/@[^@\n]*@// /^EXECINFO_H *=/s/@[^@\n]*@/execinfo.h/ +/^FLOAT_H *=/s/@[^@\n]*@// /^GETOPT_CDEFS_H *=/s/@[^@\n]*@/getopt-cdefs.h/ /^GMP_H *=/s/@[^@\n]*@/gmp.h/ /^LIMITS_H *=/s/@[^@\n]*@/limits.h/ @@ -427,7 +433,7 @@ s/= @GL_GENERATE_STDDEF_H_CONDITION@/= 1/ s/= @GL_GENERATE_STDINT_H_CONDITION@/= 1/ s/= @GL_GENERATE_LIMITS_H_CONDITION@/= 1/ s/= @GL_GENERATE_ERRNO_H_CONDITION@/= / -s/= @GL_GENERATE_LIMITS_H_CONDITION@/= / +s/= @GL_GENERATE_FLOAT_H_CONDITION@/= / s/= @GL_GENERATE_GETOPT_CDEFS_H_CONDITION@/= 1/ s/= @GL_GENERATE_GETOPT_H_CONDITION@/= 1/ s/= @GL_GENERATE_GMP_H_CONDITION@/= 1/ @@ -436,6 +442,8 @@ s/= @GL_GENERATE_MINI_GMP_H_CONDITION@/= 1/ s/= @GL_GENERATE_STDCKDINT_H_CONDITION@/= 1/ s/= @GL_COND_OBJ_STDIO_READ_CONDITION@/= / s/= @GL_COND_OBJ_STDIO_WRITE_CONDITION@/= / +s/= @GL_COND_OBJ_STPNCPY_CONDITION@/= / +s/= @GL_COND_OBJ_.*@/= 1/ s/\$\(MKDIR_P\) malloc// # # Determine which modules to build and which to omit @@ -453,8 +461,11 @@ OMIT_GNULIB_MODULE_euidaccess = true\ OMIT_GNULIB_MODULE_faccessat = true\ OMIT_GNULIB_MODULE_fcntl = true\ OMIT_GNULIB_MODULE_fdopendir = true\ +OMIT_GNULIB_MODULE_float = true\ OMIT_GNULIB_MODULE_fstatat = true\ OMIT_GNULIB_MODULE_fsync = true\ +OMIT_GNULIB_MODULE_getline = true\ +OMIT_GNULIB_MODULE_getdelim = true\ OMIT_GNULIB_MODULE_getdtablesize = true\ OMIT_GNULIB_MODULE_getgroups = true\ OMIT_GNULIB_MODULE_gettimeofday = true\ @@ -462,20 +473,25 @@ OMIT_GNULIB_MODULE_group-member = true\ OMIT_GNULIB_MODULE_inttypes-incomplete = true\ OMIT_GNULIB_MODULE_localtime-buffer = true\ OMIT_GNULIB_MODULE_lstat = true\ +OMIT_GNULIB_MODULE_math = true\ OMIT_GNULIB_MODULE_nanosleep = true\ OMIT_GNULIB_MODULE_open = true\ OMIT_GNULIB_MODULE_pipe2 = true\ +OMIT_GNULIB_MODULE_printf-posix = true\ +OMIT_GNULIB_MODULE_printf-frexpl = true\ OMIT_GNULIB_MODULE_pselect = true\ OMIT_GNULIB_MODULE_putenv = true\ OMIT_GNULIB_MODULE_qcopy-acl = true\ OMIT_GNULIB_MODULE_readlink = true\ OMIT_GNULIB_MODULE_readlinkat = true\ +OMIT_GNULIB_MODULE_stpcpy = true\ OMIT_GNULIB_MODULE_strtoimax = true\ OMIT_GNULIB_MODULE_strtoll = true\ OMIT_GNULIB_MODULE_symlink = true\ OMIT_GNULIB_MODULE_sys_select = true\ OMIT_GNULIB_MODULE_sys_time = true\ -OMIT_GNULIB_MODULE_crypto\/md5 = true +OMIT_GNULIB_MODULE_crypto\/md5 = true\ +OMIT_GNULIB_MODULE_vprintf-posix = true /^arg-nonnull\.h:/,/^[ ][ ]*mv /c\ arg-nonnull.h: $(top_srcdir)/build-aux/snippet/arg-nonnull.h\ sed -n -e '/GL_ARG_NONNULL/,$$p' < $(top_srcdir)/build-aux/snippet/arg-nonnull.h > $@ commit d6bddca26c7cf827e098ae783e865fcbcdd48799 Author: Po Lu Date: Tue Mar 14 13:19:01 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsWindow.java (figureChange): Detect mice on up events as well. (onSomeKindOfMotionEvent): Work past framework bug. * src/androidterm.c (android_perform_conversion_query): * src/textconv.c (textconv_query): * src/textconv.h (TEXTCONV_SKIP_ACTIVE_REGION): Remove unused code. diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index d786c104153..a8d1beedef7 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -755,6 +755,14 @@ private static class Coordinate break; case MotionEvent.ACTION_UP: + + /* Detect mice. If this is a mouse event, give it to + onSomeKindOfMotionEvent. */ + if ((Build.VERSION.SDK_INT + >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) + && event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE) + return -2; + /* Primary pointer released with index 0. */ pointerID = event.getPointerId (0); pointerMap.remove (pointerID); @@ -916,6 +924,7 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) EmacsNative.sendLeaveNotify (this.handle, (int) event.getX (), (int) event.getY (), event.getEventTime ()); + return true; case MotionEvent.ACTION_BUTTON_PRESS: @@ -949,13 +958,35 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) return true; case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_UP: /* Emacs must return true even though touch events are not handled here, because the value of this function is used by the system to decide whether or not Emacs gets ACTION_MOVE events. */ return true; + case MotionEvent.ACTION_UP: + /* However, if ACTION_UP reports a different button state from + the last known state, look up which button was released and + send a ButtonRelease event; this is to work around a bug in + the framework where real ACTION_BUTTON_RELEASE events are + not delivered. */ + + if (Build.VERSION.SDK_INT + < Build.VERSION_CODES.ICE_CREAM_SANDWICH) + return true; + + if (event.getButtonState () == 0 && lastButtonState != 0) + { + EmacsNative.sendButtonRelease (this.handle, (int) event.getX (), + (int) event.getY (), + event.getEventTime (), + lastModifiers, + whatButtonWasIt (event, false)); + lastButtonState = event.getButtonState (); + } + + return true; + case MotionEvent.ACTION_SCROLL: /* Send a scroll event with the specified deltas. */ EmacsNative.sendWheel (this.handle, (int) event.getX (), diff --git a/src/androidterm.c b/src/androidterm.c index 72d81744128..0110c4b6dd8 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -4685,8 +4685,6 @@ NATIVE_NAME (finishComposingText) (JNIEnv *env, jobject object, /* Obtain the text from the frame whose window is that specified in DATA using the text conversion query specified there. - Adjust the query position to skip over any active composing region. - Set ((struct android_conversion_query_context *) DATA)->success on success. */ @@ -4704,7 +4702,7 @@ android_perform_conversion_query (void *data) if (!f) return; - textconv_query (f, &context->query, TEXTCONV_SKIP_ACTIVE_REGION); + textconv_query (f, &context->query, 0); /* context->query.text will have been set even if textconv_query returns 1. */ diff --git a/src/textconv.c b/src/textconv.c index 900277016f2..91f6e861c60 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -147,9 +147,6 @@ select_window (Lisp_Object window, Lisp_Object norecord) If FLAGS & TEXTCONV_SKIP_CONVERSION_REGION, then first move PT past the conversion region in the specified direction if it is inside. - If FLAGS & TEXTCONV_SKIP_ACTIVE_REGION, then also move PT past the - region if the mark is active. - Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION or if deleting the text was successful, and 1 otherwise. */ @@ -234,37 +231,6 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query, } } - /* Likewise for the region if the mark is active. */ - - if (flags & TEXTCONV_SKIP_ACTIVE_REGION) - { - temp = mark; - - if (temp == -1) - goto escape; - - start = min (temp, PT); - end = max (temp, PT); - - if (pos >= start && pos < end) - { - switch (query->direction) - { - case TEXTCONV_FORWARD_CHAR: - case TEXTCONV_FORWARD_WORD: - case TEXTCONV_CARET_DOWN: - case TEXTCONV_NEXT_LINE: - case TEXTCONV_LINE_START: - pos = end; - break; - - default: - pos = max (BEGV, start); - break; - } - } - } - escape: /* If pos is outside the accessible part of the buffer or if it diff --git a/src/textconv.h b/src/textconv.h index 9bc9e7d9bd1..6abca97bc52 100644 --- a/src/textconv.h +++ b/src/textconv.h @@ -119,7 +119,6 @@ Copyright (C) 2023 Free Software Foundation, Inc. }; #define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0) -#define TEXTCONV_SKIP_ACTIVE_REGION (1 << 1) extern int textconv_query (struct frame *, struct textconv_callback_struct *, int); commit 5964051fcefc52e02c88a41b91797cc7a785d550 Author: Po Lu Date: Tue Mar 14 09:48:02 2023 +0800 Update Android port * doc/emacs/android.texi (Android Windowing): Document how to display dialogs when Emacs is in the background. * java/org/gnu/emacs/EmacsDialog.java (display1): Use system dialogs if possible. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 15415f12570..c92700980de 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -507,12 +507,25 @@ Android Windowing @cindex volume/multimedia buttons, Android The volume keys are normally reserved by Emacs and used to provide the ability to quit Emacs without a physical keyboard -(@pxref{On-Screen-Keyboards}.) However, if you want them to adjust +(@pxref{On-Screen Keyboards}.) However, if you want them to adjust the volume instead, you can set the variable @code{android-pass-multimedia-buttons-to-system} to a non-@code{nil} value; note that you will no longer be able to quit Emacs using the volume buttons in that case. +@cindex dialog boxes, android + Emacs is unable to display dialog boxes (@pxref{Dialog Boxes}) while +it does not have the input focus on Android 6.0 or later. If this is +important to you, this ability can be restored by granting Emacs +permission to display over other programs. Normally, this can be done +from the: + +@indentedblock +System -> Apps -> Emacs -> More -> Display over other apps +@end indentedblock + +menu in the system settings, but this procedure may vary by device. + @node Android Fonts @section Font backends and selection under Android @cindex fonts, android diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java index aed84de29e5..de5a37bd5c5 100644 --- a/java/org/gnu/emacs/EmacsDialog.java +++ b/java/org/gnu/emacs/EmacsDialog.java @@ -23,8 +23,14 @@ import java.util.ArrayList; import android.app.AlertDialog; -import android.content.DialogInterface; + import android.content.Context; +import android.content.DialogInterface; + +import android.os.Build; + +import android.provider.Settings; + import android.util.Log; import android.widget.Button; @@ -33,6 +39,8 @@ import android.view.View; import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; /* Toolkit dialog implementation. This object is built from JNI and describes a single alert dialog. Then, `inflate' turns it into @@ -225,33 +233,54 @@ private class EmacsButton implements View.OnClickListener, /* Internal helper for display run on the main thread. */ + @SuppressWarnings("deprecation") private boolean display1 () { - EmacsActivity activity; - int size; + Context context; + int size, type; Button buttonView; EmacsButton button; AlertDialog dialog; + Window window; + + /* First, try to display a dialog using the service context. */ - if (EmacsActivity.focusedActivities.isEmpty ()) + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M + || Settings.canDrawOverlays (EmacsService.SERVICE)) + context = EmacsService.SERVICE; + else if (EmacsActivity.focusedActivities.isEmpty ()) { /* If focusedActivities is empty then this dialog may have been displayed immediately after a popup dialog is dismissed. */ - activity = EmacsActivity.lastFocusedActivity; + context = EmacsActivity.lastFocusedActivity; - if (activity == null) + if (context == null) return false; } else - activity = EmacsActivity.focusedActivities.get (0); + context = EmacsActivity.focusedActivities.get (0); + + Log.d (TAG, "display1: using context " + context); - dialog = dismissDialog = toAlertDialog (activity); + dialog = dismissDialog = toAlertDialog (context); try { + if (context == EmacsService.SERVICE) + { + /* Apply the system alert window type to make sure this + dialog can be displayed. */ + + window = dialog.getWindow (); + type = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY + : WindowManager.LayoutParams.TYPE_PHONE); + window.setType (type); + } + dismissDialog.show (); } catch (Exception exception) commit e0417a4577134f7705d9112bbd8af1e7916b5504 Merge: d31f557d4c7 3fb30c8f133 Author: Po Lu Date: Tue Mar 14 09:01:48 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit d31f557d4c7725a9fb81c976701426baf5404786 Author: Po Lu Date: Mon Mar 13 20:39:29 2023 +0800 Update Android port * etc/NEWS: Announce new option. * lisp/menu-bar.el (menu-bar-close-window): New option. (kill-this-buffer): (kill-this-buffer-enabled-p): Adjust accordingly. * src/keyboard.c (lispy_function_keys): Add more silly keys. diff --git a/etc/NEWS b/etc/NEWS index 2de8fb885a6..bd7d9522ad7 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -47,6 +47,12 @@ compositing manager, Emacs will now redisplay such a frame even though 'frame-visible-p' returns nil or 'icon' for it. This can happen, for example, as part of preview for iconified frames. +--- +** New user option 'menu-bar-close-window'. +When non-nil, selecting Close from the File menu or clicking Close in +the tool bar will result in the current window being closed, if +possible. + +++ ** 'write-region-inhibit-fsync' now defaults to t in interactive mode, as it has in batch mode since Emacs 24. diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index 2d907fb3827..949d805465d 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el @@ -79,6 +79,14 @@ menu-bar-print-menu :help "Print current buffer with page headings")) menu)) +(defcustom menu-bar-close-window nil + "Whether or not to close the current window from the menu bar. +If non-nil, selecting Close from the File menu or clicking Close +in the tool bar will close the current window where possible." + :type 'boolean + :group 'menu + :version "30.1") + (defvar menu-bar-file-menu (let ((menu (make-sparse-keymap "File"))) @@ -2168,12 +2176,19 @@ kill-this-buffer ;; (Bug#8184). ((not (menu-bar-menu-frame-live-and-visible-p))) ((menu-bar-non-minibuffer-window-p) - (kill-buffer (current-buffer))) + (kill-buffer (current-buffer)) + ;; Also close the current window if `menu-bar-close-windows' is + ;; set. + (when menu-bar-close-window + (ignore-errors (delete-window)))) (t (abort-recursive-edit)))) (defun kill-this-buffer-enabled-p () - "Return non-nil if the `kill-this-buffer' menu item should be enabled." + "Return non-nil if the `kill-this-buffer' menu item should be enabled. +It should be enabled there is at least one non-hidden buffer, or if +`menu-bar-close-window' is non-nil and there is more than one window on +this frame." (or (not (menu-bar-non-minibuffer-window-p)) (let (found-1) ;; Instead of looping over entire buffer list, stop once we've @@ -2183,7 +2198,9 @@ kill-this-buffer-enabled-p (unless (string-match-p "^ " (buffer-name buffer)) (if (not found-1) (setq found-1 t) - (throw 'found-2 t)))))))) + (throw 'found-2 t)))))) + (and menu-bar-close-window + (window-parent (selected-window))))) (put 'dired 'menu-enable '(menu-bar-non-minibuffer-window-p)) diff --git a/src/keyboard.c b/src/keyboard.c index 0a74b435f0e..e376fa299dd 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4971,11 +4971,14 @@ #define FUNCTION_KEY_OFFSET 0 function keys that Emacs recognizes. */ [111] = "escape", [112] = "delete", - [121] = "break", [120] = "sysrq", + [121] = "break", [122] = "home", [123] = "end", [124] = "insert", + [126] = "media-play", + [127] = "media-pause", + [130] = "media-record", [131] = "f1", [132] = "f2", [133] = "f3", @@ -4993,18 +4996,22 @@ #define FUNCTION_KEY_OFFSET 0 [19] = "up", [20] = "down", [213] = "muhenkan", + [213] = "muhenkan", + [214] = "henkan", [214] = "henkan", [215] = "hiragana-katakana", [218] = "kana", [21] = "left", [22] = "right", [24] = "volume-up", - [25] = "volume-down", [259] = "help", + [25] = "volume-down", [268] = "kp-up-left", [269] = "kp-down-left", [270] = "kp-up-right", [271] = "kp-down-right", + [272] = "media-skip-forward", + [273] = "media-skip-backward", [277] = "cut", [278] = "copy", [279] = "paste", @@ -5015,6 +5022,11 @@ #define FUNCTION_KEY_OFFSET 0 [67] = "backspace", [82] = "menu", [84] = "find", + [85] = "media-play-pause", + [86] = "media-stop", + [87] = "media-next", + [88] = "media-previous", + [89] = "media-rewind", [92] = "prior", [93] = "next", }; commit e6186b6a2a452f555ab024d1fc770683561ac024 Author: Po Lu Date: Mon Mar 13 18:31:30 2023 +0800 Update Android port * src/android.c (android_check_string, android_build_string): Reduce consing when building menu bar strings. diff --git a/src/android.c b/src/android.c index 16c645ced1e..efbf8fc95cc 100644 --- a/src/android.c +++ b/src/android.c @@ -5389,6 +5389,23 @@ emacs_abort (void) +/* Return whether or not TEXT, a string without multibyte + characters, has no bytes with the 8th bit set. */ + +static bool +android_check_string (Lisp_Object text) +{ + ptrdiff_t i; + + for (i = 0; i < ASIZE (text); ++i) + { + if (SREF (text, i) & 128) + return false; + } + + return true; +} + /* Given a Lisp string TEXT, return a local reference to an equivalent Java string. */ @@ -5401,6 +5418,21 @@ android_build_string (Lisp_Object text) jchar *characters; USE_SAFE_ALLOCA; + /* Directly encode TEXT if it contains no multibyte + characters. This is okay because the Java extended UTF + format is compatible with ASCII. */ + + if (SBYTES (text) == SCHARS (text) + && android_check_string (text)) + { + string = (*android_java_env)->NewStringUTF (android_java_env, + SSDATA (text)); + android_exception_check (); + SAFE_FREE (); + + return string; + } + encoded = code_convert_string_norecord (text, Qutf_16le, true); nchars = (SBYTES (encoded) / sizeof (jchar)); commit d3bb4b668755ae257bc00006d0806122cf7d7bf1 Author: Po Lu Date: Mon Mar 13 15:53:39 2023 +0800 ; * etc/MACHINES (Android): Update with more recent information. diff --git a/etc/MACHINES b/etc/MACHINES index 8ba0b1faa77..c9fdc6cea96 100644 --- a/etc/MACHINES +++ b/etc/MACHINES @@ -133,9 +133,13 @@ the list at the end of this file. ** Android - Emacs is known to run on all Android versions from 2.3 onwards. - It should work with Android 2.2 as well, but only the build - has been tested, and actually running the built Emacs has not. + Emacs is known to run on all Android versions from 2.2 onwards, on + Linux kernel 2.26.29 or later. + + Android 2.2 has only been tested on ARM. mips64 has not been + tested, but builds. With these exceptions, Emacs is known to run on + all supported versions of Android on all supported machines: arm, + armv7, arm64, x86, x86_64, and mips. See the file INSTALL.android for detailed installation instructions. commit b776feb7f2737fb6b3fca05ae3b786dc67a2a9ae Author: Po Lu Date: Mon Mar 13 13:25:02 2023 +0800 Update Android port * doc/emacs/android.texi (Android Startup): Document changes to emacsclient wrapper. * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity) (startEmacsClient): Open EmacsActivity if the service is not running. * java/org/gnu/emacs/EmacsService.java (onCreate): * java/org/gnu/emacs/EmacsThread.java (EmacsThread, run): Pass any file to open to Emacs. * lisp/term/android-win.el (handle-args-function): Implement. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index d50acda7710..15415f12570 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -133,6 +133,11 @@ Android Startup and the name of the file being opened. Then, upon success, the focus is transferred to any open Emacs frame. + However, if Emacs is not running at the time the wrapper is opened, +it starts Emacs and gives it the file to open as an argument. Note +that if that Emacs in turn does not start the Emacs server, subsequent +attempts to open the file with the wrapper will fail. + @cindex /content directory, android Some files are given to Emacs as ``content identifiers'', which the system provides access to outside the normal filesystem APIs. Emacs diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index e8fb24d53d8..f402e25c7fb 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -72,6 +72,7 @@ public final class EmacsOpenActivity extends Activity DialogInterface.OnCancelListener { private static final String TAG = "EmacsOpenActivity"; + public static String fileToOpen; private class EmacsClientThread extends Thread { @@ -317,6 +318,20 @@ private class EmacsClientThread extends Thread Process process; EmacsClientThread thread; File file; + Intent intent; + + /* If the Emacs service is not running, then start Emacs and make + it open this file. */ + + if (EmacsService.SERVICE == null) + { + fileToOpen = fileName; + intent = new Intent (EmacsOpenActivity.this, + EmacsActivity.class); + finish (); + startActivity (intent); + return; + } libDir = EmacsService.getLibraryDirectory (this); builder = new ProcessBuilder (libDir + "/libemacsclient.so", diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 9c48c56ca26..33436892caa 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -215,7 +215,8 @@ invocation of app_process (through android-emacs) can classPath = getApkFile (); Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir - + ", libDir = " + libDir + ", and classPath = " + classPath); + + ", libDir = " + libDir + ", and classPath = " + classPath + + "; fileToOpen = " + EmacsOpenActivity.fileToOpen); /* Start the thread that runs Emacs. */ thread = new EmacsThread (this, new Runnable () { @@ -228,7 +229,9 @@ invocation of app_process (through android-emacs) can (float) pixelDensityY, classPath, EmacsService.this); } - }, needDashQ); + }, needDashQ, + /* If any file needs to be opened, open it now. */ + EmacsOpenActivity.fileToOpen); thread.start (); } catch (IOException exception) diff --git a/java/org/gnu/emacs/EmacsThread.java b/java/org/gnu/emacs/EmacsThread.java index 30484710651..d175fe332b5 100644 --- a/java/org/gnu/emacs/EmacsThread.java +++ b/java/org/gnu/emacs/EmacsThread.java @@ -20,24 +20,32 @@ package org.gnu.emacs; import java.lang.Thread; +import java.util.Arrays; import android.os.Build; +import android.util.Log; public class EmacsThread extends Thread { + private static final String TAG = "EmacsThread"; + /* Whether or not Emacs should be started -Q. */ private boolean startDashQ; /* Runnable run to initialize Emacs. */ private Runnable paramsClosure; + /* Whether or not to open a file after starting Emacs. */ + private String fileToOpen; + public EmacsThread (EmacsService service, Runnable paramsClosure, - boolean startDashQ) + boolean startDashQ, String fileToOpen) { super ("Emacs main thread"); this.startDashQ = startDashQ; this.paramsClosure = paramsClosure; + this.fileToOpen = fileToOpen; } @Override @@ -46,14 +54,27 @@ public class EmacsThread extends Thread { String args[]; - if (!startDashQ) - args = new String[] { "libandroid-emacs.so", }; + if (fileToOpen == null) + { + if (!startDashQ) + args = new String[] { "libandroid-emacs.so", }; + else + args = new String[] { "libandroid-emacs.so", "-Q", }; + } else - args = new String[] { "libandroid-emacs.so", "-Q", }; + { + if (!startDashQ) + args = new String[] { "libandroid-emacs.so", + fileToOpen, }; + else + args = new String[] { "libandroid-emacs.so", "-Q", + fileToOpen, }; + } paramsClosure.run (); /* Run the native code now. */ + Log.d (TAG, "run: " + Arrays.toString (args)); EmacsNative.initEmacs (args, EmacsApplication.dumpFileName, Build.VERSION.SDK_INT); } diff --git a/lisp/term/android-win.el b/lisp/term/android-win.el index 94fe4d6489b..fc393681ac4 100644 --- a/lisp/term/android-win.el +++ b/lisp/term/android-win.el @@ -56,10 +56,10 @@ window-system-initialization (cl-defmethod frame-creation-function (params &context (window-system android)) (x-create-frame-with-faces params)) -(cl-defmethod handle-args-function (_ignored &context (window-system android)) - ;; Nothing to do here: Android has no command line to provide - ;; arguments on. - (ignore)) +(cl-defmethod handle-args-function (args &context (window-system android)) + ;; Android has no command line to provide arguments on. + ;; However, call x-handle-args to handle file name args. + (x-handle-args args)) ;;; Selection support. commit c3524b15aa77b309f325fcb806fe9e9c91c4e99e Author: Po Lu Date: Mon Mar 13 09:58:52 2023 +0800 Update Android port * src/image.c (image_create_bitmap_from_file, image_find_image_fd) (close_android_fd, slurp_file): Return and use `struct android_fd_or_asset' on Android. (xbm_load, xpm_load, pbm_load, png_load_body, jpeg_load_body) (webp_load, svg_load): Adjust accordingly. diff --git a/src/image.c b/src/image.c index a244b23ecd2..dc45a2d2419 100644 --- a/src/image.c +++ b/src/image.c @@ -652,9 +652,19 @@ image_create_bitmap_from_data (struct frame *f, char *bits, return id; } +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY +#include "android.h" + +/* This abstraction allows directly loading images from assets without + copying them to a file descriptor first. */ +typedef struct android_fd_or_asset image_fd; +#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ +typedef int image_fd; +#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */ + #if defined HAVE_HAIKU || defined HAVE_NS || defined HAVE_ANDROID -static char *slurp_file (int, ptrdiff_t *); -static Lisp_Object image_find_image_fd (Lisp_Object, int *); +static char *slurp_file (image_fd, ptrdiff_t *); +static Lisp_Object image_find_image_fd (Lisp_Object, image_fd *); static bool xbm_read_bitmap_data (struct frame *, char *, char *, int *, int *, char **, bool); #endif @@ -877,7 +887,8 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file) emacs_abort (); #else ptrdiff_t id, size; - int fd, width, height, rc; + int width, height, rc; + image_fd fd; char *contents, *data; Lisp_Object found; android_pixmap bitmap; @@ -4135,10 +4146,11 @@ image_unget_x_image (struct image *img, bool mask_p, Emacs_Pix_Container ximg) PFD is null, do not open the file. */ static Lisp_Object -image_find_image_fd (Lisp_Object file, int *pfd) +image_find_image_fd (Lisp_Object file, image_fd *pfd) { Lisp_Object file_found, search_path; int fd; + void *platform; /* TODO I think this should use something like image-load-path instead. Unfortunately, that can contain non-string elements. */ @@ -4147,9 +4159,10 @@ image_find_image_fd (Lisp_Object file, int *pfd) Vx_bitmap_file_path); /* Try to find FILE in data-directory/images, then x-bitmap-file-path. */ + platform = NULL; fd = openp (search_path, file, Qnil, &file_found, pfd ? Qt : make_fixnum (R_OK), false, false, - NULL); + pfd ? &platform : NULL); if (fd == -2) { /* The file exists locally, but has a file name handler. @@ -4159,10 +4172,23 @@ image_find_image_fd (Lisp_Object file, int *pfd) Lisp_Object encoded_name = ENCODE_FILE (file_found); fd = emacs_open (SSDATA (encoded_name), O_RDONLY, 0); } - else if (fd < 0) + /* FD is -3 if PLATFORM is set to a valid asset file descriptor on + Android. */ + else if (fd < 0 && fd != -3) return Qnil; + +#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY if (pfd) *pfd = fd; +#else + /* Construct an asset file descriptor. */ + + if (pfd) + { + pfd->fd = fd; + pfd->asset = platform; + } +#endif return file_found; } @@ -4176,14 +4202,25 @@ image_find_image_file (Lisp_Object file) return image_find_image_fd (file, 0); } +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + +static void +close_android_fd (void *ptr) +{ + android_close_asset (*(struct android_fd_or_asset *) ptr); +} + +#endif + /* Read FILE into memory. Value is a pointer to a buffer allocated with xmalloc holding FILE's contents. Value is null if an error occurred. FD is a file descriptor open for reading FILE. Set *SIZE to the size of the file. */ static char * -slurp_file (int fd, ptrdiff_t *size) +slurp_file (image_fd fd, ptrdiff_t *size) { +#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY FILE *fp = fdopen (fd, "rb"); char *buf = NULL; @@ -4212,6 +4249,39 @@ slurp_file (int fd, ptrdiff_t *size) unbind_to (count, Qnil); } +#else + char *buf; + struct stat st; + specpdl_ref count; + + if (!android_asset_fstat (fd, &st) + && (0 <= st.st_size + && st.st_size < min (PTRDIFF_MAX, SIZE_MAX))) + { + count = SPECPDL_INDEX (); + record_unwind_protect_ptr (close_android_fd, &fd); + buf = xmalloc (st.st_size + 1); + + /* Read one byte past the end of the file. That allows + detecting if the file grows as it is being read. */ + + if (android_asset_read (fd, buf, + st.st_size + 1) == st.st_size) + *size = st.st_size; + else + { + xfree (buf); + buf = NULL; + } + + unbind_to (count, Qnil); + } + else + { + buf = NULL; + android_close_asset (fd); + } +#endif return buf; } @@ -4934,7 +5004,7 @@ xbm_load (struct frame *f, struct image *img) file_name = image_spec_value (img->spec, QCfile, NULL); if (STRINGP (file_name)) { - int fd; + image_fd fd; Lisp_Object file = image_find_image_fd (file_name, &fd); if (!STRINGP (file)) { @@ -6230,7 +6300,7 @@ xpm_load (struct frame *f, file_name = image_spec_value (img->spec, QCfile, NULL); if (STRINGP (file_name)) { - int fd; + image_fd fd; Lisp_Object file = image_find_image_fd (file_name, &fd); if (!STRINGP (file)) { @@ -7259,7 +7329,7 @@ pbm_load (struct frame *f, struct image *img) if (STRINGP (specified_file)) { - int fd; + image_fd fd; Lisp_Object file = image_find_image_fd (specified_file, &fd); if (!STRINGP (file)) { @@ -7927,8 +7997,11 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c) if (NILP (specified_data)) { int fd; - Lisp_Object file = image_find_image_fd (specified_file, &fd); - if (!STRINGP (file)) + Lisp_Object file = image_find_image_file (specified_file); + + if (!STRINGP (file) + || (fd = emacs_open (SSDATA (ENCODE_FILE (file)), + O_RDONLY, 0)) < 0) { image_error ("Cannot find image file `%s'", specified_file); return 0; @@ -8655,8 +8728,10 @@ jpeg_load_body (struct frame *f, struct image *img, if (NILP (specified_data)) { int fd; - Lisp_Object file = image_find_image_fd (specified_file, &fd); - if (!STRINGP (file)) + Lisp_Object file = image_find_image_file (specified_file); + if (!STRINGP (file) + || (fd = emacs_open (SSDATA (ENCODE_FILE (file)), + O_RDONLY, 0)) < 0) { image_error ("Cannot find image file `%s'", specified_file); return 0; @@ -10153,7 +10228,7 @@ webp_load (struct frame *f, struct image *img) if (NILP (specified_data)) { - int fd; + image_fd fd; file = image_find_image_fd (specified_file, &fd); if (!STRINGP (file)) { @@ -11552,7 +11627,7 @@ svg_load (struct frame *f, struct image *img) base_uri = image_spec_value (img->spec, QCbase_uri, NULL); if (STRINGP (file_name)) { - int fd; + image_fd fd; Lisp_Object file = image_find_image_fd (file_name, &fd); if (!STRINGP (file)) { commit a517c24697d080475e2d531c8ce1d433aa44a9c6 Merge: 08a3749794b 75f04848a65 Author: Po Lu Date: Mon Mar 13 07:52:08 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 08a3749794b8e6c1b3db882ee15e3e91f3700414 Author: Po Lu Date: Sun Mar 12 20:53:34 2023 +0800 Update Android port * src/android.c (android_get_screen_width): (android_get_screen_height): (android_get_mm_width): (android_get_mm_height): (android_detect_mouse): Correctly handle Java exceptions. diff --git a/src/android.c b/src/android.c index a2c239736a7..16c645ced1e 100644 --- a/src/android.c +++ b/src/android.c @@ -4880,45 +4880,80 @@ android_damage_window (android_drawable handle, int android_get_screen_width (void) { - return (*android_java_env)->CallIntMethod (android_java_env, - emacs_service, - service_class.get_screen_width, - (jboolean) false); + int rc; + jmethodID method; + + method = service_class.get_screen_width; + rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env, + emacs_service, + service_class.class, + method, + (jboolean) false); + android_exception_check (); + return rc; } int android_get_screen_height (void) { - return (*android_java_env)->CallIntMethod (android_java_env, - emacs_service, - service_class.get_screen_height, - (jboolean) false); + int rc; + jmethodID method; + + method = service_class.get_screen_height; + rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env, + emacs_service, + service_class.class, + method, + (jboolean) false); + android_exception_check (); + return rc; } int android_get_mm_width (void) { - return (*android_java_env)->CallIntMethod (android_java_env, - emacs_service, - service_class.get_screen_width, - (jboolean) true); + int rc; + jmethodID method; + + method = service_class.get_screen_width; + rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env, + emacs_service, + service_class.class, + method, + (jboolean) true); + android_exception_check (); + return rc; } int android_get_mm_height (void) { - return (*android_java_env)->CallIntMethod (android_java_env, - emacs_service, - service_class.get_screen_height, - (jboolean) true); + int rc; + jmethodID method; + + method = service_class.get_screen_height; + rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env, + emacs_service, + service_class.class, + method, + (jboolean) true); + android_exception_check (); + return rc; } bool android_detect_mouse (void) { - return (*android_java_env)->CallBooleanMethod (android_java_env, - emacs_service, - service_class.detect_mouse); + bool rc; + jmethodID method; + + method = service_class.detect_mouse; + rc = (*android_java_env)->CallNonvirtualBooleanMethod (android_java_env, + emacs_service, + service_class.class, + method); + android_exception_check (); + return rc; } void commit 90c22cb94965efff001968b9e84239c08cf14869 Merge: 776f1ca3e35 9191fd50d24 Author: Po Lu Date: Sun Mar 12 19:37:00 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 776f1ca3e3551b98569ab7daa58cd6921048881e Author: Po Lu Date: Sun Mar 12 19:36:09 2023 +0800 Update Android port * src/android.c (android_check_if_event): * src/androidgui.h: New function. * src/androidterm.c (android_event_is_for_frame): New function. (android_reset_conversion): Free and unqueue all text conversion events for the given frame. diff --git a/src/android.c b/src/android.c index e4f9dd0ffbe..a2c239736a7 100644 --- a/src/android.c +++ b/src/android.c @@ -601,6 +601,40 @@ android_next_event (union android_event *event_return) pthread_mutex_unlock (&event_queue.mutex); } +bool +android_check_if_event (union android_event *event_return, + bool (*predicate) (union android_event *, + void *), + void *arg) +{ + struct android_event_container *container; + + pthread_mutex_lock (&event_queue.mutex); + + /* Loop over each event. */ + container = event_queue.events.last; + for (; container != &event_queue.events; container = container->last) + { + /* See if the predicate matches. */ + if ((*predicate) (&container->event, arg)) + { + /* Copy out the event and return true. */ + *event_return = container->event; + --event_queue.num_events; + + /* Unlink container. */ + container->last->next = container->next; + container->next->last = container->last; + free (container); + pthread_mutex_unlock (&event_queue.mutex); + return true; + } + } + + pthread_mutex_unlock (&event_queue.mutex); + return false; +} + void android_write_event (union android_event *event) { diff --git a/src/androidgui.h b/src/androidgui.h index 0e311b629c6..ddd8e9fcf72 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -524,6 +524,10 @@ #define ANDROID_IS_MODIFIER_KEY(key) \ extern int android_pending (void); extern void android_next_event (union android_event *); +extern bool android_check_if_event (union android_event *, + bool (*) (union android_event *, + void *), + void *); extern android_window android_create_window (android_window, int, int, int, int, diff --git a/src/androidterm.c b/src/androidterm.c index ed375ef53fe..72d81744128 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5474,6 +5474,19 @@ android_update_selection (struct frame *f, struct window *w) } } +/* Return whether or not EVENT is an input method event destined for + the frame (struct frame *) ARG. */ + +static bool +android_event_is_for_frame (union android_event *event, void *arg) +{ + struct frame *f; + + f = arg; + return (event->type == ANDROID_INPUT_METHOD + && event->ime.window == FRAME_ANDROID_WINDOW (f)); +} + /* Notice that the input method connection to F should be reset as a result of a change to its contents. */ @@ -5484,6 +5497,7 @@ android_reset_conversion (struct frame *f) struct window *w; struct buffer *buffer; Lisp_Object style; + union android_event event; /* Reset the input method. @@ -5507,6 +5521,27 @@ android_reset_conversion (struct frame *f) else mode = ANDROID_IC_MODE_TEXT; + /* Remove any existing input method events that apply to FRAME from + the event queue. + + There's a small window between this and the call to + android_reset_ic between which more events can be generated. */ + + while (android_check_if_event (&event, android_event_is_for_frame, f)) + { + switch (event.ime.operation) + { + case ANDROID_IME_COMMIT_TEXT: + case ANDROID_IME_FINISH_COMPOSING_TEXT: + case ANDROID_IME_SET_COMPOSING_TEXT: + xfree (event.ime.text); + break; + + default: + break; + } + } + android_reset_ic (FRAME_ANDROID_WINDOW (f), mode); /* Clear extracted text flags. Since the IM has been reinitialised, commit 82b4b9e8692c349a45d319fe05c9fbfed4ab203d Author: Po Lu Date: Sun Mar 12 17:07:57 2023 +0800 Update Android port * src/androidterm.c (NATIVE_NAME, JNICALL) (android_build_extracted_text, android_update_selection): Use 0-based indices for Android buffer positions. Also, report surrounding text relative to the region, not to the cursor. * src/textconv.c (textconv_query): Accept new values of position. (really_set_composing_text): Use ephemeral last point. diff --git a/src/androidterm.c b/src/androidterm.c index 397971e3c87..ed375ef53fe 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -4811,7 +4811,7 @@ NATIVE_NAME (getTextAfterCursor) (JNIEnv *env, jobject object, jshort window, jstring string; /* First, set up the conversion query. */ - context.query.position = 0; + context.query.position = EMACS_INT_MAX; context.query.direction = TEXTCONV_FORWARD_CHAR; context.query.factor = min (length, 65535); context.query.operation = TEXTCONV_RETRIEVAL; @@ -4855,7 +4855,7 @@ NATIVE_NAME (getTextBeforeCursor) (JNIEnv *env, jobject object, jshort window, jstring string; /* First, set up the conversion query. */ - context.query.position = 0; + context.query.position = TYPE_MINIMUM (EMACS_INT); context.query.direction = TEXTCONV_BACKWARD_CHAR; context.query.factor = min (length, 65535); context.query.operation = TEXTCONV_RETRIEVAL; @@ -4936,8 +4936,8 @@ NATIVE_NAME (setComposingRegion) (JNIEnv *env, jobject object, jshort window, event.ime.serial = ++event_serial; event.ime.window = window; event.ime.operation = ANDROID_IME_SET_COMPOSING_REGION; - event.ime.start = start; - event.ime.end = end; + event.ime.start = start + 1; + event.ime.end = end + 1; event.ime.length = 0; event.ime.position = 0; event.ime.text = NULL; @@ -4961,8 +4961,8 @@ NATIVE_NAME (setSelection) (JNIEnv *env, jobject object, jshort window, event.ime.serial = ++event_serial; event.ime.window = window; event.ime.operation = ANDROID_IME_SET_POINT; - event.ime.start = start; - event.ime.end = end; + event.ime.start = start + 1; + event.ime.end = end + 1; event.ime.length = 0; event.ime.position = start; event.ime.text = NULL; @@ -5040,11 +5040,12 @@ NATIVE_NAME (getSelection) (JNIEnv *env, jobject object, jshort window) return NULL; /* Wraparound actually makes more sense than truncation; at least - editing will sort of work. */ + editing will sort of work. Convert the positions to start from + index 0, as that is what Android expects. */ contents[0] = (unsigned int) min (context.point, - context.mark); + context.mark) - 1; contents[1] = (unsigned int) max (context.point, - context.mark); + context.mark) - 1; /* Now create the array. */ array = (*env)->NewIntArray (env, 2); @@ -5209,8 +5210,11 @@ android_build_extracted_text (jstring text, ptrdiff_t start, min (offset, TYPE_MAXIMUM (jint))); (*env)->SetIntField (env, object, text_class.selection_end, min (offset, TYPE_MAXIMUM (jint))); + + /* Subtract 1 from start: point indices in Emacs start from 1, but + Android expects 0. */ (*env)->SetIntField (env, object, text_class.start_offset, - min (start, TYPE_MAXIMUM (jint))); + min (start - 1, TYPE_MAXIMUM (jint))); (*env)->SetObjectField (env, object, text_class.text, text); return object; } @@ -5311,8 +5315,11 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, min (context.offset, TYPE_MAXIMUM (jint))); (*env)->SetIntField (env, object, text_class.selection_end, min (context.offset, TYPE_MAXIMUM (jint))); + + /* Subtract 1 from start: point indices in Emacs start from 1, but + Android expects 0. */ (*env)->SetIntField (env, object, text_class.start_offset, - min (context.start, TYPE_MAXIMUM (jint))); + min (context.start - 1, TYPE_MAXIMUM (jint))); (*env)->SetObjectField (env, object, text_class.text, string); return object; } @@ -5397,8 +5404,9 @@ android_update_selection (struct frame *f, struct window *w) { eassert (MARKERP (f->conversion.compose_region_end)); - start = marker_position (f->conversion.compose_region_start); - end = marker_position (f->conversion.compose_region_end); + /* Indexing in android starts from 0 instead of 1. */ + start = marker_position (f->conversion.compose_region_start) - 1; + end = marker_position (f->conversion.compose_region_end) - 1; } else start = -1, end = -1; @@ -5423,9 +5431,11 @@ android_update_selection (struct frame *f, struct window *w) /* Send the update. Android doesn't have a concept of ``point'' and ``mark''; instead, it only has a selection, where the start of - the selection is less than or equal to the end. */ - android_update_ic (FRAME_ANDROID_WINDOW (f), min (point, mark), - max (point, mark), start, end); + the selection is less than or equal to the end. Also, convert + the indices from 1-based Emacs indices to 0-based Android + ones. */ + android_update_ic (FRAME_ANDROID_WINDOW (f), min (point, mark) - 1, + max (point, mark) - 1, start, end); /* Update the extracted text as well, if the input method has asked for updates. 1 is @@ -5438,25 +5448,28 @@ android_update_selection (struct frame *f, struct window *w) text = get_extracted_text (f, min (hint, 600), &start, &offset, &length, &bytes); - /* Make a string out of the extracted text. */ - string = android_text_to_string (android_java_env, - text, length, bytes); - xfree (text); - android_exception_check (); - - /* Make extracted text out of that string. */ - extracted = android_build_extracted_text (string, start, - offset); - android_exception_check_1 (string); - ANDROID_DELETE_LOCAL_REF (string); - - if (extracted) + if (text) { - /* extracted is now an associated ExtractedText object. - Perform the update. */ - android_update_extracted_text (FRAME_ANDROID_WINDOW (f), - extracted, token); - ANDROID_DELETE_LOCAL_REF (extracted); + /* Make a string out of the extracted text. */ + string = android_text_to_string (android_java_env, + text, length, bytes); + xfree (text); + android_exception_check (); + + /* Make extracted text out of that string. */ + extracted = android_build_extracted_text (string, start, + offset); + android_exception_check_1 (string); + ANDROID_DELETE_LOCAL_REF (string); + + if (extracted) + { + /* extracted is now an associated ExtractedText object. + Perform the update. */ + android_update_extracted_text (FRAME_ANDROID_WINDOW (f), + extracted, token); + ANDROID_DELETE_LOCAL_REF (extracted); + } } } } diff --git a/src/textconv.c b/src/textconv.c index 3206bb48204..900277016f2 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -137,6 +137,10 @@ select_window (Lisp_Object window, Lisp_Object norecord) window and QUERY->factor times QUERY->direction from that position. Return it in QUERY->text. + If QUERY->position is TYPE_MINIMUM (EMACS_INT) or EMACS_INT_MAX, + start at the window's last point or mark, whichever is greater or + smaller. + Then, either delete that text from the buffer if QUERY->operation is TEXTCONV_SUBSTITUTION, or return 0. @@ -155,8 +159,9 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query, { specpdl_ref count; ptrdiff_t pos, pos_byte, end, end_byte, start; - ptrdiff_t temp, temp1; + ptrdiff_t temp, temp1, mark; char *buffer; + struct window *w; /* Save the excursion, as there will be extensive changes to the selected window. */ @@ -171,12 +176,35 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query, select_window ((WINDOW_LIVE_P (f->old_selected_window) ? f->old_selected_window : f->selected_window), Qt); + w = XWINDOW (selected_window); /* Now find the appropriate text bounds for QUERY. First, move point QUERY->position steps forward or backwards. */ pos = PT; + /* If QUERY->position is EMACS_INT_MAX, use the last mark or the + ephemeral last point, whichever is greater. + + The opposite applies for EMACS_INT_MIN. */ + + mark = get_mark (); + + if (query->position == EMACS_INT_MAX) + { + pos = (mark == -1 + ? w->ephemeral_last_point + : max (w->ephemeral_last_point, mark)); + goto escape1; + } + else if (query->position == TYPE_MINIMUM (EMACS_INT)) + { + pos = (mark == -1 + ? w->ephemeral_last_point + : min (w->ephemeral_last_point, mark)); + goto escape1; + } + /* Next, if POS lies within the conversion region and the caller asked for it to be moved away, move it away from the conversion region. */ @@ -210,7 +238,7 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query, if (flags & TEXTCONV_SKIP_ACTIVE_REGION) { - temp = get_mark (); + temp = mark; if (temp == -1) goto escape; @@ -246,6 +274,8 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query, if (INT_ADD_WRAPV (pos, query->position, &pos)) pos = PT; + escape1: + if (pos < BEGV) pos = BEGV; @@ -792,7 +822,7 @@ really_set_composing_text (struct frame *f, ptrdiff_t position, /* If PT hasn't changed, the conversion region definitely has. Otherwise, redisplay will update the input method instead. */ - if (PT == w->last_point + if (PT == w->ephemeral_last_point && text_interface && text_interface->compose_region_changed) { commit 3573db24ad0125d1a553e85eb08c93c61c62ef33 Merge: a17380e80d1 9199fa00caa Author: Po Lu Date: Sun Mar 12 15:43:56 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit a17380e80d162dbc15110ce84ff2e12e11e0623b Author: Po Lu Date: Sun Mar 12 15:43:14 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity) (onCancel): New function. (displayFailureDialog): Handle dialog cancellation. * src/sfntfont.c (sfnt_parse_languages): Look for SLNG tag if DLNG is not present. diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index 51335ddb2dd..e8fb24d53d8 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -68,7 +68,8 @@ import java.io.UnsupportedEncodingException; public final class EmacsOpenActivity extends Activity - implements DialogInterface.OnClickListener + implements DialogInterface.OnClickListener, + DialogInterface.OnCancelListener { private static final String TAG = "EmacsOpenActivity"; @@ -121,6 +122,13 @@ private class EmacsClientThread extends Thread finish (); } + @Override + public void + onCancel (DialogInterface dialog) + { + finish (); + } + public String readEmacsClientLog () { @@ -178,6 +186,7 @@ private class EmacsClientThread extends Thread dialog.setMessage (text); dialog.setButton (DialogInterface.BUTTON_POSITIVE, "OK", this); + dialog.setOnCancelListener (this); dialog.show (); } diff --git a/src/sfntfont.c b/src/sfntfont.c index e8e437072d9..ab92def0aff 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -482,7 +482,14 @@ sfnt_parse_languages (struct sfnt_meta_table *meta, &map); if (!data) - return; + { + /* Fall back to the supported languages metadata. */ + data = sfnt_find_metadata (meta, SFNT_META_DATA_TAG_SLNG, + &map); + + if (!data) + return; + } USE_SAFE_ALLOCA; commit b0abc50218696b7b5db6589d73c49fdb64b3e289 Author: Po Lu Date: Sun Mar 12 11:06:47 2023 +0800 Add Super modifier support to Android port * src/androidgui.h (enum android_modifier_mask): New modifier ANDROID_SUPER_MASK. * src/androidterm.c (android_android_to_emacs_modifiers) (android_emacs_to_android_modifiers): Add new modifier. diff --git a/src/androidgui.h b/src/androidgui.h index b918d03ceca..0e311b629c6 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -247,6 +247,7 @@ #define PWinGravity (1L << 9) /* program specified window gravity */ ANDROID_SHIFT_MASK = 193, ANDROID_CONTROL_MASK = 4096, ANDROID_ALT_MASK = 2, + ANDROID_SUPER_MASK = 4, }; struct android_key_event diff --git a/src/androidterm.c b/src/androidterm.c index 019b84bf391..397971e3c87 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -362,7 +362,8 @@ android_android_to_emacs_modifiers (struct android_display_info *dpyinfo, { return ((state & ANDROID_CONTROL_MASK) ? ctrl_modifier : 0 | (state & ANDROID_SHIFT_MASK) ? shift_modifier : 0 - | (state & ANDROID_ALT_MASK) ? meta_modifier : 0); + | (state & ANDROID_ALT_MASK) ? meta_modifier : 0 + | (state & ANDROID_SUPER_MASK) ? super_modifier : 0); } static int @@ -371,7 +372,8 @@ android_emacs_to_android_modifiers (struct android_display_info *dpyinfo, { return ((state & ctrl_modifier) ? ANDROID_CONTROL_MASK : 0 | (state & shift_modifier) ? ANDROID_SHIFT_MASK : 0 - | (state & meta_modifier) ? ANDROID_ALT_MASK : 0); + | (state & meta_modifier) ? ANDROID_ALT_MASK : 0 + | (state & super_modifier) ? ANDROID_SUPER_MASK : 0); } static void android_frame_rehighlight (struct android_display_info *); commit 5c2fee0c257ea24cf28e0d493e5dbaf41152d94c Author: Po Lu Date: Sun Mar 12 10:45:26 2023 +0800 Fix crash during androidterm init * src/androidterm.c (syms_of_androidterm): Initialize Vandroid_build_fingerprint in case GC happens. diff --git a/src/androidterm.c b/src/androidterm.c index bd7e60dcb3f..019b84bf391 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5849,6 +5849,7 @@ syms_of_androidterm (void) doc: /* String identifying the device's OS version. This is a string that uniquely identifies the version of Android Emacs is running on. */); + Vandroid_build_fingerprint = Qnil; /* Only defined so loadup.el loads scroll-bar.el. */ DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars, commit 7971b533a28053fa695ee8643ec4018793dbc7c6 Author: Po Lu Date: Sun Mar 12 10:39:56 2023 +0800 ; * src/emacs-module.c (module_reset_handlerlist): Fix macro conflict. diff --git a/src/emacs-module.c b/src/emacs-module.c index 133a06f35b7..3d06ef0020a 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -1620,13 +1620,13 @@ finalize_runtime_unwind (void *raw_ert) /* Must be called after setting up a handler immediately before returning from the function. See the comments in lisp.h and the code in eval.c for details. The macros below arrange for this - function to be called automatically. HANDLERLIST points to the + function to be called automatically. IHANDLERLIST points to the handler list. */ static void -module_reset_handlerlist (struct handler *handlerlist) +module_reset_handlerlist (struct handler *ihandlerlist) { - eassert (handlerlist == handlerlist); + eassert (handlerlist == ihandlerlist); handlerlist = handlerlist->next; } commit 6c68d9bd3a18c74384fc764179fd92a024d6c35d Author: Po Lu Date: Sun Mar 12 10:37:18 2023 +0800 Clean up emacs-module.c * src/emacs-module.c (MODULE_HANDLE_NONLOCAL_EXIT) (module_make_global_ref, module_free_global_ref) (module_make_function, module_get_function_finalizer) (module_set_function_finalizer, module_make_interactive) (module_funcall, module_intern, module_type_of) (module_extract_integer, module_make_integer, module_extract_float) (module_make_float, module_copy_string_contents) (module_make_string, module_make_unibyte_string) (module_make_user_ptr, module_get_user_ptr, module_set_user_ptr) (module_get_user_finalizer, module_set_user_finalizer) (module_vec_set, module_vec_get, module_vec_size) (module_process_input, module_extract_time, module_make_time) (module_extract_big_integer, module_make_big_integer) (module_open_channel, module_reset_handlerlist): Adjust as recommended by Paul Eggert . diff --git a/src/emacs-module.c b/src/emacs-module.c index 4719b15c992..133a06f35b7 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -206,7 +206,7 @@ Copyright (C) 2015-2023 Free Software Foundation, Inc. static void module_non_local_exit_throw_1 (emacs_env *, Lisp_Object, Lisp_Object); static void module_out_of_memory (emacs_env *); -static void module_reset_handlerlist (struct handler **); +static void module_reset_handlerlist (struct handler *); static bool value_storage_contains_p (const struct emacs_value_storage *, emacs_value, ptrdiff_t *); @@ -280,13 +280,13 @@ #define MODULE_HANDLE_NONLOCAL_EXIT(retval) \ module_handle_nonlocal_exit (env, \ internal_cleanup->nonlocal_exit, \ internal_cleanup->val); \ - module_reset_handlerlist (&internal_cleanup); \ + module_reset_handlerlist (internal_cleanup); \ return retval; \ } \ do { } while (false) -#define MODULE_INTERNAL_CLEANUP \ - module_reset_handlerlist (&internal_cleanup) +#define MODULE_INTERNAL_CLEANUP() \ + module_reset_handlerlist (internal_cleanup) /* Implementation of runtime and environment functions. @@ -440,7 +440,7 @@ module_make_global_ref (emacs_env *env, emacs_value value) bool overflow = INT_ADD_WRAPV (ref->refcount, 1, &ref->refcount); if (overflow) overflow_error (); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return &ref->value; } else @@ -453,7 +453,7 @@ module_make_global_ref (emacs_env *env, emacs_value value) Lisp_Object value; XSETPSEUDOVECTOR (value, ref, PVEC_OTHER); hash_put (h, new_obj, value, hashcode); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return &ref->value; } } @@ -486,7 +486,7 @@ module_free_global_ref (emacs_env *env, emacs_value global_value) hash_remove_from_table (h, obj); } - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); } static enum emacs_funcall_exit @@ -607,7 +607,7 @@ module_make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity, eassert (MODULE_FUNCTIONP (result)); value = lisp_to_value (env, result); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return value; } @@ -617,7 +617,7 @@ module_get_function_finalizer (emacs_env *env, emacs_value arg) MODULE_FUNCTION_BEGIN (NULL); Lisp_Object lisp = value_to_lisp (arg); CHECK_MODULE_FUNCTION (lisp); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return XMODULE_FUNCTION (lisp)->finalizer; } @@ -629,7 +629,7 @@ module_set_function_finalizer (emacs_env *env, emacs_value arg, Lisp_Object lisp = value_to_lisp (arg); CHECK_MODULE_FUNCTION (lisp); XMODULE_FUNCTION (lisp)->finalizer = fin; - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); } void @@ -649,7 +649,7 @@ module_make_interactive (emacs_env *env, emacs_value function, emacs_value spec) /* Normalize (interactive nil) to (interactive). */ XMODULE_FUNCTION (lisp_fun)->interactive_form = NILP (lisp_spec) ? list1 (Qinteractive) : list2 (Qinteractive, lisp_spec); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); } Lisp_Object @@ -683,7 +683,7 @@ module_funcall (emacs_env *env, emacs_value func, ptrdiff_t nargs, newargs[1 + i] = value_to_lisp (args[i]); emacs_value result = lisp_to_value (env, Ffuncall (nargs1, newargs)); SAFE_FREE (); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return result; } @@ -694,7 +694,7 @@ module_intern (emacs_env *env, const char *name) MODULE_FUNCTION_BEGIN (NULL); tem = lisp_to_value (env, intern (name)); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return tem; } @@ -705,7 +705,7 @@ module_type_of (emacs_env *env, emacs_value arg) MODULE_FUNCTION_BEGIN (NULL); tem = lisp_to_value (env, Ftype_of (value_to_lisp (arg))); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return tem; } @@ -732,7 +732,7 @@ module_extract_integer (emacs_env *env, emacs_value arg) intmax_t i; if (! integer_to_intmax (lisp, &i)) xsignal1 (Qoverflow_error, lisp); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return i; } @@ -743,7 +743,7 @@ module_make_integer (emacs_env *env, intmax_t n) MODULE_FUNCTION_BEGIN (NULL); value = lisp_to_value (env, make_int (n)); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return value; } @@ -754,7 +754,7 @@ module_extract_float (emacs_env *env, emacs_value arg) MODULE_FUNCTION_BEGIN (0); Lisp_Object lisp = value_to_lisp (arg); CHECK_TYPE (FLOATP (lisp), Qfloatp, lisp); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return XFLOAT_DATA (lisp); } @@ -766,7 +766,7 @@ module_make_float (emacs_env *env, double d) MODULE_FUNCTION_BEGIN (NULL); value = lisp_to_value (env, make_float (d)); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return value; } @@ -800,7 +800,7 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buf, if (buf == NULL) { *len = required_buf_size; - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return true; } @@ -816,7 +816,7 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buf, *len = required_buf_size; memcpy (buf, SDATA (lisp_str_utf8), raw_size + 1); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return true; } @@ -831,7 +831,7 @@ module_make_string (emacs_env *env, const char *str, ptrdiff_t len) Lisp_Object lstr = len == 0 ? empty_multibyte_string : module_decode_utf_8 (str, len); value = lisp_to_value (env, lstr); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return value; } @@ -846,7 +846,7 @@ module_make_unibyte_string (emacs_env *env, const char *str, ptrdiff_t length) Lisp_Object lstr = length == 0 ? empty_unibyte_string : make_unibyte_string (str, length); value = lisp_to_value (env, lstr); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return value; } @@ -858,7 +858,7 @@ module_make_user_ptr (emacs_env *env, emacs_finalizer fin, void *ptr) MODULE_FUNCTION_BEGIN (NULL); value = lisp_to_value (env, make_user_ptr (fin, ptr)); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return value; } @@ -869,7 +869,7 @@ module_get_user_ptr (emacs_env *env, emacs_value arg) MODULE_FUNCTION_BEGIN (NULL); Lisp_Object lisp = value_to_lisp (arg); CHECK_USER_PTR (lisp); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return XUSER_PTR (lisp)->p; } @@ -881,7 +881,7 @@ module_set_user_ptr (emacs_env *env, emacs_value arg, void *ptr) Lisp_Object lisp = value_to_lisp (arg); CHECK_USER_PTR (lisp); XUSER_PTR (lisp)->p = ptr; - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); } static emacs_finalizer @@ -890,7 +890,7 @@ module_get_user_finalizer (emacs_env *env, emacs_value arg) MODULE_FUNCTION_BEGIN (NULL); Lisp_Object lisp = value_to_lisp (arg); CHECK_USER_PTR (lisp); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return XUSER_PTR (lisp)->finalizer; } @@ -902,7 +902,7 @@ module_set_user_finalizer (emacs_env *env, emacs_value arg, Lisp_Object lisp = value_to_lisp (arg); CHECK_USER_PTR (lisp); XUSER_PTR (lisp)->finalizer = fin; - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); } static void @@ -922,7 +922,7 @@ module_vec_set (emacs_env *env, emacs_value vector, ptrdiff_t index, Lisp_Object lisp = value_to_lisp (vector); check_vec_index (lisp, index); ASET (lisp, index, value_to_lisp (value)); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); } static emacs_value @@ -934,7 +934,7 @@ module_vec_get (emacs_env *env, emacs_value vector, ptrdiff_t index) Lisp_Object lisp = value_to_lisp (vector); check_vec_index (lisp, index); value = lisp_to_value (env, AREF (lisp, index)); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return value; } @@ -945,7 +945,7 @@ module_vec_size (emacs_env *env, emacs_value vector) MODULE_FUNCTION_BEGIN (0); Lisp_Object lisp = value_to_lisp (vector); CHECK_VECTOR (lisp); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return ASIZE (lisp); } @@ -967,7 +967,7 @@ module_process_input (emacs_env *env) MODULE_FUNCTION_BEGIN (emacs_process_input_quit); maybe_quit (); rc = emacs_process_input_continue; - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return rc; } @@ -978,7 +978,7 @@ module_extract_time (emacs_env *env, emacs_value arg) MODULE_FUNCTION_BEGIN ((struct timespec) {0}); value = lisp_time_argument (value_to_lisp (arg)); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return value; } @@ -990,7 +990,7 @@ module_make_time (emacs_env *env, struct timespec time) MODULE_FUNCTION_BEGIN (NULL); value = lisp_to_value (env, timespec_to_lisp (time)); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return value; } @@ -1070,7 +1070,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, *sign = (0 < x) - (x < 0); if (x == 0 || count == NULL) { - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return true; } /* As a simplification we don't check how many array elements @@ -1083,7 +1083,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, if (magnitude == NULL) { *count = required; - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return true; } if (*count < required) @@ -1102,14 +1102,14 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, verify (required * bits < PTRDIFF_MAX); for (ptrdiff_t i = 0; i < required; ++i) magnitude[i] = (emacs_limb_t) (u >> (i * bits)); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return true; } const mpz_t *x = xbignum_val (o); *sign = mpz_sgn (*x); if (count == NULL) { - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return true; } size_t required_size = (mpz_sizeinbase (*x, 2) + numb - 1) / numb; @@ -1119,7 +1119,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, if (magnitude == NULL) { *count = required; - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return true; } if (*count < required) @@ -1132,7 +1132,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, size_t written; mpz_export (magnitude, &written, order, size, endian, nails, *x); eassert (written == required_size); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return true; } @@ -1146,7 +1146,7 @@ module_make_big_integer (emacs_env *env, int sign, if (sign == 0) { value = lisp_to_value (env, make_fixed_natnum (0)); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return value; } enum { order = -1, size = sizeof *magnitude, endian = 0, nails = 0 }; @@ -1154,7 +1154,7 @@ module_make_big_integer (emacs_env *env, int sign, if (sign < 0) mpz_neg (mpz[0], mpz[0]); value = lisp_to_value (env, make_integer_mpz ()); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return value; } @@ -1165,7 +1165,7 @@ module_open_channel (emacs_env *env, emacs_value pipe_process) MODULE_FUNCTION_BEGIN (-1); rc = open_channel_for_module (value_to_lisp (pipe_process)); - MODULE_INTERNAL_CLEANUP; + MODULE_INTERNAL_CLEANUP (); return rc; } @@ -1620,12 +1620,13 @@ finalize_runtime_unwind (void *raw_ert) /* Must be called after setting up a handler immediately before returning from the function. See the comments in lisp.h and the code in eval.c for details. The macros below arrange for this - function to be called automatically. PHANDLERLIST points to a word - containing the handler list, for sanity checking. */ + function to be called automatically. HANDLERLIST points to the + handler list. */ + static void -module_reset_handlerlist (struct handler **phandlerlist) +module_reset_handlerlist (struct handler *handlerlist) { - eassert (handlerlist == *phandlerlist); + eassert (handlerlist == handlerlist); handlerlist = handlerlist->next; } commit b83324d1bd5fcab98c6fed71f65424f7017a9a82 Author: Po Lu Date: Sun Mar 12 09:53:41 2023 +0800 Update Android port * configure.ac: Take option `--with-shared-user-id' and give it to AndroidManifest.xml.in. * java/AndroidManifest.xml.in: Substitute that into the application info. * java/INSTALL (BUILDING WITH A SHARED USER ID): New section. diff --git a/configure.ac b/configure.ac index 45041781f26..a8b1f297bc4 100644 --- a/configure.ac +++ b/configure.ac @@ -567,6 +567,10 @@ AC_DEFUN OPTION_DEFAULT_OFF([android],[cross-compile Android application package]) OPTION_DEFAULT_ON([android-debug],[don't build Emacs as a debug package on Android]) +AC_ARG_WITH([shared-user-id], + [AS_HELP_STRING([--with-shared-user-id=ID], + [use the given shared user ID in Android builds])]) + AC_ARG_WITH([file-notification],[AS_HELP_STRING([--with-file-notification=LIB], [use a file notification library (LIB one of: yes, inotify, kqueue, gfile, w32, no)])], [ case "${withval}" in @@ -798,6 +802,7 @@ AC_DEFUN ANDROID_JAR= ANDROID_ABI= WARN_JAVAFLAGS= +ANDROID_SHARED_USER_ID= # This is a list of Makefiles that have alternative versions for # Android. @@ -2571,7 +2576,7 @@ AC_DEFUN REALLY_ANDROID= CM_OBJ="cm.o" -if test "${ANDROID}" = "yes"; then +AS_IF([test "$ANDROID" = "yes"],[ window_system=android no_x=yes ANDROID_OBJ="androidterm.o androidfns.o androidfont.o androidmenu.o" @@ -2582,10 +2587,17 @@ AC_DEFUN AC_DEFINE([HAVE_ANDROID], [1], [Define to 1 if Emacs is being built with Android support]) - if test "${XCONFIGURE}" != "android"; then + AS_IF([test "$XCONFIGURE" != "android"], [ AC_DEFINE([ANDROID_STUBIFY], [1], [Define to 1 if Emacs is being built for Android, but all API calls need to be stubbed out]) - else + + # Now set any shared user ID that was specified. + AS_IF([test -n "$with_shared_user_id"], + [emacs_val=$with_shared_user_id + emacs_val=`AS_ECHO(["$with_shared_user_id"]) \ + | sed -e 's/"/\\"/'` + emacs_val="\"$emacs_val\"" + ANDROID_SHARED_USER_ID="android:sharedUserId=$emacs_val"])],[ # Emacs will be built as a shared library, and a wrapper around it # will also be built for the benefit of applications. This # requires Emacs be built as a position independent executable. @@ -2598,11 +2610,9 @@ AC_DEFUN # Link with libraries required for Android support. # API 9 and later require `-landroid' for the asset manager. # API 8 uses an emulation via the JNI. - if test "$ANDROID_SDK" -lt "9"; then - ANDROID_LIBS="-llog -ljnigraphics" - else - ANDROID_LIBS="-landroid -llog -ljnigraphics" - fi + AS_IF([test "$ANDROID_SDK" -lt "9"], + [ANDROID_LIBS="-llog -ljnigraphics"], + [ANDROID_LIBS="-landroid -llog -ljnigraphics"]) # This is required to make the system load emacs.apk's libpng # (among others) instead of the system's own. But it doesn't work @@ -2621,15 +2631,14 @@ AC_DEFUN AC_CHECK_DECLS([android_get_device_api_level]) # Say this build is really for Android. - REALLY_ANDROID=yes - fi -fi + REALLY_ANDROID=yes])]) -AC_SUBST(ANDROID) -AC_SUBST(ANDROID_OBJ) -AC_SUBST(ANDROID_LIBS) -AC_SUBST(ANDROID_LDFLAGS) -AC_SUBST(ANDROID_CFLAGS) +AC_SUBST([ANDROID]) +AC_SUBST([ANDROID_OBJ]) +AC_SUBST([ANDROID_LIBS]) +AC_SUBST([ANDROID_LDFLAGS]) +AC_SUBST([ANDROID_CFLAGS]) +AC_SUBST([ANDROID_SHARED_USER_ID]) if test "${with_pgtk}" = "yes"; then window_system=pgtk @@ -7518,7 +7527,8 @@ AC_DEFUN [Summary of some of the main features enabled by configure.]) AS_ECHO([" Does Emacs use -lXaw3d? ${HAVE_XAW3D} - Does Emacs use Android? ${ANDROID} + Is Emacs being built for Android? ${ANDROID}\ +`AS_IF([test -n "$with_shared_user_id"],[AS_ECHO([" ($with_shared_user_id)"])])` Does Emacs use the X Double Buffer Extension? ${HAVE_XDBE} Does Emacs use -lXpm? ${HAVE_XPM} Does Emacs use -ljpeg? ${HAVE_JPEG} diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in index 4ebfe470c0a..f7f834e7582 100644 --- a/java/AndroidManifest.xml.in +++ b/java/AndroidManifest.xml.in @@ -69,6 +69,7 @@ along with GNU Emacs. If not, see . --> android:supportsRtl="true" android:theme="@style/EmacsStyle" android:debuggable="@ANDROID_DEBUGGABLE@" + @ANDROID_SHARED_USER_ID@ android:extractNativeLibs="true"> Date: Sat Mar 11 19:08:24 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit a697ca556225864d6a820cd1b316a9577ccf84bd Author: Po Lu Date: Sat Mar 11 18:58:18 2023 +0800 Improve default value of `with_mailutils' on Android * configure.ac: Default to off on Android. diff --git a/configure.ac b/configure.ac index 3eb57a089a0..45041781f26 100644 --- a/configure.ac +++ b/configure.ac @@ -318,10 +318,12 @@ AC_DEFUN [], [with_mailutils=$with_features AS_IF([test "$with_mailutils" = yes], - [AS_IF([test "x$XCONFIGURE" != "xandroid"], + [AS_IF([test "x$XCONFIGURE" != "xandroid" \ + && test "$with_android" = "no"], [(movemail --version) >/dev/null 2>&1 || with_mailutils=no], - [dnl don't check for movemail if cross-compiling. - with_mailutils=yes])])]) + [dnl Don't check for movemail if cross-compiling. + dnl instead, default to false. + with_mailutils=no])])]) AS_IF([test "$with_mailutils" = no], [with_mailutils=]) commit b931a92ac4fddc745ac8fa8f07899568b012117f Author: Po Lu Date: Sat Mar 11 17:43:15 2023 +0800 ; * configure.ac: Fix typo. diff --git a/configure.ac b/configure.ac index 74ff59c79e4..3eb57a089a0 100644 --- a/configure.ac +++ b/configure.ac @@ -317,7 +317,7 @@ AC_DEFUN installed])], [], [with_mailutils=$with_features - AS_IF(["$with_mailutils" = yes], + AS_IF([test "$with_mailutils" = yes], [AS_IF([test "x$XCONFIGURE" != "xandroid"], [(movemail --version) >/dev/null 2>&1 || with_mailutils=no], [dnl don't check for movemail if cross-compiling. commit c4b77b82decb757af0aff1b7420203fa0805b483 Author: Po Lu Date: Sat Mar 11 17:42:24 2023 +0800 Update Android port * configure.ac (HAVE_MAILUTILS, with_mailutils) (ANDROID_SDK_8_OR_EARLIER, XCONFIGURE): Fix POP and mailutils configuration on Android. * java/Makefile.in: * src/callproc.c (syms_of_callproc): Avoid using built-in movemail when --with-mailutils. diff --git a/configure.ac b/configure.ac index 56e293a6baa..74ff59c79e4 100644 --- a/configure.ac +++ b/configure.ac @@ -317,24 +317,31 @@ AC_DEFUN installed])], [], [with_mailutils=$with_features - if test "$with_mailutils" = yes; then - (movemail --version) >/dev/null 2>&1 || with_mailutils=no - fi]) -if test "$with_mailutils" = no; then - with_mailutils= -fi + AS_IF(["$with_mailutils" = yes], + [AS_IF([test "x$XCONFIGURE" != "xandroid"], + [(movemail --version) >/dev/null 2>&1 || with_mailutils=no], + [dnl don't check for movemail if cross-compiling. + with_mailutils=yes])])]) +AS_IF([test "$with_mailutils" = no], + [with_mailutils=]) + +AS_IF([test x"$with_mailutils" = xyes], + [AC_DEFINE([HAVE_MAILUTILS], [1], + [Define to 1 if Emacs was configured with mailutils])]) + AC_SUBST([with_mailutils]) AC_ARG_WITH([pop], [AS_HELP_STRING([--with-pop], [Support POP mail retrieval if Emacs movemail is used (not recommended, as Emacs movemail POP is insecure). This is the default only on - native MS-Windows.])], + native MS-Windows and Android.])], [], - [case $host in - *-mingw*) with_pop=yes;; - *) with_pop=no-by-default;; - esac]) + dnl Enable movemail POP support on Android as GNU Mailutils is + dnl normally unavailable on that platform. + [AS_CASE([$host], + [*-mingw*|*android*], [with_pop=yes], + [with_pop=no-by-default])]) if test "$with_pop" = yes; then AC_DEFINE([MAIL_USE_POP]) fi @@ -794,6 +801,9 @@ AC_DEFUN # Android. android_makefiles="lib/Makefile lib/gnulib.mk lib-src/Makefile src/Makefile" +# This is whether or not to package mailutils into the executable. +emacs_use_mailutils= + AC_ARG_VAR([JAVAC], [Java compiler path. Used for Android.]) AC_ARG_VAR([JARSIGNER], [Java package signer path. Used for Android.]) AC_ARG_VAR([APKSIGNER], [Android package signer path. Used for Android.]) @@ -1151,6 +1161,11 @@ AC_DEFUN passthrough="$passthrough --with-tree-sitter=$with_tree_sitter" passthrough="$passthrough --with-imagemagick=$with_imagemagick" passthrough="$passthrough --with-lcms2=$with_lcms2" + passthrough="$passthrough --with-mailutils=$with_mailutils" + passthrough="$passthrough --with-pop=$with_pop" + + AS_IF([test "x$with_mailutils" = "xyes"], [emacs_use_mailutils=yes]) + AC_SUBST([emacs_use_mailutils]) AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC" \ ANDROID_SDK="$android_sdk" android_abi=$android_abi \ @@ -1230,6 +1245,8 @@ AC_DEFUN with_tree_sitter=no with_imagemagick=no with_lcms2=no + with_mailutils=no + with_pop=no fi with_rsvg=no diff --git a/java/Makefile.in b/java/Makefile.in index 1a7852487ef..b5ab58fe576 100644 --- a/java/Makefile.in +++ b/java/Makefile.in @@ -23,6 +23,9 @@ srcdir = builddir = @builddir@ version = @version@ +# Don't install movemail if mailutils are to be used. +emacs_use_mailutils = @emacs_use_mailutils@ + # This is the host lib-src and lib, not the cross compiler's lib-src. libsrc = ../lib-src EXEEXT = @EXEEXT@ @@ -114,15 +117,19 @@ .PHONY: all: $(APK_NAME) # Binaries to cross-compile. -CROSS_SRC_BINS = $(top_builddir)/cross/src/android-emacs -CROSS_LIBSRC_BINS = $(top_builddir)/cross/lib-src/ctags \ - $(top_builddir)/cross/lib-src/hexl \ - $(top_builddir)/cross/lib-src/movemail \ - $(top_builddir)/cross/lib-src/ebrowse \ - $(top_builddir)/cross/lib-src/emacsclient \ - $(top_builddir)/cross/lib-src/etags +CROSS_SRC_BINS := $(top_builddir)/cross/src/android-emacs +CROSS_LIBSRC_BINS := $(top_builddir)/cross/lib-src/ctags \ + $(top_builddir)/cross/lib-src/hexl \ + $(top_builddir)/cross/lib-src/ebrowse \ + $(top_builddir)/cross/lib-src/emacsclient \ + $(top_builddir)/cross/lib-src/etags +CROSS_LIBSRC_BINS_MOVEMAIL := $(top_builddir)/cross/lib-src/movemail CROSS_BINS = $(CROSS_SRC_BINS) $(CROSS_LIBSRC_BINS) +ifneq ($(emacs_use_mailutils),yes) +CROSS_LIBSRC_BINS := $(CROSS_LIBSRC_BINS) $(CROSS_LIBSRC_BINS_MOVEMAIL) +endif + # Libraries to cross-compile. CROSS_LIBS = $(top_builddir)/cross/src/libemacs.so diff --git a/src/callproc.c b/src/callproc.c index 8d3519fdab2..a1811a3bb23 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -2178,7 +2178,10 @@ syms_of_callproc (void) Use this instead of calling `movemail' directly, as `movemail' may have been renamed to comply with executable naming restrictions on the system. */); -#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY + /* Don't change the name of `movemail' if Emacs is being built to + use movemail from another source. */ +#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY \ + || defined HAVE_MAILUTILS Vmovemail_program_name = build_pure_c_string ("movemail"); #else Vmovemail_program_name = build_pure_c_string ("libmovemail.so"); commit 78faad633068d69dce25f246efb5374c21dae6bd Author: Po Lu Date: Sat Mar 11 15:56:53 2023 +0800 Update Android port * src/android.c (android_resolve_handle) (android_resolve_handle2): Don't perform checking done by CheckJNI by default. (android_copy_area): Check for errors here because CopyArea can perform a lot of consing. (android_define_cursor): Remove redundant code. diff --git a/src/android.c b/src/android.c index 79598ab86df..e4f9dd0ffbe 100644 --- a/src/android.c +++ b/src/android.c @@ -2986,6 +2986,12 @@ android_resolve_handle (android_handle handle, /* ANDROID_NONE. */ return NULL; + /* CheckJNI will normally ensure that the handle exists and is + the right type, but with a less informative error message. + Don't waste cycles doing our own checking here. */ + +#ifdef ENABLE_CHECKING + if (!android_handles[handle].handle) { __android_log_print (ANDROID_LOG_ERROR, __func__, @@ -3000,6 +3006,8 @@ android_resolve_handle (android_handle handle, emacs_abort (); } +#endif /* ENABLE_CHECKING */ + return android_handles[handle].handle; } @@ -3011,6 +3019,12 @@ android_resolve_handle2 (android_handle handle, if (!handle) return NULL; + /* CheckJNI will normally ensure that the handle exists and is + the right type, but with a less informative error message. + Don't waste cycles doing our own checking here. */ + +#ifdef ENABLE_CHECKING + if (!android_handles[handle].handle) { __android_log_print (ANDROID_LOG_ERROR, __func__, @@ -3026,6 +3040,8 @@ android_resolve_handle2 (android_handle handle, emacs_abort (); } +#endif /* ENABLE_CHECKING */ + return android_handles[handle].handle; } @@ -3861,6 +3877,7 @@ android_copy_area (android_drawable src, android_drawable dest, (jint) src_x, (jint) src_y, (jint) width, (jint) height, (jint) dest_x, (jint) dest_y); + android_exception_check (); } void @@ -6345,9 +6362,7 @@ android_define_cursor (android_window window, android_cursor cursor) jmethodID method; window1 = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); - cursor1 = (cursor - ? android_resolve_handle (cursor, ANDROID_HANDLE_CURSOR) - : NULL); + cursor1 = android_resolve_handle (cursor, ANDROID_HANDLE_CURSOR); method = window_class.define_cursor; (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, commit 4a328b857812625ec82b31d8efd928f8580d9561 Author: Po Lu Date: Sat Mar 11 11:35:59 2023 +0800 Fix problems with the menu bar on large screen Android devices * java/org/gnu/emacs/EmacsActivity.java (onContextMenuClosed): Process submenu closing normally if it happens more than 300 ms after a submenu item was selected. * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) (onMenuItemClick, display1): Give `wasSubmenuSelected' different values depending on how the submenu was selected. diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 735a464be8e..b480fc40b2e 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -321,10 +321,14 @@ public class EmacsActivity extends Activity /* See the comment inside onMenuItemClick. */ - if (EmacsContextMenu.wasSubmenuSelected + if (((EmacsContextMenu.wasSubmenuSelected == -2) + || (EmacsContextMenu.wasSubmenuSelected >= 0 + && ((System.currentTimeMillis () + - EmacsContextMenu.wasSubmenuSelected) + <= 300))) || menu == lastClosedMenu) { - EmacsContextMenu.wasSubmenuSelected = false; + EmacsContextMenu.wasSubmenuSelected = -1; lastClosedMenu = menu; return; } diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index abc1869ac6a..d780641ba70 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -46,8 +46,11 @@ public final class EmacsContextMenu /* Whether or not an item was selected. */ public static boolean itemAlreadySelected; - /* Whether or not a submenu was selected. */ - public static boolean wasSubmenuSelected; + /* Whether or not a submenu was selected. + Value is -1 if no; value is -2 if yes, and a context menu + close event will definitely be sent. Any other value is + the timestamp when the submenu was selected. */ + public static long wasSubmenuSelected; /* The serial ID of the last context menu to be displayed. */ public static int lastMenuEventSerial; @@ -78,7 +81,7 @@ private static class Item implements MenuItem.OnMenuItemClickListener /* Still set wasSubmenuSelected -- if not set, the dismissal of this context menu will result in a context menu event being sent. */ - wasSubmenuSelected = true; + wasSubmenuSelected = -2; /* Running a popup menu from inside a click handler doesn't work, so make sure it is displayed @@ -103,8 +106,13 @@ private static class Item implements MenuItem.OnMenuItemClickListener Setting this flag makes EmacsActivity to only handle SubMenuBuilder being closed, which always means the menu - has actually been dismissed. */ - wasSubmenuSelected = true; + has actually been dismissed. + + However, these extraneous events aren't sent on devices + where submenus display without dismissing their parents. + Thus, only ignore the close event if it happens within + 300 milliseconds of the submenu being selected. */ + wasSubmenuSelected = System.currentTimeMillis (); return false; } @@ -291,7 +299,7 @@ private static class Item implements MenuItem.OnMenuItemClickListener itemAlreadySelected = false; /* No submenu has been selected yet. */ - wasSubmenuSelected = false; + wasSubmenuSelected = -1; return window.view.popupMenu (this, xPosition, yPosition, false); commit 98c7825f5d4e53499a47956c311847525a9deec0 Author: Po Lu Date: Sat Mar 11 09:58:01 2023 +0800 ; * lib/gnulib.mk.in: Update from gnulib. diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index ca4df18458c..99286852f8b 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -1117,7 +1117,6 @@ INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@ INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@ JARSIGNER = @JARSIGNER@ JAVAC = @JAVAC@ -JAVAFLAGS = @JAVAFLAGS@ JPEG_CFLAGS = @JPEG_CFLAGS@ JSON_CFLAGS = @JSON_CFLAGS@ JSON_LIBS = @JSON_LIBS@ @@ -1210,6 +1209,7 @@ NDK_BUILD_AR = @NDK_BUILD_AR@ NDK_BUILD_ARCH = @NDK_BUILD_ARCH@ NDK_BUILD_CC = @NDK_BUILD_CC@ NDK_BUILD_CFLAGS = @NDK_BUILD_CFLAGS@ +NDK_BUILD_CXX = @NDK_BUILD_CXX@ NDK_BUILD_CXX_SHARED = @NDK_BUILD_CXX_SHARED@ NDK_BUILD_MODULES = @NDK_BUILD_MODULES@ NDK_BUILD_NASM = @NDK_BUILD_NASM@ @@ -1603,6 +1603,7 @@ W32_LIBS = @W32_LIBS@ W32_OBJ = @W32_OBJ@ W32_RES_LINK = @W32_RES_LINK@ WARN_CFLAGS = @WARN_CFLAGS@ +WARN_JAVAFLAGS = @WARN_JAVAFLAGS@ WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@ WEBKIT_CFLAGS = @WEBKIT_CFLAGS@ WEBKIT_LIBS = @WEBKIT_LIBS@ commit f573ce3f66daefbb26dec079581eb7cc755a9b3c Merge: 248b3459615 c6bfffa9fe1 Author: Po Lu Date: Sat Mar 11 09:55:05 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 248b34596159c460a4afd152035e226c44cf31fd Author: Po Lu Date: Sat Mar 11 09:53:24 2023 +0800 Don't use GCC extensions in src/emacs-module.c * configure.ac: Default modules to on. Remove check for __attribute__((cleanup)). However, keep the new `ifavailable' value for systems without dlopen. * src/emacs-module.c (MODULE_HANDLE_NONLOCAL_EXIT): Don't rely on cleanup attribute and correctly reset handlerlist upon longjmp. (MODULE_INTERNAL_CLEANUP): New macro. (module_make_global_ref, module_free_global_ref) (module_make_function, module_get_function_finalizer) (module_set_function_finalizer, module_make_interactive) (module_funcall, module_intern, module_type_of) (module_extract_integer, module_make_integer, module_extract_float) (module_make_float, module_copy_string_contents) (module_make_string, module_make_unibyte_string) (module_make_user_ptr, module_get_user_ptr, module_set_user_ptr) (module_get_user_finalizer, module_set_user_finalizer) (module_vec_set, module_vec_get, module_vec_size) (module_process_input, module_extract_time, module_make_time) (module_extract_big_integer, module_make_big_integer) (module_open_channel): Call MODULE_INTERNAL_CLEANUP prior to returning. diff --git a/configure.ac b/configure.ac index 328b55d96ec..56e293a6baa 100644 --- a/configure.ac +++ b/configure.ac @@ -550,7 +550,7 @@ AC_DEFUN OPTION_DEFAULT_ON([selinux],[don't compile with SELinux support]) OPTION_DEFAULT_ON([gnutls],[don't use -lgnutls for SSL/TLS support]) OPTION_DEFAULT_ON([zlib],[don't compile with zlib decompression support]) -OPTION_DEFAULT_IFAVAILABLE([modules],[don't compile with dynamic modules support]) +OPTION_DEFAULT_ON([modules],[don't compile with dynamic modules support]) OPTION_DEFAULT_ON([threads],[don't compile with elisp threading support]) OPTION_DEFAULT_OFF([cygwin32-native-compilation],[use native compilation on 32-bit Cygwin]) OPTION_DEFAULT_ON([xinput2],[don't use version 2 of the X Input Extension for input]) @@ -4874,90 +4874,36 @@ AC_DEFUN esac fi -if test "${with_modules}" != "no"; then - # __attribute__ ((cleanup)) is required for dynamic modules to - # work. - AC_CACHE_CHECK([for working __attribute__((cleanup))], - [emacs_cv_attribute_cleanup], - [AC_RUN_IFELSE([AC_LANG_PROGRAM([[ - - extern int exit (); - - cleanup_func_1 (k) - int *k; - { - exit (*k - 100); - } - - cleanup_func () - { - int k __attribute__((cleanup (cleanup_func_1))) = 100; - } - - ]], [[cleanup_func (); return 1;]])], - [emacs_cv_attribute_cleanup=yes], - [emacs_cv_attribute_cleanup=no], - [AC_COMPILE_IFELSE([ - AC_LANG_PROGRAM([[ - cleanup_func_1 (k) - int *k; - { - return *k; - }; - cleanup_func () - { - int k __attribute__((cleanup (cleanup_func_1))) = 100; - }]], - [[cleanup_func ()]])], - [emacs_cv_attribute_cleanup="guessing yes"], - [emacs_cv_attribute_cleanup=no])])]) - - if test "$emacs_cv_attribute_cleanup" = "no"; then - if test "${with_modules}" = "ifavailable"; then - AC_MSG_WARN([your compiler does not support cleanup attributes, -and as a result dynamic modules have been disabled]) - else - AC_MSG_ERROR([your compiler is missing the cleanup attribute -required for dynamic modules to work]) - fi - else - case $opsys in - gnu|gnu-linux) - LIBMODULES="-ldl" - HAVE_MODULES=yes - ;; - cygwin|mingw32|darwin) - HAVE_MODULES=yes - ;; - *) - # BSD systems have dlopen in libc. - AC_CHECK_FUNC([dlopen], [HAVE_MODULES=yes]) - ;; - esac - - if test "${HAVE_MODULES}" = no; then - AC_MSG_ERROR([Dynamic modules are not supported on your system]) - else - SAVE_LIBS=$LIBS - LIBS="$LIBS $LIBMODULES" - AC_CHECK_FUNCS([dladdr dlfunc]) - LIBS=$SAVE_LIBS - fi - fi -fi +AS_IF([test "x$with_modules" != "xno"], + [AS_CASE(["$opsys"], + [gnu|gnu-linux], + [LIBMODULES="-ldl" + HAVE_MODULES=yes], + [cygwin|mingw32|darwin], + [HAVE_MODULES=yes], + # BSD systems have dlopen in libc. + [AC_CHECK_FUNC([dlopen], [HAVE_MODULES=yes])]) + + AS_IF([test "x$HAVE_MODULES" = "xno"], + [AS_IF([test "$with_modules" = "ifavailable"], + [AC_MSG_WARN([Dynamic modules are not supported on your system])], + [AC_MSG_ERROR([Dynamic modules are not supported on your system])])], + [SAVE_LIBS=$LIBS + LIBS="$LIBS $LIBMODULES" + AC_CHECK_FUNCS([dladdr dlfunc]) + LIBS=$SAVE_LIBS])]) + +AS_IF([test "x$HAVE_MODULES" = xyes], + [MODULES_OBJ="emacs-module.o" + NEED_DYNLIB=yes + AC_DEFINE([HAVE_MODULES], [1], [Define to 1 if dynamic modules are enabled]) + AC_DEFINE_UNQUOTED([MODULES_SUFFIX], ["$MODULES_SUFFIX"], + [System extension for dynamic libraries]) + AS_IF([test -n "$MODULES_SECONDARY_SUFFIX"], + [AC_DEFINE_UNQUOTED([MODULES_SECONDARY_SUFFIX], + ["$MODULES_SECONDARY_SUFFIX"], + [Alternative system extension for dynamic libraries.])])]) -if test "${HAVE_MODULES}" = yes; then - MODULES_OBJ="emacs-module.o" - NEED_DYNLIB=yes - AC_DEFINE([HAVE_MODULES], [1], [Define to 1 if dynamic modules are enabled]) - AC_DEFINE_UNQUOTED([MODULES_SUFFIX], ["$MODULES_SUFFIX"], - [System extension for dynamic libraries]) - if test -n "${MODULES_SECONDARY_SUFFIX}"; then - AC_DEFINE_UNQUOTED([MODULES_SECONDARY_SUFFIX], - ["$MODULES_SECONDARY_SUFFIX"], - [Alternative system extension for dynamic libraries.]) - fi -fi AC_SUBST([MODULES_OBJ]) AC_SUBST([LIBMODULES]) AC_SUBST([HAVE_MODULES]) diff --git a/src/emacs-module.c b/src/emacs-module.c index d9e564771d0..4719b15c992 100644 --- a/src/emacs-module.c +++ b/src/emacs-module.c @@ -253,10 +253,8 @@ module_decode_utf_8 (const char *str, ptrdiff_t len) /* It is very important that pushing the handler doesn't itself raise a signal. Install the cleanup only after the handler has been - pushed. Use __attribute__ ((cleanup)) to avoid - non-local-exit-prone manual cleanup. This is an extension provided - by GCC and similar compilers; configure prevents module.c from - being compiled when it is not present. + pushed. All code following this point should use + MODULE_INTERNAL_CLEANUP before each return. The do-while forces uses of the macro to be followed by a semicolon. This macro cannot enclose its entire body inside a do-while, as the @@ -276,17 +274,20 @@ #define MODULE_HANDLE_NONLOCAL_EXIT(retval) \ return retval; \ } \ struct handler *internal_cleanup \ - __attribute__ ((cleanup (module_reset_handlerlist))) \ = internal_handler; \ if (sys_setjmp (internal_cleanup->jmp)) \ { \ module_handle_nonlocal_exit (env, \ internal_cleanup->nonlocal_exit, \ internal_cleanup->val); \ + module_reset_handlerlist (&internal_cleanup); \ return retval; \ } \ do { } while (false) +#define MODULE_INTERNAL_CLEANUP \ + module_reset_handlerlist (&internal_cleanup) + /* Implementation of runtime and environment functions. @@ -313,7 +314,10 @@ #define MODULE_HANDLE_NONLOCAL_EXIT(retval) \ Emacs functions, by placing the macro MODULE_HANDLE_NONLOCAL_EXIT right after the above 2 tests. - 5. Do NOT use 'eassert' for checking validity of user code in the + 5. Finally, any code which expands MODULE_HANDLE_NONLOCAL_EXIT + should use MODULE_INTERNAL_CLEANUP prior to returning. + + 6. Do NOT use 'eassert' for checking validity of user code in the module. Instead, make those checks part of the code, and if the check fails, call 'module_non_local_exit_signal_1' or 'module_non_local_exit_throw_1' to report the error. This is @@ -436,6 +440,7 @@ module_make_global_ref (emacs_env *env, emacs_value value) bool overflow = INT_ADD_WRAPV (ref->refcount, 1, &ref->refcount); if (overflow) overflow_error (); + MODULE_INTERNAL_CLEANUP; return &ref->value; } else @@ -448,6 +453,7 @@ module_make_global_ref (emacs_env *env, emacs_value value) Lisp_Object value; XSETPSEUDOVECTOR (value, ref, PVEC_OTHER); hash_put (h, new_obj, value, hashcode); + MODULE_INTERNAL_CLEANUP; return &ref->value; } } @@ -479,6 +485,8 @@ module_free_global_ref (emacs_env *env, emacs_value global_value) if (--ref->refcount == 0) hash_remove_from_table (h, obj); } + + MODULE_INTERNAL_CLEANUP; } static enum emacs_funcall_exit @@ -572,6 +580,8 @@ #define XSET_MODULE_FUNCTION(var, ptr) \ module_make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity, emacs_function func, const char *docstring, void *data) { + emacs_value value; + MODULE_FUNCTION_BEGIN (NULL); if (! (0 <= min_arity @@ -596,7 +606,9 @@ module_make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity, XSET_MODULE_FUNCTION (result, function); eassert (MODULE_FUNCTIONP (result)); - return lisp_to_value (env, result); + value = lisp_to_value (env, result); + MODULE_INTERNAL_CLEANUP; + return value; } static emacs_finalizer @@ -605,6 +617,7 @@ module_get_function_finalizer (emacs_env *env, emacs_value arg) MODULE_FUNCTION_BEGIN (NULL); Lisp_Object lisp = value_to_lisp (arg); CHECK_MODULE_FUNCTION (lisp); + MODULE_INTERNAL_CLEANUP; return XMODULE_FUNCTION (lisp)->finalizer; } @@ -616,6 +629,7 @@ module_set_function_finalizer (emacs_env *env, emacs_value arg, Lisp_Object lisp = value_to_lisp (arg); CHECK_MODULE_FUNCTION (lisp); XMODULE_FUNCTION (lisp)->finalizer = fin; + MODULE_INTERNAL_CLEANUP; } void @@ -635,6 +649,7 @@ module_make_interactive (emacs_env *env, emacs_value function, emacs_value spec) /* Normalize (interactive nil) to (interactive). */ XMODULE_FUNCTION (lisp_fun)->interactive_form = NILP (lisp_spec) ? list1 (Qinteractive) : list2 (Qinteractive, lisp_spec); + MODULE_INTERNAL_CLEANUP; } Lisp_Object @@ -668,21 +683,30 @@ module_funcall (emacs_env *env, emacs_value func, ptrdiff_t nargs, newargs[1 + i] = value_to_lisp (args[i]); emacs_value result = lisp_to_value (env, Ffuncall (nargs1, newargs)); SAFE_FREE (); + MODULE_INTERNAL_CLEANUP; return result; } static emacs_value module_intern (emacs_env *env, const char *name) { + emacs_value tem; + MODULE_FUNCTION_BEGIN (NULL); - return lisp_to_value (env, intern (name)); + tem = lisp_to_value (env, intern (name)); + MODULE_INTERNAL_CLEANUP; + return tem; } static emacs_value module_type_of (emacs_env *env, emacs_value arg) { + emacs_value tem; + MODULE_FUNCTION_BEGIN (NULL); - return lisp_to_value (env, Ftype_of (value_to_lisp (arg))); + tem = lisp_to_value (env, Ftype_of (value_to_lisp (arg))); + MODULE_INTERNAL_CLEANUP; + return tem; } static bool @@ -708,14 +732,20 @@ module_extract_integer (emacs_env *env, emacs_value arg) intmax_t i; if (! integer_to_intmax (lisp, &i)) xsignal1 (Qoverflow_error, lisp); + MODULE_INTERNAL_CLEANUP; return i; } static emacs_value module_make_integer (emacs_env *env, intmax_t n) { + emacs_value value; + MODULE_FUNCTION_BEGIN (NULL); - return lisp_to_value (env, make_int (n)); + value = lisp_to_value (env, make_int (n)); + MODULE_INTERNAL_CLEANUP; + + return value; } static double @@ -724,14 +754,21 @@ module_extract_float (emacs_env *env, emacs_value arg) MODULE_FUNCTION_BEGIN (0); Lisp_Object lisp = value_to_lisp (arg); CHECK_TYPE (FLOATP (lisp), Qfloatp, lisp); + MODULE_INTERNAL_CLEANUP; + return XFLOAT_DATA (lisp); } static emacs_value module_make_float (emacs_env *env, double d) { + emacs_value value; + MODULE_FUNCTION_BEGIN (NULL); - return lisp_to_value (env, make_float (d)); + value = lisp_to_value (env, make_float (d)); + MODULE_INTERNAL_CLEANUP; + + return value; } static bool @@ -763,6 +800,7 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buf, if (buf == NULL) { *len = required_buf_size; + MODULE_INTERNAL_CLEANUP; return true; } @@ -778,36 +816,51 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buf, *len = required_buf_size; memcpy (buf, SDATA (lisp_str_utf8), raw_size + 1); + MODULE_INTERNAL_CLEANUP; return true; } static emacs_value module_make_string (emacs_env *env, const char *str, ptrdiff_t len) { + emacs_value value; + MODULE_FUNCTION_BEGIN (NULL); if (! (0 <= len && len <= STRING_BYTES_BOUND)) overflow_error (); Lisp_Object lstr = len == 0 ? empty_multibyte_string : module_decode_utf_8 (str, len); - return lisp_to_value (env, lstr); + value = lisp_to_value (env, lstr); + MODULE_INTERNAL_CLEANUP; + return value; } static emacs_value module_make_unibyte_string (emacs_env *env, const char *str, ptrdiff_t length) { + emacs_value value; + MODULE_FUNCTION_BEGIN (NULL); if (! (0 <= length && length <= STRING_BYTES_BOUND)) overflow_error (); Lisp_Object lstr = length == 0 ? empty_unibyte_string : make_unibyte_string (str, length); - return lisp_to_value (env, lstr); + value = lisp_to_value (env, lstr); + MODULE_INTERNAL_CLEANUP; + + return value; } static emacs_value module_make_user_ptr (emacs_env *env, emacs_finalizer fin, void *ptr) { + emacs_value value; + MODULE_FUNCTION_BEGIN (NULL); - return lisp_to_value (env, make_user_ptr (fin, ptr)); + value = lisp_to_value (env, make_user_ptr (fin, ptr)); + MODULE_INTERNAL_CLEANUP; + + return value; } static void * @@ -816,6 +869,8 @@ module_get_user_ptr (emacs_env *env, emacs_value arg) MODULE_FUNCTION_BEGIN (NULL); Lisp_Object lisp = value_to_lisp (arg); CHECK_USER_PTR (lisp); + MODULE_INTERNAL_CLEANUP; + return XUSER_PTR (lisp)->p; } @@ -826,6 +881,7 @@ module_set_user_ptr (emacs_env *env, emacs_value arg, void *ptr) Lisp_Object lisp = value_to_lisp (arg); CHECK_USER_PTR (lisp); XUSER_PTR (lisp)->p = ptr; + MODULE_INTERNAL_CLEANUP; } static emacs_finalizer @@ -834,6 +890,7 @@ module_get_user_finalizer (emacs_env *env, emacs_value arg) MODULE_FUNCTION_BEGIN (NULL); Lisp_Object lisp = value_to_lisp (arg); CHECK_USER_PTR (lisp); + MODULE_INTERNAL_CLEANUP; return XUSER_PTR (lisp)->finalizer; } @@ -845,6 +902,7 @@ module_set_user_finalizer (emacs_env *env, emacs_value arg, Lisp_Object lisp = value_to_lisp (arg); CHECK_USER_PTR (lisp); XUSER_PTR (lisp)->finalizer = fin; + MODULE_INTERNAL_CLEANUP; } static void @@ -864,15 +922,21 @@ module_vec_set (emacs_env *env, emacs_value vector, ptrdiff_t index, Lisp_Object lisp = value_to_lisp (vector); check_vec_index (lisp, index); ASET (lisp, index, value_to_lisp (value)); + MODULE_INTERNAL_CLEANUP; } static emacs_value module_vec_get (emacs_env *env, emacs_value vector, ptrdiff_t index) { + emacs_value value; + MODULE_FUNCTION_BEGIN (NULL); Lisp_Object lisp = value_to_lisp (vector); check_vec_index (lisp, index); - return lisp_to_value (env, AREF (lisp, index)); + value = lisp_to_value (env, AREF (lisp, index)); + MODULE_INTERNAL_CLEANUP; + + return value; } static ptrdiff_t @@ -881,6 +945,8 @@ module_vec_size (emacs_env *env, emacs_value vector) MODULE_FUNCTION_BEGIN (0); Lisp_Object lisp = value_to_lisp (vector); CHECK_VECTOR (lisp); + MODULE_INTERNAL_CLEANUP; + return ASIZE (lisp); } @@ -896,23 +962,37 @@ module_should_quit (emacs_env *env) static enum emacs_process_input_result module_process_input (emacs_env *env) { + enum emacs_process_input_result rc; + MODULE_FUNCTION_BEGIN (emacs_process_input_quit); maybe_quit (); - return emacs_process_input_continue; + rc = emacs_process_input_continue; + MODULE_INTERNAL_CLEANUP; + return rc; } static struct timespec module_extract_time (emacs_env *env, emacs_value arg) { + struct timespec value; + MODULE_FUNCTION_BEGIN ((struct timespec) {0}); - return lisp_time_argument (value_to_lisp (arg)); + value = lisp_time_argument (value_to_lisp (arg)); + MODULE_INTERNAL_CLEANUP; + + return value; } static emacs_value module_make_time (emacs_env *env, struct timespec time) { + emacs_value value; + MODULE_FUNCTION_BEGIN (NULL); - return lisp_to_value (env, timespec_to_lisp (time)); + value = lisp_to_value (env, timespec_to_lisp (time)); + MODULE_INTERNAL_CLEANUP; + + return value; } /* @@ -989,7 +1069,10 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, EMACS_INT x = XFIXNUM (o); *sign = (0 < x) - (x < 0); if (x == 0 || count == NULL) - return true; + { + MODULE_INTERNAL_CLEANUP; + return true; + } /* As a simplification we don't check how many array elements are exactly required, but use a reasonable static upper bound. For most architectures exactly one element should @@ -1000,6 +1083,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, if (magnitude == NULL) { *count = required; + MODULE_INTERNAL_CLEANUP; return true; } if (*count < required) @@ -1018,12 +1102,16 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, verify (required * bits < PTRDIFF_MAX); for (ptrdiff_t i = 0; i < required; ++i) magnitude[i] = (emacs_limb_t) (u >> (i * bits)); + MODULE_INTERNAL_CLEANUP; return true; } const mpz_t *x = xbignum_val (o); *sign = mpz_sgn (*x); if (count == NULL) - return true; + { + MODULE_INTERNAL_CLEANUP; + return true; + } size_t required_size = (mpz_sizeinbase (*x, 2) + numb - 1) / numb; eassert (required_size <= PTRDIFF_MAX); ptrdiff_t required = (ptrdiff_t) required_size; @@ -1031,6 +1119,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, if (magnitude == NULL) { *count = required; + MODULE_INTERNAL_CLEANUP; return true; } if (*count < required) @@ -1043,6 +1132,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, size_t written; mpz_export (magnitude, &written, order, size, endian, nails, *x); eassert (written == required_size); + MODULE_INTERNAL_CLEANUP; return true; } @@ -1050,21 +1140,34 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign, module_make_big_integer (emacs_env *env, int sign, ptrdiff_t count, const emacs_limb_t *magnitude) { + emacs_value value; + MODULE_FUNCTION_BEGIN (NULL); if (sign == 0) - return lisp_to_value (env, make_fixed_natnum (0)); + { + value = lisp_to_value (env, make_fixed_natnum (0)); + MODULE_INTERNAL_CLEANUP; + return value; + } enum { order = -1, size = sizeof *magnitude, endian = 0, nails = 0 }; mpz_import (mpz[0], count, order, size, endian, nails, magnitude); if (sign < 0) mpz_neg (mpz[0], mpz[0]); - return lisp_to_value (env, make_integer_mpz ()); + value = lisp_to_value (env, make_integer_mpz ()); + MODULE_INTERNAL_CLEANUP; + return value; } static int module_open_channel (emacs_env *env, emacs_value pipe_process) { + int rc; + MODULE_FUNCTION_BEGIN (-1); - return open_channel_for_module (value_to_lisp (pipe_process)); + rc = open_channel_for_module (value_to_lisp (pipe_process)); + MODULE_INTERNAL_CLEANUP; + + return rc; } commit e9a879260d791ccd509f031ea4e2f3fd645a9672 Author: Po Lu Date: Sat Mar 11 08:34:57 2023 +0800 Implement hourglass cursor on Android * lisp/term/android-win.el (x-pointer-arrow, x-pointer-left-ptr) (x-pointer-left-side, x-pointer-sb-h-double-arrow) (x-pointer-sb-v-double-arrow, x-pointer-watch, x-pointer-xterm) (x-pointer-invisible): New constants. * src/androidterm.c (android_show_hourglass) (android_hide_hourglass): New functions. (android_toggle_visible_pointer, android_define_frame_cursor): Define or don't define hourglass cursor if x->hourglass. (android_redisplay_interface): Add new functions. * src/androidterm.h (struct android_output): New field `hourglass'. diff --git a/lisp/term/android-win.el b/lisp/term/android-win.el index 0984b4d5840..94fe4d6489b 100644 --- a/lisp/term/android-win.el +++ b/lisp/term/android-win.el @@ -203,5 +203,19 @@ android-preedit-text (define-key special-event-map [preedit-text] 'android-preedit-text) + +;; Android cursor shapes, named according to the X scheme. +;; Many X cursors are missing. + +(defconst x-pointer-arrow 1000) +(defconst x-pointer-left-ptr 1000) +(defconst x-pointer-left-side 1020) +(defconst x-pointer-sb-h-double-arrow 1014) +(defconst x-pointer-sb-v-double-arrow 1015) +(defconst x-pointer-watch 1004) +(defconst x-pointer-xterm 1008) +(defconst x-pointer-invisible 0) + + (provide 'android-win) ;; android-win.el ends here. diff --git a/src/androidterm.c b/src/androidterm.c index 3a0f1ccc463..bd7e60dcb3f 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -104,6 +104,45 @@ android_clear_frame (struct frame *f) android_clear_window (FRAME_ANDROID_DRAWABLE (f)); } +static void +android_show_hourglass (struct frame *f) +{ + struct android_output *x; + + /* This isn't implemented like X because a window brings alongside + too many unneeded resources. */ + + x = FRAME_ANDROID_OUTPUT (f); + + /* If the hourglass window is mapped inside a popup menu, input + could be lost if the menu is popped down and the grab is + relinquished, but the hourglass window is still up. Just + avoid displaying the hourglass at all while popups are + active. */ + + if (popup_activated ()) + return; + + x->hourglass = true; + + if (!f->pointer_invisible) + android_define_cursor (FRAME_ANDROID_WINDOW (f), + x->hourglass_cursor); +} + +static void +android_hide_hourglass (struct frame *f) +{ + struct android_output *x; + + x = FRAME_ANDROID_OUTPUT (f); + x->hourglass = false; + + if (!f->pointer_invisible) + android_define_cursor (FRAME_ANDROID_WINDOW (f), + x->current_cursor); +} + static void android_flash (struct frame *f) { @@ -245,7 +284,9 @@ android_toggle_visible_pointer (struct frame *f, bool invisible) dpyinfo->invisible_cursor); else android_define_cursor (FRAME_ANDROID_WINDOW (f), - f->output_data.android->current_cursor); + (FRAME_ANDROID_OUTPUT (f)->hourglass + ? f->output_data.android->hourglass_cursor + : f->output_data.android->current_cursor)); f->pointer_invisible = invisible; } @@ -4032,6 +4073,7 @@ android_draw_glyph_string (struct glyph_string *s) android_define_frame_cursor (struct frame *f, Emacs_Cursor cursor) { if (!f->pointer_invisible + && !FRAME_ANDROID_OUTPUT (f)->hourglass && f->output_data.android->current_cursor != cursor) android_define_cursor (FRAME_ANDROID_WINDOW (f), cursor); @@ -5540,8 +5582,8 @@ android_notify_conversion (unsigned long counter) android_draw_vertical_window_border, android_draw_window_divider, NULL, - NULL, - NULL, + android_show_hourglass, + android_hide_hourglass, android_default_font_parameter, #endif }; diff --git a/src/androidterm.h b/src/androidterm.h index 2e59365b56d..9396d5fe315 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -209,6 +209,9 @@ #define _ANDROID_TERM_H_ Emacs_Cursor bottom_edge_cursor; Emacs_Cursor bottom_left_corner_cursor; + /* Whether or not the hourglass cursor is being displayed. */ + bool hourglass; + /* This is the Emacs structure for the display this frame is on. */ struct android_display_info *display_info; commit 769a4e7ff51c370b055a964ae96e2ace43fc1936 Merge: 1eb546309b2 d236ab09300 Author: Po Lu Date: Sat Mar 11 07:53:45 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 1eb546309b24f41b124a0f94aee4009c6dbd8580 Author: Po Lu Date: Fri Mar 10 19:13:22 2023 +0800 Update Android port * doc/emacs/android.texi (Android Windowing): Document how to pass multimedia keys to the system. * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New function. * java/org/gnu/emacs/EmacsView.java (onKeyDown, onKeyMultiple) (onKeyUp): Check that function. * java/org/gnu/emacs/EmacsWindow.java (defineCursor): Handle cases where cursor is NULL. * src/android.c (NATIVE_NAME): New function. * src/androidfns.c (syms_of_androidfns): New variable. * src/keyboard.c (lispy_function_keys): Add volume keys. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index 8e98b92314a..d50acda7710 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -498,6 +498,16 @@ Android Windowing no way to transfer the contents of the primary selection to another application via cut-and-paste. +@vindex android-pass-multimedia-buttons-to-system +@cindex volume/multimedia buttons, Android + The volume keys are normally reserved by Emacs and used to provide +the ability to quit Emacs without a physical keyboard +(@pxref{On-Screen-Keyboards}.) However, if you want them to adjust +the volume instead, you can set the variable +@code{android-pass-multimedia-buttons-to-system} to a non-@code{nil} +value; note that you will no longer be able to quit Emacs using the +volume buttons in that case. + @node Android Fonts @section Font backends and selection under Android @cindex fonts, android diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index d96c93a83a1..7d13ff99abb 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -174,6 +174,10 @@ public static native long sendExpose (short window, int x, int y, main thread's looper to respond. */ public static native void endSynchronous (); + /* Return whether or not KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP and + KEYCODE_VOLUME_MUTE should be forwarded to Emacs. */ + public static native boolean shouldForwardMultimediaButtons (); + /* Input connection functions. These mostly correspond to their diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 6ace609f386..878ef2f3fbf 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -361,6 +361,12 @@ else if (child.getVisibility () != GONE) public boolean onKeyDown (int keyCode, KeyEvent event) { + if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP + || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) + && !EmacsNative.shouldForwardMultimediaButtons ()) + return false; + window.onKeyDown (keyCode, event); return true; } @@ -369,6 +375,12 @@ else if (child.getVisibility () != GONE) public boolean onKeyMultiple (int keyCode, int repeatCount, KeyEvent event) { + if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP + || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) + && !EmacsNative.shouldForwardMultimediaButtons ()) + return false; + window.onKeyDown (keyCode, event); return true; } @@ -377,6 +389,12 @@ else if (child.getVisibility () != GONE) public boolean onKeyUp (int keyCode, KeyEvent event) { + if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP + || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN + || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) + && !EmacsNative.shouldForwardMultimediaButtons ()) + return false; + window.onKeyUp (keyCode, event); return true; } diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 6be609edcfe..d786c104153 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -1234,7 +1234,10 @@ else if (EmacsWindow.this.isMapped) public void run () { - view.setPointerIcon (cursor.icon); + if (cursor != null) + view.setPointerIcon (cursor.icon); + else + view.setPointerIcon (null); } }); } diff --git a/src/android.c b/src/android.c index e39c34136de..79598ab86df 100644 --- a/src/android.c +++ b/src/android.c @@ -2825,6 +2825,15 @@ NATIVE_NAME (sendExpose) (JNIEnv *env, jobject object, return event_serial; } +JNIEXPORT jboolean JNICALL +NATIVE_NAME (shouldForwardMultimediaButtons) (JNIEnv *env, + jobject object) +{ + /* Yes, android_pass_multimedia_buttons_to_system is being + read from the UI thread. */ + return !android_pass_multimedia_buttons_to_system; +} + /* Forward declarations of deadlock prevention functions. */ static void android_begin_query (void); diff --git a/src/androidfns.c b/src/androidfns.c index 589ae4331cb..e1d423ab3eb 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -3075,6 +3075,17 @@ syms_of_androidfns (void) doc: /* SKIP: real doc in xfns.c. */); Vx_max_tooltip_size = Qnil; + DEFVAR_BOOL ("android-pass-multimedia-buttons-to-system", + android_pass_multimedia_buttons_to_system, + doc: /* Whether or not to pass volume control buttons to the system. +Generally, the `volume-up', `volume-down' and `volume-mute' keys are +processed by Emacs, but setting this to non-nil they are passed to the +operating system instead of being intercepted by Emacs. + +Note that if you set this, you will no longer be able to quit Emacs +using the volume down button. */); + android_pass_multimedia_buttons_to_system = false; + /* Functions defined. */ defsubr (&Sx_create_frame); defsubr (&Sxw_color_defined_p); diff --git a/src/keyboard.c b/src/keyboard.c index a38c7394543..0a74b435f0e 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -4972,6 +4972,7 @@ #define FUNCTION_KEY_OFFSET 0 [111] = "escape", [112] = "delete", [121] = "break", + [120] = "sysrq", [122] = "home", [123] = "end", [124] = "insert", @@ -4988,6 +4989,7 @@ #define FUNCTION_KEY_OFFSET 0 [141] = "f11", [142] = "f12", [160] = "kp-ret", + [164] = "volume-mute", [19] = "up", [20] = "down", [213] = "muhenkan", @@ -4996,6 +4998,8 @@ #define FUNCTION_KEY_OFFSET 0 [218] = "kana", [21] = "left", [22] = "right", + [24] = "volume-up", + [25] = "volume-down", [259] = "help", [268] = "kp-up-left", [269] = "kp-down-left", @@ -5010,6 +5014,7 @@ #define FUNCTION_KEY_OFFSET 0 [66] = "return", [67] = "backspace", [82] = "menu", + [84] = "find", [92] = "prior", [93] = "next", }; commit 98d43dbef5786e02389e883ba5b4aaae7f261b79 Author: Po Lu Date: Fri Mar 10 15:16:13 2023 +0800 * java/org/gnu/emacs/EmacsCursor.java: New file. diff --git a/java/org/gnu/emacs/EmacsCursor.java b/java/org/gnu/emacs/EmacsCursor.java new file mode 100644 index 00000000000..c14c6f2a11b --- /dev/null +++ b/java/org/gnu/emacs/EmacsCursor.java @@ -0,0 +1,47 @@ +/* Communication module for Android terminals. -*- c-file-style: "GNU" -*- + +Copyright (C) 2023 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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 3 of the License, or (at +your option) any later version. + +GNU Emacs 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 GNU Emacs. If not, see . */ + +package org.gnu.emacs; + +import android.view.PointerIcon; +import android.os.Build; + +/* Cursor wrapper. Note that pointer icons are not supported prior to + Android 24. */ + +public final class EmacsCursor extends EmacsHandleObject +{ + /* The pointer icon associated with this cursor. */ + public final PointerIcon icon; + + public + EmacsCursor (short handle, int glyph) + { + super (handle); + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) + { + icon = null; + return; + } + + icon = PointerIcon.getSystemIcon (EmacsService.SERVICE, + glyph); + } +}; commit ae5513ede52536df2cd823699d6168985915ce0f Author: Po Lu Date: Fri Mar 10 15:16:05 2023 +0800 Implement mouse cursors on Android 7.0 and later * java/org/gnu/emacs/EmacsWindow.java (defineCursor): New function. * src/android.c (struct android_emacs_cursor): New struct. (android_init_emacs_cursor): New function. (JNICALL): Call it. (android_create_font_cursor, android_define_cursor) (android_free_cursor): New functions. * src/android.h (enum android_handle_type): Add cursor handle type. * src/androidfns.c (Fx_create_frame, android_create_tip_frame) (enum mouse_cursor, struct mouse_cursor_types, mouse_cursor_types) (struct mouse_cursor_data, android_set_mouse_color) (syms_of_androidfns): * src/androidgui.h (enum android_cursor_shape): * src/androidterm.c (make_invisible_cursor) (android_toggle_invisible_pointer, android_free_frame_resources) (android_define_frame_cursor): * src/androidterm.h (struct android_display_info) (struct android_output): Port mouse cursor code over from X. diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 3569d93136b..6be609edcfe 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -1222,4 +1222,20 @@ else if (EmacsWindow.this.isMapped) } }); } + + public void + defineCursor (final EmacsCursor cursor) + { + /* Don't post this message if pointer icons aren't supported. */ + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + view.post (new Runnable () { + @Override + public void + run () + { + view.setPointerIcon (cursor.icon); + } + }); + } }; diff --git a/src/android.c b/src/android.c index 763e17e9430..e39c34136de 100644 --- a/src/android.c +++ b/src/android.c @@ -155,6 +155,13 @@ #define ANDROID_MAX_ASSET_FD 65535 jmethodID translate_coordinates; jmethodID set_dont_accept_focus; jmethodID set_dont_focus_on_map; + jmethodID define_cursor; +}; + +struct android_emacs_cursor +{ + jclass class; + jmethodID constructor; }; /* The API level of the current device. */ @@ -234,6 +241,9 @@ #define ANDROID_MAX_ASSET_FD 65535 /* Various methods associated with the EmacsWindow class. */ static struct android_emacs_window window_class; +/* Various methods associated with the EmacsCursor class. */ +static struct android_emacs_cursor cursor_class; + /* The last event serial used. This is a 32 bit value, but it is stored in unsigned long to be consistent with X. */ unsigned int event_serial; @@ -2288,6 +2298,38 @@ #define FIND_METHOD(c_name, name, signature) \ "(II)[I"); FIND_METHOD (set_dont_focus_on_map, "setDontFocusOnMap", "(Z)V"); FIND_METHOD (set_dont_accept_focus, "setDontAcceptFocus", "(Z)V"); + FIND_METHOD (define_cursor, "defineCursor", + "(Lorg/gnu/emacs/EmacsCursor;)V"); +#undef FIND_METHOD +} + +static void +android_init_emacs_cursor (void) +{ + jclass old; + + cursor_class.class + = (*android_java_env)->FindClass (android_java_env, + "org/gnu/emacs/EmacsCursor"); + eassert (cursor_class.class); + + old = cursor_class.class; + cursor_class.class + = (jclass) (*android_java_env)->NewGlobalRef (android_java_env, + (jobject) old); + ANDROID_DELETE_LOCAL_REF (old); + + if (!cursor_class.class) + emacs_abort (); + +#define FIND_METHOD(c_name, name, signature) \ + cursor_class.c_name \ + = (*android_java_env)->GetMethodID (android_java_env, \ + cursor_class.class, \ + name, signature); \ + assert (cursor_class.c_name); + + FIND_METHOD (constructor, "", "(SI)V"); #undef FIND_METHOD } @@ -2339,6 +2381,7 @@ NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv, android_init_graphics_point (); android_init_emacs_drawable (); android_init_emacs_window (); + android_init_emacs_cursor (); /* Set HOME to the app data directory. */ setenv ("HOME", android_files_dir, 1); @@ -6243,6 +6286,81 @@ android_asset_fstat (struct android_fd_or_asset asset, return 0; } + + +/* Window cursor support. */ + +android_cursor +android_create_font_cursor (enum android_cursor_shape shape) +{ + android_cursor id; + short prev_max_handle; + jobject object; + + /* First, allocate the cursor handle. */ + prev_max_handle = max_handle; + id = android_alloc_id (); + + if (!id) + error ("Out of cursor handles!"); + + /* Next, create the cursor. */ + object = (*android_java_env)->NewObject (android_java_env, + cursor_class.class, + cursor_class.constructor, + (jshort) id, + (jint) shape); + if (!object) + { + (*android_java_env)->ExceptionClear (android_java_env); + max_handle = prev_max_handle; + memory_full (0); + } + + android_handles[id].type = ANDROID_HANDLE_CURSOR; + android_handles[id].handle + = (*android_java_env)->NewGlobalRef (android_java_env, object); + (*android_java_env)->ExceptionClear (android_java_env); + ANDROID_DELETE_LOCAL_REF (object); + + if (!android_handles[id].handle) + memory_full (0); + + return id; +} + +void +android_define_cursor (android_window window, android_cursor cursor) +{ + jobject window1, cursor1; + jmethodID method; + + window1 = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); + cursor1 = (cursor + ? android_resolve_handle (cursor, ANDROID_HANDLE_CURSOR) + : NULL); + method = window_class.define_cursor; + + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + window1, + window_class.class, + method, cursor1); + android_exception_check (); +} + +void +android_free_cursor (android_cursor cursor) +{ + if (android_handles[cursor].type != ANDROID_HANDLE_CURSOR) + { + __android_log_print (ANDROID_LOG_ERROR, __func__, + "Trying to destroy something not a CURSOR!"); + emacs_abort (); + } + + android_destroy_handle (cursor); +} + #else /* ANDROID_STUBIFY */ /* X emulation functions for Android. */ diff --git a/src/android.h b/src/android.h index 450f3859df9..03592bd955d 100644 --- a/src/android.h +++ b/src/android.h @@ -63,6 +63,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. ANDROID_HANDLE_WINDOW, ANDROID_HANDLE_GCONTEXT, ANDROID_HANDLE_PIXMAP, + ANDROID_HANDLE_CURSOR, }; extern jobject android_resolve_handle (android_handle, diff --git a/src/androidfns.c b/src/androidfns.c index 2724b9595c1..589ae4331cb 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -802,6 +802,7 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, FRAME_BACKGROUND_PIXEL (f) = -1; f->output_data.android->cursor_pixel = -1; f->output_data.android->cursor_foreground_pixel = -1; + f->output_data.android->mouse_pixel = -1; black = build_string ("black"); FRAME_FOREGROUND_PIXEL (f) @@ -812,6 +813,8 @@ DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame, = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); f->output_data.android->cursor_foreground_pixel = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.android->mouse_pixel + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); } /* Set the name; the functions to which we pass f expect the name to @@ -1798,6 +1801,7 @@ android_create_tip_frame (struct android_display_info *dpyinfo, FRAME_BACKGROUND_PIXEL (f) = -1; f->output_data.android->cursor_pixel = -1; f->output_data.android->cursor_foreground_pixel = -1; + f->output_data.android->mouse_pixel = -1; black = build_string ("black"); FRAME_FOREGROUND_PIXEL (f) @@ -1808,6 +1812,8 @@ android_create_tip_frame (struct android_display_info *dpyinfo, = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); f->output_data.android->cursor_foreground_pixel = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); + f->output_data.android->mouse_pixel + = android_decode_color (f, black, BLACK_PIX_DEFAULT (f)); } /* Set the name; the functions to which we pass f expect the name to @@ -2632,13 +2638,147 @@ android_set_menu_bar_lines (struct frame *f, Lisp_Object value, adjust_frame_glyphs (f); } + + +/* These enums must stay in sync with the mouse_cursor_types array + below! */ + +enum mouse_cursor + { + mouse_cursor_text, + mouse_cursor_nontext, + mouse_cursor_hourglass, + mouse_cursor_mode, + mouse_cursor_hand, + mouse_cursor_horizontal_drag, + mouse_cursor_vertical_drag, + mouse_cursor_left_edge, + mouse_cursor_top_left_corner, + mouse_cursor_top_edge, + mouse_cursor_top_right_corner, + mouse_cursor_right_edge, + mouse_cursor_bottom_right_corner, + mouse_cursor_bottom_edge, + mouse_cursor_bottom_left_corner, + mouse_cursor_max + }; + +struct mouse_cursor_types +{ + /* Printable name for error messages (optional). */ + const char *name; + + /* Lisp variable controlling the cursor shape. */ + /* FIXME: A couple of these variables are defined in the C code but + are not actually accessible from Lisp. They should probably be + made accessible or removed. */ + Lisp_Object *shape_var_ptr; + + /* The default shape. */ + int default_shape; +}; + +/* This array must stay in sync with enum mouse_cursor above! */ + +static const struct mouse_cursor_types mouse_cursor_types[] = + { + {"text", &Vx_pointer_shape, ANDROID_XC_XTERM, }, + {"nontext", &Vx_nontext_pointer_shape, ANDROID_XC_LEFT_PTR, }, + {"hourglass", &Vx_hourglass_pointer_shape, ANDROID_XC_WATCH, }, + {"modeline", &Vx_mode_pointer_shape, ANDROID_XC_XTERM, }, + {NULL, &Vx_sensitive_text_pointer_shape, ANDROID_XC_HAND2, }, + {NULL, &Vx_window_horizontal_drag_shape, ANDROID_XC_SB_H_DOUBLE_ARROW, }, + {NULL, &Vx_window_vertical_drag_shape, ANDROID_XC_SB_V_DOUBLE_ARROW, }, + {NULL, &Vx_window_left_edge_shape, ANDROID_XC_LEFT_SIDE, }, + {NULL, &Vx_window_top_left_corner_shape, ANDROID_XC_TOP_LEFT_CORNER, }, + {NULL, &Vx_window_top_edge_shape, ANDROID_XC_TOP_SIDE, }, + {NULL, &Vx_window_top_right_corner_shape, ANDROID_XC_TOP_RIGHT_CORNER, }, + {NULL, &Vx_window_right_edge_shape, ANDROID_XC_RIGHT_SIDE, }, + {NULL, &Vx_window_bottom_right_corner_shape, + ANDROID_XC_BOTTOM_RIGHT_CORNER, }, + {NULL, &Vx_window_bottom_edge_shape, ANDROID_XC_BOTTOM_SIDE, }, + {NULL, &Vx_window_bottom_left_corner_shape, + ANDROID_XC_BOTTOM_LEFT_CORNER, }, + }; + +struct mouse_cursor_data +{ + /* Cursor numbers chosen. */ + unsigned int cursor_num[mouse_cursor_max]; + + /* Allocated Cursor values, or zero for failed attempts. */ + android_cursor cursor[mouse_cursor_max]; +}; + + + static void android_set_mouse_color (struct frame *f, Lisp_Object arg, Lisp_Object oldval) { - /* Changing the mouse color is unsupported under Android, so this is - left intact. */ - android_decode_color (f, arg, BLACK_PIX_DEFAULT (f)); + struct android_output *x = f->output_data.android; + struct mouse_cursor_data cursor_data = { -1, -1 }; + unsigned long pixel = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f)); + unsigned long mask_color = FRAME_BACKGROUND_PIXEL (f); + int i; + + /* Don't let pointers be invisible. */ + if (mask_color == pixel) + pixel = FRAME_FOREGROUND_PIXEL (f); + + x->mouse_pixel = pixel; + + for (i = 0; i < mouse_cursor_max; i++) + { + Lisp_Object shape_var = *mouse_cursor_types[i].shape_var_ptr; + cursor_data.cursor_num[i] + = (!NILP (shape_var) + ? check_uinteger_max (shape_var, UINT_MAX) + : mouse_cursor_types[i].default_shape); + } + + block_input (); + + for (i = 0; i < mouse_cursor_max; i++) + cursor_data.cursor[i] + = android_create_font_cursor (cursor_data.cursor_num[i]); + + if (FRAME_ANDROID_WINDOW (f)) + { + f->output_data.android->current_cursor + = cursor_data.cursor[mouse_cursor_text]; + android_define_cursor (FRAME_ANDROID_WINDOW (f), + f->output_data.android->current_cursor); + } + +#define INSTALL_CURSOR(FIELD, SHORT_INDEX) \ + eassert (x->FIELD \ + != cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX]); \ + if (x->FIELD != 0) \ + android_free_cursor (x->FIELD); \ + x->FIELD = cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX]; + + INSTALL_CURSOR (text_cursor, text); + INSTALL_CURSOR (nontext_cursor, nontext); + INSTALL_CURSOR (hourglass_cursor, hourglass); + INSTALL_CURSOR (modeline_cursor, mode); + INSTALL_CURSOR (hand_cursor, hand); + INSTALL_CURSOR (horizontal_drag_cursor, horizontal_drag); + INSTALL_CURSOR (vertical_drag_cursor, vertical_drag); + INSTALL_CURSOR (left_edge_cursor, left_edge); + INSTALL_CURSOR (top_left_corner_cursor, top_left_corner); + INSTALL_CURSOR (top_edge_cursor, top_edge); + INSTALL_CURSOR (top_right_corner_cursor, top_right_corner); + INSTALL_CURSOR (right_edge_cursor, right_edge); + INSTALL_CURSOR (bottom_right_corner_cursor, bottom_right_corner); + INSTALL_CURSOR (bottom_edge_cursor, bottom_edge); + INSTALL_CURSOR (bottom_left_corner_cursor, bottom_left_corner); + +#undef INSTALL_CURSOR + + unblock_input (); + + update_face_from_frame_parameter (f, Qmouse_color, arg); } static void @@ -2845,6 +2985,81 @@ syms_of_androidfns (void) DEFSYM (Qtrue_color, "true-color"); DEFSYM (Qalways, "always"); + DEFVAR_LISP ("x-pointer-shape", Vx_pointer_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_pointer_shape = Qnil; + +#if false /* This doesn't really do anything. */ + DEFVAR_LISP ("x-nontext-pointer-shape", Vx_nontext_pointer_shape, + doc: /* SKIP: real doc in xfns.c. */); +#endif + Vx_nontext_pointer_shape = Qnil; + + DEFVAR_LISP ("x-hourglass-pointer-shape", Vx_hourglass_pointer_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_hourglass_pointer_shape = Qnil; + + DEFVAR_LISP ("x-sensitive-text-pointer-shape", + Vx_sensitive_text_pointer_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_sensitive_text_pointer_shape = Qnil; + + DEFVAR_LISP ("x-window-horizontal-drag-cursor", + Vx_window_horizontal_drag_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_horizontal_drag_shape = Qnil; + + DEFVAR_LISP ("x-window-vertical-drag-cursor", + Vx_window_vertical_drag_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_vertical_drag_shape = Qnil; + + DEFVAR_LISP ("x-window-left-edge-cursor", + Vx_window_left_edge_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_left_edge_shape = Qnil; + + DEFVAR_LISP ("x-window-top-left-corner-cursor", + Vx_window_top_left_corner_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_top_left_corner_shape = Qnil; + + DEFVAR_LISP ("x-window-top-edge-cursor", + Vx_window_top_edge_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_top_edge_shape = Qnil; + + DEFVAR_LISP ("x-window-top-right-corner-cursor", + Vx_window_top_right_corner_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_top_right_corner_shape = Qnil; + + DEFVAR_LISP ("x-window-right-edge-cursor", + Vx_window_right_edge_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_right_edge_shape = Qnil; + + DEFVAR_LISP ("x-window-bottom-right-corner-cursor", + Vx_window_bottom_right_corner_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_bottom_right_corner_shape = Qnil; + + DEFVAR_LISP ("x-window-bottom-edge-cursor", + Vx_window_bottom_edge_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_bottom_edge_shape = Qnil; + +#if false /* This doesn't really do anything. */ + DEFVAR_LISP ("x-mode-pointer-shape", Vx_mode_pointer_shape, + doc: /* SKIP: real doc in xfns.c. */); +#endif + Vx_mode_pointer_shape = Qnil; + + DEFVAR_LISP ("x-window-bottom-left-corner-cursor", + Vx_window_bottom_left_corner_shape, + doc: /* SKIP: real text in xfns.c. */); + Vx_window_bottom_left_corner_shape = Qnil; + DEFVAR_LISP ("x-cursor-fore-pixel", Vx_cursor_fore_pixel, doc: /* SKIP: real doc in xfns.c. */); Vx_cursor_fore_pixel = Qnil; diff --git a/src/androidgui.h b/src/androidgui.h index 5858a168080..b918d03ceca 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -36,6 +36,7 @@ #define _ANDROID_GUI_H_ typedef android_handle android_window, Emacs_Window; typedef android_handle android_gcontext, GContext; typedef android_handle android_drawable, Drawable; +typedef android_handle android_cursor, Emacs_Cursor; typedef unsigned int android_time; @@ -162,10 +163,6 @@ #define _ANDROID_GUI_H_ enum android_swap_action swap_action; }; -/* Android doesn't support cursors, so define this to something - unused. */ -typedef char Emacs_Cursor; - #define NativeRectangle Emacs_Rectangle #define CONVERT_TO_NATIVE_RECT(xr, nr) ((xr) = (nr)) #define CONVERT_FROM_EMACS_RECT(xr, nr) ((nr) = (xr)) @@ -620,6 +617,29 @@ #define ANDROID_IS_MODIFIER_KEY(key) \ int); extern int android_set_fullscreen (android_window, bool); +enum android_cursor_shape + { + ANDROID_XC_XTERM = 1008, + ANDROID_XC_LEFT_PTR = 1000, + ANDROID_XC_WATCH = 1004, + ANDROID_XC_HAND2 = 1002, + ANDROID_XC_SB_H_DOUBLE_ARROW = 1014, + ANDROID_XC_SB_V_DOUBLE_ARROW = 1015, + ANDROID_XC_LEFT_SIDE = 1020, + ANDROID_XC_TOP_LEFT_CORNER = 1020, + ANDROID_XC_TOP_SIDE = 1020, + ANDROID_XC_TOP_RIGHT_CORNER = 1020, + ANDROID_XC_RIGHT_SIDE = 1020, + ANDROID_XC_BOTTOM_RIGHT_CORNER = 1020, + ANDROID_XC_BOTTOM_SIDE = 1020, + ANDROID_XC_BOTTOM_LEFT_CORNER = 1020, + ANDROID_XC_NULL = 0, + }; + +extern android_cursor android_create_font_cursor (enum android_cursor_shape); +extern void android_define_cursor (android_window, android_cursor); +extern void android_free_cursor (android_cursor); + #endif diff --git a/src/androidterm.c b/src/androidterm.c index 2e2bf86706c..3a0f1ccc463 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -224,10 +224,38 @@ android_ring_bell (struct frame *f) } } +static android_cursor +make_invisible_cursor (struct android_display_info *dpyinfo) +{ + return android_create_font_cursor (ANDROID_XC_NULL); +} + static void -android_toggle_invisible_pointer (struct frame *f, bool invisible) +android_toggle_visible_pointer (struct frame *f, bool invisible) { + struct android_display_info *dpyinfo; + + dpyinfo = FRAME_DISPLAY_INFO (f); + + if (!dpyinfo->invisible_cursor) + dpyinfo->invisible_cursor = make_invisible_cursor (dpyinfo); + + if (invisible) + android_define_cursor (FRAME_ANDROID_WINDOW (f), + dpyinfo->invisible_cursor); + else + android_define_cursor (FRAME_ANDROID_WINDOW (f), + f->output_data.android->current_cursor); + f->pointer_invisible = invisible; +} + +static void +android_toggle_invisible_pointer (struct frame *f, bool invisible) +{ + block_input (); + android_toggle_visible_pointer (f, invisible); + unblock_input (); } /* Start an update of frame F. This function is installed as a hook @@ -2040,6 +2068,38 @@ android_free_frame_resources (struct frame *f) android_free_gcs (f); + /* Free cursors. */ + if (f->output_data.android->text_cursor) + android_free_cursor (f->output_data.android->text_cursor); + if (f->output_data.android->nontext_cursor) + android_free_cursor (f->output_data.android->nontext_cursor); + if (f->output_data.android->modeline_cursor) + android_free_cursor (f->output_data.android->modeline_cursor); + if (f->output_data.android->hand_cursor) + android_free_cursor (f->output_data.android->hand_cursor); + if (f->output_data.android->hourglass_cursor) + android_free_cursor (f->output_data.android->hourglass_cursor); + if (f->output_data.android->horizontal_drag_cursor) + android_free_cursor (f->output_data.android->horizontal_drag_cursor); + if (f->output_data.android->vertical_drag_cursor) + android_free_cursor (f->output_data.android->vertical_drag_cursor); + if (f->output_data.android->left_edge_cursor) + android_free_cursor (f->output_data.android->left_edge_cursor); + if (f->output_data.android->top_left_corner_cursor) + android_free_cursor (f->output_data.android->top_left_corner_cursor); + if (f->output_data.android->top_edge_cursor) + android_free_cursor (f->output_data.android->top_edge_cursor); + if (f->output_data.android->top_right_corner_cursor) + android_free_cursor (f->output_data.android->top_right_corner_cursor); + if (f->output_data.android->right_edge_cursor) + android_free_cursor (f->output_data.android->right_edge_cursor); + if (f->output_data.android->bottom_right_corner_cursor) + android_free_cursor (f->output_data.android->bottom_right_corner_cursor); + if (f->output_data.android->bottom_edge_cursor) + android_free_cursor (f->output_data.android->bottom_edge_cursor); + if (f->output_data.android->bottom_left_corner_cursor) + android_free_cursor (f->output_data.android->bottom_left_corner_cursor); + /* Free extra GCs allocated by android_setup_relief_colors. */ if (f->output_data.android->white_relief.gc) { @@ -3971,7 +4031,11 @@ android_draw_glyph_string (struct glyph_string *s) static void android_define_frame_cursor (struct frame *f, Emacs_Cursor cursor) { - /* Not supported, because cursors are not supported on Android. */ + if (!f->pointer_invisible + && f->output_data.android->current_cursor != cursor) + android_define_cursor (FRAME_ANDROID_WINDOW (f), cursor); + + f->output_data.android->current_cursor = cursor; } static void diff --git a/src/androidterm.h b/src/androidterm.h index 9964eb54880..2e59365b56d 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -136,6 +136,9 @@ #define _ANDROID_TERM_H_ /* ID of the last menu event received. -1 means Emacs is waiting for a context menu event. */ int menu_event_id; + + /* The invisible cursor used for pointer blanking. */ + android_cursor invisible_cursor; }; /* Structure representing a single tool (finger or stylus) pressed @@ -176,6 +179,7 @@ #define _ANDROID_TERM_H_ /* Various colors. */ unsigned long cursor_pixel; + unsigned long mouse_pixel; unsigned long cursor_foreground_pixel; /* Foreground color for scroll bars. A value of -1 means use the @@ -187,7 +191,7 @@ #define _ANDROID_TERM_H_ bars). */ unsigned long scroll_bar_background_pixel; - /* Unused stuff (cursors). */ + /* Cursors associated with this frame. */ Emacs_Cursor text_cursor; Emacs_Cursor nontext_cursor; Emacs_Cursor modeline_cursor; commit a7c8ae7d675c402923e2ff8359b9cc286792bbb0 Author: Po Lu Date: Fri Mar 10 11:39:30 2023 +0800 ; * java/org/gnu/emacs/EmacsNative.java: Add missing dependency. diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 11da5db8746..d96c93a83a1 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -229,7 +229,8 @@ public static native ExtractedText getExtractedText (short window, "tasn1_emacs", "hogweed_emacs", "jansson_emacs", "jpeg_emacs", "tiff_emacs", "xml2_emacs", - "icuuc_emacs", }; + "icuuc_emacs", + "tree-sitter_emacs", }; for (String dependency : libraryDeps) { commit 417e0539cfe5af5ab20ffc2342b0796ad5c0ef1b Author: Po Lu Date: Fri Mar 10 10:02:36 2023 +0800 Avoid using Linux sysfs APIs to access battery state on Android * lisp/battery.el (battery-status-function): Don't look for /sys, /proc* on Android. Explain why. diff --git a/lisp/battery.el b/lisp/battery.el index a51bc5267b3..c55fcbbee8c 100644 --- a/lisp/battery.el +++ b/lisp/battery.el @@ -97,20 +97,21 @@ battery--find-linux-sysfs-batteries (defcustom battery-status-function (cond ((member battery-upower-service (dbus-list-activatable-names)) #'battery-upower) - ;; Try to find the relevant devices in /sys and /proc on - ;; Android as well, in case the system makes them available. - ((and (memq system-type '(gnu/linux android)) + ((and (eq system-type 'gnu/linux) (file-readable-p "/sys/") (battery--find-linux-sysfs-batteries)) #'battery-linux-sysfs) - ((and (memq system-type '(gnu/linux android)) + ((and (eq system-type 'gnu/linux) (file-directory-p "/proc/acpi/battery")) #'battery-linux-proc-acpi) - ((and (memq system-type '(gnu/linux android)) + ((and (eq system-type 'gnu/linux) (file-readable-p "/proc/") (file-readable-p "/proc/apm")) #'battery-linux-proc-apm) ;; Now try the Android battery status function. + ;; Note that even though the Linux kernel APIs are sometimes + ;; available on Android, they are badly implemented by Android + ;; kernels, so avoid using those. ((eq system-type 'android) #'battery-android) ((and (eq system-type 'berkeley-unix) commit 488a75f2e2b73038ff341f3484a8cf8584633eff Author: Po Lu Date: Fri Mar 10 09:40:41 2023 +0800 Port Android battery status to Android 4.4 and earlier * java/org/gnu/emacs/EmacsService.java (EmacsService) (queryBattery19): New function. (queryBattery): Call it on old systems. Also, return AC line status and temperature. * lisp/battery.el (battery-android): Implement more format directives. * src/android.c (android_query_battery): Handle new status fields. * src/android.h (struct android_battery_state): Add `plugged' and `temperature'. * src/androidfns.c (Fandroid_query_battery): Return new fields. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index 848ad4de789..9c48c56ca26 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -40,6 +40,7 @@ import android.content.Context; import android.content.ContentResolver; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.content.pm.PackageManager; @@ -738,6 +739,36 @@ invocation of app_process (through android-emacs) can } } + private long[] + queryBattery19 () + { + IntentFilter filter; + Intent battery; + long capacity, chargeCounter, currentAvg, currentNow; + long status, remaining, plugged, temp; + + filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED); + battery = registerReceiver (null, filter); + + if (battery == null) + return null; + + capacity = battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 0); + chargeCounter + = (battery.getIntExtra (BatteryManager.EXTRA_SCALE, 0) + / battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 100) * 100); + currentAvg = 0; + currentNow = 0; + status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0); + remaining = -1; + plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0); + temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0); + + return new long[] { capacity, chargeCounter, currentAvg, + currentNow, remaining, status, plugged, + temp, }; + } + /* Return the status of the battery. See struct android_battery_status for the order of the elements returned. @@ -750,14 +781,16 @@ invocation of app_process (through android-emacs) can Object tem; BatteryManager manager; long capacity, chargeCounter, currentAvg, currentNow; - long status, remaining; + long status, remaining, plugged, temp; int prop; + IntentFilter filter; + Intent battery; - /* Android 4.4 or earlier require applications to listen to - changes to the battery instead of querying for its status. */ + /* Android 4.4 or earlier require applications to use a different + API to query the battery status. */ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) - return null; + return queryBattery19 (); tem = getSystemService (Context.BATTERY_SERVICE); manager = (BatteryManager) tem; @@ -776,7 +809,8 @@ invocation of app_process (through android-emacs) can only return ``charging'' or ``discharging''. */ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) - status = manager.getIntProperty (BatteryManager.BATTERY_PROPERTY_STATUS); + status + = manager.getIntProperty (BatteryManager.BATTERY_PROPERTY_STATUS); else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) status = (manager.isCharging () ? BatteryManager.BATTERY_STATUS_CHARGING @@ -789,8 +823,27 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) remaining = manager.computeChargeTimeRemaining (); + plugged = -1; + temp = -1; + + /* Now obtain additional information from the battery manager. */ + + filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED); + battery = registerReceiver (null, filter); + + if (battery != null) + { + plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0); + temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0); + + /* Make status more reliable. */ + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) + status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0); + } + return new long[] { capacity, chargeCounter, currentAvg, - currentNow, remaining, status, }; + currentNow, remaining, status, plugged, + temp, }; } /* Display the specified STRING in a small dialog box on the main diff --git a/lisp/battery.el b/lisp/battery.el index a2bbd463c12..a51bc5267b3 100644 --- a/lisp/battery.el +++ b/lisp/battery.el @@ -1089,9 +1089,11 @@ battery-android The following %-sequences are provided: %c Current capacity (mAh) %r Current rate of charge or discharge (mA) +%L AC line status (verbose). %B Battery status (verbose) %b Battery status, empty means high, `-' means low, `+' means charging and `?' means unknown. +%d Temperature (in degrees Celsius) %p Battery load percentage. %m Remaining time (to charge) in minutes. %h Remaining time (to charge) in hours. @@ -1139,7 +1141,14 @@ battery-android (cons ?m (or minutes "N/A")) (cons ?h (or hours "N/A")) (cons ?t (or remaining "N/A")) - (cons ?L "N/A"))))) + (cons ?L (cl-case (nth 6 status) + (0 "off-line") + (1 "on-line") + (2 "on-line (dock)") + (3 "on-line (USB)") + (4 "on-line (wireless)") + (t "unknown"))) + (cons ?t (/ (or (nth 7 status) 0) 10.0)))))) ;;; Private functions. diff --git a/src/android.c b/src/android.c index 69c87e731bd..763e17e9430 100644 --- a/src/android.c +++ b/src/android.c @@ -5754,7 +5754,7 @@ android_get_current_api_level (void) } /* Query the status of the battery, and place it in *STATUS. - Value is 1 if the system is too old, else 0. */ + Value is 1 upon failure, else 0. */ int android_query_battery (struct android_battery_state *status) @@ -5783,6 +5783,8 @@ android_query_battery (struct android_battery_state *status) status->current_now = longs[3]; status->remaining = longs[4]; status->status = longs[5]; + status->plugged = longs[6]; + status->temperature = longs[7]; (*android_java_env)->ReleaseLongArrayElements (android_java_env, array, longs, diff --git a/src/android.h b/src/android.h index ed0089ad94e..450f3859df9 100644 --- a/src/android.h +++ b/src/android.h @@ -160,6 +160,18 @@ Copyright (C) 2023 Free Software Foundation, Inc. but is not charging either. 1, if the battery state is unknown. */ int status; + + /* The power source of the battery. Value is: + + 0, if on battery power. + 1, for line power. + 8, for dock power. + 2, for USB power. + 4, for wireless power. */ + int plugged; + + /* The temperature of the battery in 10 * degrees centigrade. */ + int temperature; }; extern Lisp_Object android_browse_url (Lisp_Object); diff --git a/src/androidfns.c b/src/androidfns.c index 5a23e8bd196..2724b9595c1 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -2797,11 +2797,13 @@ android_set_no_accept_focus (struct frame *f, Lisp_Object new_value, DEFUN ("android-query-battery", Fandroid_query_battery, Sandroid_query_battery, 0, 0, 0, doc: /* Perform a query for battery information. -This function will not work before Android 5.0. Value is nil upon failure, or a list of the form: (CAPACITY CHARGE-COUNTER CURRENT-AVERAGE CURRENT-NOW STATUS - REMAINING) + REMAINING PLUGGED TEMP) + +where REMAINING, CURRENT-AVERAGE, and CURRENT-NOW are undefined prior +to Android 5.0. See the documentation at @@ -2822,12 +2824,14 @@ DEFUN ("android-query-battery", Fandroid_query_battery, if (android_query_battery (&state)) return Qnil; - return listn (6, make_int (state.capacity), - make_int (state.charge_counter), + return listn (8, make_int (state.capacity), + make_fixnum (state.charge_counter), make_int (state.current_average), make_int (state.current_now), - make_int (state.status), - make_int (state.remaining)); + make_fixnum (state.status), + make_int (state.remaining), + make_fixnum (state.plugged), + make_fixnum (state.temperature)); } #endif commit 4392423cb6df5a8af9a0520da04378e189fd387e Merge: f34d9fab892 8ee205d2325 Author: Po Lu Date: Fri Mar 10 08:43:13 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit f34d9fab892ac06b1cd5ddbf05140502d6692754 Merge: a7f0f9498f2 26740f30469 Author: Po Lu Date: Thu Mar 9 19:57:06 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit a7f0f9498f2ce303f73764817e49d0ea0e2e5c9c Author: Po Lu Date: Thu Mar 9 19:42:13 2023 +0800 Update Android port * src/android.c (android_destroy_handle): Handle OOM errors in android_destroy_handle. diff --git a/src/android.c b/src/android.c index e2ae77e30d0..69c87e731bd 100644 --- a/src/android.c +++ b/src/android.c @@ -2914,6 +2914,13 @@ android_destroy_handle (android_handle handle) (*android_java_env)->CallVoidMethod (android_java_env, android_handles[handle].handle, method); + + /* Just clear any exception thrown. If destroying the handle + fails from an out-of-memory error, then Emacs loses some + resources, but that is not as big deal as signalling. */ + (*android_java_env)->ExceptionClear (android_java_env); + + /* Delete the global reference regardless of any error. */ (*android_java_env)->DeleteGlobalRef (android_java_env, android_handles[handle].handle); android_handles[handle].handle = NULL; commit 7e3c22536f86d468168a1918b3df24f48e3e7d92 Author: Po Lu Date: Thu Mar 9 19:05:24 2023 +0800 ; * textconv.c: Remove out-of-date comment. diff --git a/src/textconv.c b/src/textconv.c index c1ce83b1d7d..3206bb48204 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -39,12 +39,8 @@ Copyright (C) 2023 Free Software Foundation, Inc. -/* The window system's text conversion interface. - NULL when the window system has not set up text conversion. - - This interface will later be heavily extended on the - feature/android branch to deal with Android's much less - straightforward text conversion protocols. */ +/* The window system's text conversion interface. NULL when the + window system has not set up text conversion. */ static struct textconv_interface *text_interface; commit e859a14bee7a84a3aaed45770c89ef60c68b3e08 Author: Po Lu Date: Thu Mar 9 16:30:02 2023 +0800 Fix menu and popup race conditions on Android * java/org/gnu/emacs/EmacsActivity.java (onContextMenuClosed): * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) (onMenuItemClick, run): * java/org/gnu/emacs/EmacsDialog.java (EmacsDialog, onClick) (createDialog, onDismiss): Take menu event serial, and pass it along in context menu events. * java/org/gnu/emacs/EmacsNative.java (sendContextMenu): New argument. * src/android.c (sendContextMenu): Pass serial number in event. * src/androidgui.h (struct android_menu_event): New field `menu_event_serial'. * src/androidmenu.c (FIND_METHOD_STATIC) (android_init_emacs_context_menu): Adjust method declarations. (android_menu_show, android_dialog_show): * src/androidterm.c (handle_one_android_event): Expect serial in context menu events. * src/androidterm.h: Update prototypes. diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 692d8a14e22..735a464be8e 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -315,6 +315,8 @@ public class EmacsActivity extends Activity public final void onContextMenuClosed (Menu menu) { + int serial; + Log.d (TAG, "onContextMenuClosed: " + menu); /* See the comment inside onMenuItemClick. */ @@ -335,7 +337,11 @@ public class EmacsActivity extends Activity /* Send a context menu event given that no menu item has already been selected. */ if (!EmacsContextMenu.itemAlreadySelected) - EmacsNative.sendContextMenu ((short) 0, 0); + { + serial = EmacsContextMenu.lastMenuEventSerial; + EmacsNative.sendContextMenu ((short) 0, 0, + serial); + } super.onContextMenuClosed (menu); } diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index 0553ff9d4a3..abc1869ac6a 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -49,6 +49,9 @@ public final class EmacsContextMenu /* Whether or not a submenu was selected. */ public static boolean wasSubmenuSelected; + /* The serial ID of the last context menu to be displayed. */ + public static int lastMenuEventSerial; + private static class Item implements MenuItem.OnMenuItemClickListener { public int itemID; @@ -106,7 +109,8 @@ private static class Item implements MenuItem.OnMenuItemClickListener } /* Send a context menu event. */ - EmacsNative.sendContextMenu ((short) 0, itemID); + EmacsNative.sendContextMenu ((short) 0, itemID, + lastMenuEventSerial); /* Say that an item has already been selected. */ itemAlreadySelected = true; @@ -293,12 +297,13 @@ private static class Item implements MenuItem.OnMenuItemClickListener false); } - /* Display this context menu on WINDOW, at xPosition and - yPosition. */ + /* Display this context menu on WINDOW, at xPosition and yPosition. + SERIAL is a number that will be returned in any menu event + generated to identify this context menu. */ public boolean display (final EmacsWindow window, final int xPosition, - final int yPosition) + final int yPosition, final int serial) { Runnable runnable; final Holder rc; @@ -312,6 +317,7 @@ private static class Item implements MenuItem.OnMenuItemClickListener { synchronized (this) { + lastMenuEventSerial = serial; rc.thing = display1 (window, xPosition, yPosition); notify (); } diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java index 80a5e5f7369..aed84de29e5 100644 --- a/java/org/gnu/emacs/EmacsDialog.java +++ b/java/org/gnu/emacs/EmacsDialog.java @@ -57,6 +57,9 @@ public final class EmacsDialog implements DialogInterface.OnDismissListener /* Dialog to dismiss after click. */ private AlertDialog dismissDialog; + /* The menu serial associated with this dialog box. */ + private int menuEventSerial; + private class EmacsButton implements View.OnClickListener, DialogInterface.OnClickListener { @@ -76,7 +79,7 @@ private class EmacsButton implements View.OnClickListener, Log.d (TAG, "onClicked " + this); wasButtonClicked = true; - EmacsNative.sendContextMenu ((short) 0, id); + EmacsNative.sendContextMenu ((short) 0, id, menuEventSerial); dismissDialog.dismiss (); } @@ -87,15 +90,16 @@ private class EmacsButton implements View.OnClickListener, Log.d (TAG, "onClicked " + this); wasButtonClicked = true; - EmacsNative.sendContextMenu ((short) 0, id); + EmacsNative.sendContextMenu ((short) 0, id, menuEventSerial); } }; /* Create a popup dialog with the title TITLE and the text TEXT. - TITLE may be NULL. */ + TITLE may be NULL. MENUEVENTSERIAL is a number which will + identify this popup dialog inside events it sends. */ public static EmacsDialog - createDialog (String title, String text) + createDialog (String title, String text, int menuEventSerial) { EmacsDialog dialog; @@ -103,6 +107,7 @@ private class EmacsButton implements View.OnClickListener, dialog.buttons = new ArrayList (); dialog.title = title; dialog.text = text; + dialog.menuEventSerial = menuEventSerial; return dialog; } @@ -330,6 +335,6 @@ private class EmacsButton implements View.OnClickListener, if (wasButtonClicked) return; - EmacsNative.sendContextMenu ((short) 0, 0); + EmacsNative.sendContextMenu ((short) 0, 0, menuEventSerial); } }; diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 8e626b9534b..11da5db8746 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -155,7 +155,8 @@ public static native long sendWheel (short window, int x, int y, public static native long sendDeiconified (short window); /* Send an ANDROID_CONTEXT_MENU event. */ - public static native long sendContextMenu (short window, int menuEventID); + public static native long sendContextMenu (short window, int menuEventID, + int menuEventSerial); /* Send an ANDROID_EXPOSE event. */ public static native long sendExpose (short window, int x, int y, diff --git a/src/android.c b/src/android.c index b14d2845544..e2ae77e30d0 100644 --- a/src/android.c +++ b/src/android.c @@ -2744,7 +2744,8 @@ NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object, JNIEXPORT jlong JNICALL NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object, - jshort window, jint menu_event_id) + jshort window, jint menu_event_id, + jint menu_event_serial) { JNI_STACK_ALIGNMENT_PROLOGUE; @@ -2754,6 +2755,7 @@ NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object, event.menu.serial = ++event_serial; event.menu.window = window; event.menu.menu_event_id = menu_event_id; + event.menu.menu_event_serial = menu_event_serial; android_write_event (&event); return event_serial; diff --git a/src/androidgui.h b/src/androidgui.h index afcaed98cae..5858a168080 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -418,6 +418,9 @@ #define ANDROID_IS_MODIFIER_KEY(key) \ /* Menu event ID. */ int menu_event_id; + + /* Menu event serial; this counter identifies the context menu. */ + int menu_event_serial; }; enum android_ime_operation diff --git a/src/androidmenu.c b/src/androidmenu.c index 540b25cf602..7d9c33e28b1 100644 --- a/src/androidmenu.c +++ b/src/androidmenu.c @@ -35,6 +35,11 @@ Copyright (C) 2023 Free Software Foundation, Inc. static int popup_activated_flag; +/* Serial number used to identify which context menu events are + associated with the context menu currently being displayed. */ + +unsigned int current_menu_serial; + int popup_activated (void) { @@ -96,7 +101,8 @@ #define FIND_METHOD_STATIC(c_name, name, signature) \ eassert (menu_class.c_name); FIND_METHOD_STATIC (create_context_menu, "createContextMenu", - "(Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;"); + "(Ljava/lang/String;)" + "Lorg/gnu/emacs/EmacsContextMenu;"); FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ" "Ljava/lang/String;)V"); @@ -105,7 +111,7 @@ #define FIND_METHOD_STATIC(c_name, name, signature) \ "Lorg/gnu/emacs/EmacsContextMenu;"); FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V"); FIND_METHOD (parent, "parent", "()Lorg/gnu/emacs/EmacsContextMenu;"); - FIND_METHOD (display, "display", "(Lorg/gnu/emacs/EmacsWindow;II)Z"); + FIND_METHOD (display, "display", "(Lorg/gnu/emacs/EmacsWindow;III)Z"); FIND_METHOD (dismiss, "dismiss", "(Lorg/gnu/emacs/EmacsWindow;)V"); #undef FIND_METHOD @@ -254,8 +260,10 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, struct android_menu_subprefix *subprefix, *temp_subprefix; struct android_menu_subprefix *subprefix_1; bool checkmark; + unsigned int serial; count = SPECPDL_INDEX (); + serial = ++current_menu_serial; block_input (); @@ -458,7 +466,8 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, context_menu, menu_class.display, window, (jint) x, - (jint) y); + (jint) y, + (jint) serial); android_exception_check (); if (!rc) @@ -606,7 +615,7 @@ #define FIND_METHOD_STATIC(c_name, name, signature) \ name, signature); \ FIND_METHOD_STATIC (create_dialog, "createDialog", "(Ljava/lang/String;" - "Ljava/lang/String;)Lorg/gnu/emacs/EmacsDialog;"); + "Ljava/lang/String;I)Lorg/gnu/emacs/EmacsDialog;"); FIND_METHOD (add_button, "addButton", "(Ljava/lang/String;IZ)V"); FIND_METHOD (display, "display", "()Z"); @@ -625,6 +634,10 @@ android_dialog_show (struct frame *f, Lisp_Object title, bool rc; int id; jmethodID method; + unsigned int serial; + + /* Generate a unique ID for events from this dialog box. */ + serial = ++current_menu_serial; if (menu_items_n_panes > 1) { @@ -651,7 +664,8 @@ android_dialog_show (struct frame *f, Lisp_Object title, dialog = (*android_java_env)->CallStaticObjectMethod (android_java_env, dialog_class.class, method, java_header, - java_title); + java_title, + (jint) serial); android_exception_check (); /* Delete now unused local references. */ diff --git a/src/androidterm.c b/src/androidterm.c index b502b1c6de5..2e2bf86706c 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -1457,7 +1457,12 @@ handle_one_android_event (struct android_display_info *dpyinfo, /* Context menu handling. */ case ANDROID_CONTEXT_MENU: - if (dpyinfo->menu_event_id == -1) + if (dpyinfo->menu_event_id == -1 + /* Previously displayed popup menus might generate events + after dismissal, which might interfere. + `current_menu_serial' is always set to an identifier + identifying the last context menu to be displayed. */ + && event->menu.menu_event_serial == current_menu_serial) dpyinfo->menu_event_id = event->menu.menu_event_id; goto OTHER; diff --git a/src/androidterm.h b/src/androidterm.h index 9bd11bb7853..9964eb54880 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -421,6 +421,12 @@ #define XSCROLL_BAR(vec) ((struct scroll_bar *) XVECTOR (vec)) /* Defined in androidmenu.c. */ +#ifndef ANDROID_STUBIFY + +extern unsigned int current_menu_serial; + +#endif + extern Lisp_Object android_menu_show (struct frame *, int, int, int, Lisp_Object, const char **); extern Lisp_Object android_popup_dialog (struct frame *, Lisp_Object, commit 745890de5204850bb4173c19ceb79c698acb7a20 Author: Po Lu Date: Thu Mar 9 14:57:45 2023 +0800 Fix webp test for Android * configure.ac (HAVE_WEBP): Disable WebPGetInfo check when REALLY_ANDROID. diff --git a/configure.ac b/configure.ac index a5c8b3cb3a9..328b55d96ec 100644 --- a/configure.ac +++ b/configure.ac @@ -3455,12 +3455,13 @@ AC_DEFUN CFLAGS="$CFLAGS $WEBP_CFLAGS" LIBS="$LIBS $WEBP_LIBS" - AC_CHECK_FUNC([WebPGetInfo], [], - [WEBP_MODULE="$WEBP_MODULE libwebpdecoder >= $WEBP_REQUIRED" - HAVE_WEBP=no - AS_UNSET([WEBP_LIBS]) - AS_UNSET([WEBP_CFLAGS]) - EMACS_CHECK_MODULES([WEBP], [$WEBP_MODULE])]) + AS_IF([test "$REALLY_ANDROID" != "yes"], [ + AC_CHECK_FUNC([WebPGetInfo], [], + [WEBP_MODULE="$WEBP_MODULE libwebpdecoder >= $WEBP_REQUIRED" + HAVE_WEBP=no + AS_UNSET([WEBP_LIBS]) + AS_UNSET([WEBP_CFLAGS]) + EMACS_CHECK_MODULES([WEBP], [$WEBP_MODULE])])]) CFLAGS=$OLD_CFLAGS LIBS=$OLD_LIBS commit c99354042a676acf218aa8cda20b3d100240c207 Merge: 682a6542cd7 5056b8e5897 Author: Po Lu Date: Thu Mar 9 14:53:37 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 682a6542cd7298e5116cd37651f762e084b6a5c0 Author: Po Lu Date: Thu Mar 9 14:50:32 2023 +0800 Update Android port * java/debug.sh (is_root): Port to android versions which don't support `chmod +x'. * src/android.c (android_content_name_p): Disable before API level 19. diff --git a/java/debug.sh b/java/debug.sh index 83690e0b536..339b3604810 100755 --- a/java/debug.sh +++ b/java/debug.sh @@ -284,7 +284,7 @@ is_root= if (adb -s $device shell ls /system/bin | grep -G tee); then # Copy it to the user directory. adb -s $device shell "$gdbserver_cat" - adb -s $device shell "run-as $package chmod +x gdbserver" + adb -s $device shell "run-as $package chmod 777 gdbserver" gdbserver_cmd="./gdbserver" else # Hopefully this is an old version of Android which allows diff --git a/src/android.c b/src/android.c index 5420fbde9b9..b14d2845544 100644 --- a/src/android.c +++ b/src/android.c @@ -994,6 +994,12 @@ android_get_asset_name (const char *filename) static bool android_content_name_p (const char *filename) { + /* Content URIs aren't supported before Android 4.4, so return + false. */ + + if (android_api_level < 19) + return false; + return (!strcmp (filename, "/content") || !strncmp (filename, "/content/", sizeof "/content/" - 1)); commit dcc3c63c6e6f2536341b22cb428ef4755a171e6b Author: Po Lu Date: Thu Mar 9 11:27:00 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsContextMenu.java (EmacsContextMenu) (addItem): New argument `tooltip'. diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index dec5e148a8e..0553ff9d4a3 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -139,11 +139,14 @@ private static class Item implements MenuItem.OnMenuItemClickListener If this is not a submenu and ISCHECKABLE is set, make the item checkable. Likewise, if ISCHECKED is set, make the item - checked. */ + checked. + + If TOOLTIP is non-NULL, set the menu item tooltip to TOOLTIP. */ public void addItem (int itemID, String itemName, boolean isEnabled, - boolean isCheckable, boolean isChecked) + boolean isCheckable, boolean isChecked, + String tooltip) { Item item; @@ -153,6 +156,7 @@ private static class Item implements MenuItem.OnMenuItemClickListener item.isEnabled = isEnabled; item.isCheckable = isCheckable; item.isChecked = isChecked; + item.tooltip = tooltip; menuItems.add (item); } commit 55634b5f79ffb723eebe4a2f6c773213011e6a61 Author: Po Lu Date: Thu Mar 9 10:50:03 2023 +0800 Update Android port * src/android.c (android_build_string): Convert the text to UTF-16, and create the Java string using that. (android_build_jstring): Update comment. * src/androidmenu.c (android_init_emacs_context_menu): Add String argument to `addItem'. (android_menu_show): Correctly pass help strings in regular menu items. * src/sfnt.c (_sfnt_swap16, _sfnt_swap32): Avoid reserved names. diff --git a/src/android.c b/src/android.c index e8f076771b9..5420fbde9b9 100644 --- a/src/android.c +++ b/src/android.c @@ -5244,28 +5244,48 @@ android_build_string (Lisp_Object text) { Lisp_Object encoded; jstring string; + size_t nchars; + jchar *characters; + USE_SAFE_ALLOCA; - encoded = ENCODE_UTF_8 (text); + encoded = code_convert_string_norecord (text, Qutf_16le, + true); + nchars = (SBYTES (encoded) / sizeof (jchar)); - /* Note that Java expects this string to be in ``modified UTF - encoding'', which is actually UTF-8, except with NUL encoded as a - two-byte sequence. The only consequence of passing an actual - UTF-8 string is that NUL bytes cannot be represented, which is - not really of consequence. */ - string = (*android_java_env)->NewStringUTF (android_java_env, - SSDATA (encoded)); + /* Encode the string as UTF-16 prior to creating the string. + Copy the string to a separate buffer in order to preserve + alignment. */ + + characters = SAFE_ALLOCA (SBYTES (encoded)); + memcpy (characters, SDATA (encoded), SBYTES (encoded)); + + /* Create the string. */ + string + = (*android_java_env)->NewString (android_java_env, + characters, nchars); android_exception_check (); + SAFE_FREE (); return string; } -/* Do the same, except TEXT is constant string data. */ +/* Do the same, except TEXT is constant string data in ASCII or + UTF-8 containing no characters outside the Basic Multilingual + Plane. */ jstring android_build_jstring (const char *text) { jstring string; + /* Note that Java expects this string to be in ``modified UTF + encoding'', which is actually UTF-8, except with NUL + encoded as a two-byte sequence, and surrogate pairs encoded + in the three-byte extended encoding. The only consequence + of passing an actual UTF-8 string is that NUL bytes and + characters requiring surrogate pairs cannot be represented, + which is not really of consequence. */ + string = (*android_java_env)->NewStringUTF (android_java_env, text); android_exception_check (); diff --git a/src/androidmenu.c b/src/androidmenu.c index 2ba11aa1663..540b25cf602 100644 --- a/src/androidmenu.c +++ b/src/androidmenu.c @@ -98,7 +98,8 @@ #define FIND_METHOD_STATIC(c_name, name, signature) \ FIND_METHOD_STATIC (create_context_menu, "createContextMenu", "(Ljava/lang/String;)Lorg/gnu/emacs/EmacsContextMenu;"); - FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ)V"); + FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ" + "Ljava/lang/String;)V"); FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;" "Ljava/lang/String;Ljava/lang/String;)" "Lorg/gnu/emacs/EmacsContextMenu;"); @@ -411,6 +412,14 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, title_string = (!NILP (item_name) ? android_build_string (item_name) : NULL); + help_string = NULL; + + /* Menu items can have tool tips on Android 26 and + later. In this case, set it to the help string. */ + + if (android_get_current_api_level () >= 26 + && STRINGP (help)) + help_string = android_build_string (help); /* Determine whether or not to display a check box. */ @@ -424,11 +433,15 @@ android_menu_show (struct frame *f, int x, int y, int menuflags, title_string, (jboolean) !NILP (enable), (jboolean) checkmark, - (jboolean) !NILP (selected)); + (jboolean) !NILP (selected), + help_string); android_exception_check (); if (title_string) ANDROID_DELETE_LOCAL_REF (title_string); + + if (help_string) + ANDROID_DELETE_LOCAL_REF (help_string); } i += MENU_ITEMS_ITEM_LENGTH; diff --git a/src/sfnt.c b/src/sfnt.c index b4f587a4690..5b219bf6369 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -155,7 +155,7 @@ #define MAX(a, b) ((a) > (b) ? (a) : (b)) /* Swap values from TrueType to system byte order. */ static void -_sfnt_swap16 (uint16_t *value) +sfnt_swap16_1 (uint16_t *value) { #ifndef WORDS_BIGENDIAN *value = bswap_16 (*value); @@ -163,15 +163,15 @@ _sfnt_swap16 (uint16_t *value) } static void -_sfnt_swap32 (uint32_t *value) +sfnt_swap32_1 (uint32_t *value) { #ifndef WORDS_BIGENDIAN *value = bswap_32 (*value); #endif } -#define sfnt_swap16(what) (_sfnt_swap16 ((uint16_t *) (what))) -#define sfnt_swap32(what) (_sfnt_swap32 ((uint32_t *) (what))) +#define sfnt_swap16(what) (sfnt_swap16_1 ((uint16_t *) (what))) +#define sfnt_swap32(what) (sfnt_swap32_1 ((uint32_t *) (what))) /* Read the table directory from the file FD. FD must currently be at the start of the file (or an offset defined in the TTC header, if commit 009d4cfc8846a97baefc64cbd965de397206a33b Author: Po Lu Date: Thu Mar 9 09:36:36 2023 +0800 Fix crash upon restoring desktop * src/android.c (android_set_input_focus): Don't call method on window using service class. diff --git a/src/android.c b/src/android.c index e620a041348..e8f076771b9 100644 --- a/src/android.c +++ b/src/android.c @@ -4363,7 +4363,7 @@ android_set_input_focus (android_window handle, unsigned long time) (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window, - service_class.class, + window_class.class, make_input_focus, (jlong) time); android_exception_check (); commit ad678306c464e0fd13bc10c78634f6e573c4c8b4 Author: Po Lu Date: Thu Mar 9 09:33:22 2023 +0800 ; * src/sfnt.c (ODD): Use PUSH_UNCHECKED. diff --git a/src/sfnt.c b/src/sfnt.c index c5aeda11ff2..b4f587a4690 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -6057,7 +6057,8 @@ #define ODD() \ result \ = interpreter->state.round (result, \ interpreter); \ - PUSH (((result & 127) == 64) ? 1 : 0); \ + PUSH_UNCHECKED (((result & 127) \ + == 64) ? 1 : 0); \ } #define EVEN() \ commit 5e3bba2dbe32ffdfd5fadceade08a365be2bb11a Merge: 1bf8fd61c5c da4f1fa550f Author: Po Lu Date: Thu Mar 9 09:27:12 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 1bf8fd61c5c4781a4f00ea4a4465915c04dcb659 Author: Po Lu Date: Wed Mar 8 21:18:50 2023 +0800 Update Android port * src/fileio.c (Fcopy_file): On Android, ignore ENOSYS and ENOTSUP when restoring file times, as the system call used is supported by many kernels. diff --git a/src/fileio.c b/src/fileio.c index 88582704d7e..99f710ccbf0 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -2495,7 +2495,17 @@ DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, struct timespec ts[2]; ts[0] = get_stat_atime (&st); ts[1] = get_stat_mtime (&st); - if (futimens (ofd, ts) != 0) + if (futimens (ofd, ts) != 0 + /* Various versions of the Android C library are missing + futimens, which leads a gnulib fallback to be installed + that uses fdutimens instead. However, fdutimens is not + supported on many Android kernels, so just silently fail + if errno is ENOTSUP or ENOSYS. */ +#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY + && errno != ENOTSUP + && errno != ENOSYS +#endif + ) xsignal2 (Qfile_date_error, build_string ("Cannot set file date"), newname); } commit c15f9aee2e6882e6b24bdb87e82f70274c6592ea Merge: f9e68ed00e4 5ff018524c7 Author: Po Lu Date: Wed Mar 8 20:07:12 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit f9e68ed00e469d841c58681cd9d06e3d985614ad Author: Po Lu Date: Wed Mar 8 20:06:41 2023 +0800 Fix occasional crash * src/androidterm.c (android_build_extracted_text): Return NULL if text class not initialized. (android_update_selection): Check that EXTRACTED is not NULL. diff --git a/src/androidterm.c b/src/androidterm.c index f4a535292f2..b502b1c6de5 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -5080,6 +5080,10 @@ android_build_extracted_text (jstring text, ptrdiff_t start, env = android_java_env; + /* Return NULL if the class has not yet been obtained. */ + if (!text_class.class) + return NULL; + /* Create an ExtractedText object containing this information. */ object = (*env)->NewObject (env, text_class.class, text_class.constructor); @@ -5333,11 +5337,14 @@ android_update_selection (struct frame *f, struct window *w) android_exception_check_1 (string); ANDROID_DELETE_LOCAL_REF (string); - /* extracted is now an associated ExtractedText object. Perform - the update. */ - android_update_extracted_text (FRAME_ANDROID_WINDOW (f), - extracted, token); - ANDROID_DELETE_LOCAL_REF (extracted); + if (extracted) + { + /* extracted is now an associated ExtractedText object. + Perform the update. */ + android_update_extracted_text (FRAME_ANDROID_WINDOW (f), + extracted, token); + ANDROID_DELETE_LOCAL_REF (extracted); + } } } commit bb55528c7b58c5f50336ed3f2ff9759559d78680 Author: Po Lu Date: Wed Mar 8 15:04:49 2023 +0800 Update Android port * doc/emacs/android.texi (Android File System): Document what `temp~unlinked' means in the temporary files directory. * java/org/gnu/emacs/EmacsService.java (updateExtractedText): New function. * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): Ask the input method nicely to not display the extracted text UI. * src/android.c (struct android_emacs_service): New method `updateExtractedText'. (android_hack_asset_fd_fallback): Improve naming convention. Fix typo. (android_init_emacs_service): Add new method. (android_update_extracted_text): New function. (android_open_asset): Fix typo. * src/androidgui.h: Update prototypes. * src/androidterm.c (struct android_get_extracted_text_context): New field `flags'. (android_get_extracted_text): Set flags on the frame's output data. (android_build_extracted_text): New function. (getExtractedText): Move out class structures. (android_update_selection): Send updates to extracted text if the input method asked for them. (android_reset_conversion): Clear extracted text flags. * src/androidterm.h (struct android_output): New fields for storing extracted text data. diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi index d49e0754b0a..8e98b92314a 100644 --- a/doc/emacs/android.texi +++ b/doc/emacs/android.texi @@ -205,6 +205,13 @@ Android File System app data directory is typically symlinked to @file{/data/data/org.gnu.emacs}. +@cindex temp~unlinked.NNNN files, Android + On Android devices running very old (2.6.29) versions of the Linux +kernel, Emacs needs to create files named starting with +@file{temp~unlinked} in the the temporary file directory in order to +read from asset files. Do not create files with such names yourself, +or they may be overwritten or removed. + @cindex file system limitations, Android 11 On Android 11 and later, the Android system restricts applications from accessing files in the @file{/sdcard} directory using diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index f99d7a40067..848ad4de789 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -29,6 +29,7 @@ import android.view.InputDevice; import android.view.KeyEvent; +import android.view.inputmethod.ExtractedText; import android.app.Notification; import android.app.NotificationManager; @@ -811,4 +812,15 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) } }); } + + public void + updateExtractedText (EmacsWindow window, ExtractedText text, + int token) + { + if (DEBUG_IC) + Log.d (TAG, "updateExtractedText: @" + token + ", " + text); + + window.view.imManager.updateExtractedText (window.view, + token, text); + } }; diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 90a2c912a5a..6ace609f386 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -569,6 +569,7 @@ else if (child.getVisibility () != GONE) /* Make sure the input method never displays a full screen input box that obscures Emacs. */ info.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; + info.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI; /* Set a reasonable inputType. */ info.inputType = InputType.TYPE_CLASS_TEXT; diff --git a/src/android.c b/src/android.c index 11b0fa5e0f3..e620a041348 100644 --- a/src/android.c +++ b/src/android.c @@ -112,6 +112,7 @@ #define ANDROID_MAX_ASSET_FD 65535 jmethodID check_content_uri; jmethodID query_battery; jmethodID display_toast; + jmethodID update_extracted_text; }; struct android_emacs_pixmap @@ -1236,13 +1237,12 @@ android_hack_asset_fd_fallback (AAsset *asset) Creating an ashmem file descriptor and reading from it doesn't work on these old Android versions. */ - snprintf (filename, PATH_MAX, "%s/%s.%d", - android_cache_dir, "temp-unlinked", - getpid ()); + snprintf (filename, PATH_MAX, "%s/temp~unlinked.%d", + android_cache_dir, getpid ()); fd = open (filename, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR); - if (fd < 1) + if (fd < 0) return -1; if (unlink (filename)) @@ -2135,6 +2135,9 @@ #define FIND_METHOD(c_name, name, signature) \ FIND_METHOD (query_battery, "queryBattery", "()[J"); FIND_METHOD (display_toast, "displayToast", "(Ljava/lang/String;)V"); + FIND_METHOD (update_extracted_text, "updateExtractedText", + "(Lorg/gnu/emacs/EmacsWindow;" + "Landroid/view/inputmethod/ExtractedText;I)V"); #undef FIND_METHOD } @@ -5991,6 +5994,37 @@ android_reset_ic (android_window window, enum android_ic_mode mode) android_exception_check (); } +/* Make updates to extracted text known to the input method on + WINDOW. TEXT should be a local reference to the new + extracted text. TOKEN should be the token specified by the + input method. */ + +void +android_update_extracted_text (android_window window, void *text, + int token) +{ + jobject object; + jmethodID method; + + object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); + method = service_class.update_extracted_text; + + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + method, object, + /* N.B. that + text is not + jobject, + because that + type is not + available in + androidgui.h. */ + (jobject) text, + (jint) token); + android_exception_check_1 (text); +} + /* Window decoration management functions. */ @@ -6083,7 +6117,7 @@ android_open_asset (const char *filename, int oflag, mode_t mode) get a regular file descriptor. */ fd.fd = android_open (filename, oflag, mode); - if (fd.fd < 1) + if (fd.fd < 0) return fd; /* Set fd.asset to NULL, signifying that it is a file diff --git a/src/androidgui.h b/src/androidgui.h index e1c80a71a59..afcaed98cae 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -613,6 +613,8 @@ #define ANDROID_IS_MODIFIER_KEY(key) \ extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t); extern void android_reset_ic (android_window, enum android_ic_mode); +extern void android_update_extracted_text (android_window, void *, + int); extern int android_set_fullscreen (android_window, bool); #endif diff --git a/src/androidterm.c b/src/androidterm.c index 0cc2b35099c..f4a535292f2 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -4968,6 +4968,10 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, android_write_event (&event); } + + +/* Text extraction. */ + struct android_get_extracted_text_context { /* The parameters of the request. */ @@ -4976,6 +4980,9 @@ NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object, /* Token for the request. */ int token; + /* Flags associated with the request. */ + int flags; + /* The returned text, or NULL. */ char *text; @@ -5011,6 +5018,14 @@ android_get_extracted_text (void *data) = get_extracted_text (f, min (request->hint_max_chars, 600), &request->start, &request->offset, &request->length, &request->bytes); + + /* See if request->flags & GET_EXTRACTED_TEXT_MONITOR. If so, then + the input method has asked to monitor changes to the extracted + text until the next IM context reset. */ + + FRAME_ANDROID_OUTPUT (f)->extracted_text_flags = request->flags; + FRAME_ANDROID_OUTPUT (f)->extracted_text_token = request->token; + FRAME_ANDROID_OUTPUT (f)->extracted_text_hint = request->hint_max_chars; } /* Structure describing the `ExtractedTextRequest' class. @@ -5038,6 +5053,51 @@ android_get_extracted_text (void *data) jfieldID text; }; +/* Fields and methods associated with the `ExtractedTextRequest' + class. */ +struct android_extracted_text_request_class request_class; + +/* Fields and methods associated with the `ExtractedText' class. */ +struct android_extracted_text_class text_class; + +/* Return an ExtractedText object corresponding to the extracted text + TEXT. START is a character position describing the offset of the + first character in TEXT. OFFSET is the offset of point relative to + START. + + Assume that request_class and text_class have already been + initialized. + + Value is NULL if an error occurs; the exception is not cleared, + else a local reference to the ExtractedText object. */ + +static jobject +android_build_extracted_text (jstring text, ptrdiff_t start, + ptrdiff_t offset) +{ + JNIEnv *env; + jobject object; + + env = android_java_env; + + /* Create an ExtractedText object containing this information. */ + object = (*env)->NewObject (env, text_class.class, + text_class.constructor); + if (!object) + return NULL; + + (*env)->SetIntField (env, object, text_class.partial_start_offset, -1); + (*env)->SetIntField (env, object, text_class.partial_end_offset, -1); + (*env)->SetIntField (env, object, text_class.selection_start, + min (offset, TYPE_MAXIMUM (jint))); + (*env)->SetIntField (env, object, text_class.selection_end, + min (offset, TYPE_MAXIMUM (jint))); + (*env)->SetIntField (env, object, text_class.start_offset, + min (start, TYPE_MAXIMUM (jint))); + (*env)->SetObjectField (env, object, text_class.text, text); + return object; +} + JNIEXPORT jobject JNICALL NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, jshort window, jobject request, @@ -5046,14 +5106,10 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, JNI_STACK_ALIGNMENT_PROLOGUE; struct android_get_extracted_text_context context; - static struct android_extracted_text_request_class request_class; - static struct android_extracted_text_class text_class; jstring string; jclass class; jobject object; - /* TODO: report changes to extracted text. */ - /* Initialize both classes if necessary. */ if (!request_class.initialized) @@ -5106,6 +5162,7 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, = (*env)->GetIntField (env, request, request_class.hint_max_chars); context.token = (*env)->GetIntField (env, request, request_class.token); + context.flags = flags; context.text = NULL; context.window = window; @@ -5126,8 +5183,8 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, return NULL; /* Create an ExtractedText object containing this information. */ - object = (*android_java_env)->NewObject (env, text_class.class, - text_class.constructor); + object = (*env)->NewObject (env, text_class.class, + text_class.constructor); if (!object) return NULL; @@ -5143,6 +5200,8 @@ NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object, return object; } + + JNIEXPORT jstring JNICALL NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object, jshort window) @@ -5210,8 +5269,12 @@ NATIVE_NAME (requestSelectionUpdate) (JNIEnv *env, jobject object, static void android_update_selection (struct frame *f, struct window *w) { - ptrdiff_t start, end, point, mark; + ptrdiff_t start, end, point, mark, offset, length, bytes; struct buffer *b; + int hint, token; + char *text; + jobject extracted; + jstring string; if (MARKERP (f->conversion.compose_region_start)) { @@ -5246,6 +5309,36 @@ android_update_selection (struct frame *f, struct window *w) the selection is less than or equal to the end. */ android_update_ic (FRAME_ANDROID_WINDOW (f), min (point, mark), max (point, mark), start, end); + + /* Update the extracted text as well, if the input method has asked + for updates. 1 is + InputConnection.GET_EXTRACTED_TEXT_MONITOR. */ + + if (FRAME_ANDROID_OUTPUT (f)->extracted_text_flags & 1) + { + hint = FRAME_ANDROID_OUTPUT (f)->extracted_text_hint; + token = FRAME_ANDROID_OUTPUT (f)->extracted_text_token; + text = get_extracted_text (f, min (hint, 600), &start, + &offset, &length, &bytes); + + /* Make a string out of the extracted text. */ + string = android_text_to_string (android_java_env, + text, length, bytes); + xfree (text); + android_exception_check (); + + /* Make extracted text out of that string. */ + extracted = android_build_extracted_text (string, start, + offset); + android_exception_check_1 (string); + ANDROID_DELETE_LOCAL_REF (string); + + /* extracted is now an associated ExtractedText object. Perform + the update. */ + android_update_extracted_text (FRAME_ANDROID_WINDOW (f), + extracted, token); + ANDROID_DELETE_LOCAL_REF (extracted); + } } /* Notice that the input method connection to F should be reset as a @@ -5283,6 +5376,10 @@ android_reset_conversion (struct frame *f) android_reset_ic (FRAME_ANDROID_WINDOW (f), mode); + /* Clear extracted text flags. Since the IM has been reinitialised, + it should no longer be displaying extracted text. */ + FRAME_ANDROID_OUTPUT (f)->extracted_text_flags = 0; + /* Move its selection to the specified position. */ android_update_selection (f, NULL); } diff --git a/src/androidterm.h b/src/androidterm.h index ac845187a66..9bd11bb7853 100644 --- a/src/androidterm.h +++ b/src/androidterm.h @@ -241,6 +241,16 @@ #define _ANDROID_TERM_H_ /* List of all tools (either styluses or fingers) pressed onto the frame. */ struct android_touch_point *touch_points; + + /* Flags associated with the last request to obtain ``extracted + text''. */ + int extracted_text_flags; + + /* Token asssociated with that request. */ + int extracted_text_token; + + /* The number of characters of extracted text wanted by the IM. */ + int extracted_text_hint; }; enum commit fdff5442a59fd2387c23e2be2658dafa39466891 Author: Po Lu Date: Wed Mar 8 10:19:26 2023 +0800 Fix double free upon encountering invalid font * src/sfnt.c (sfnt_read_cmap_table): Don't allocate too big data. Also, free elements of (*data), not offsets into data itself. diff --git a/src/sfnt.c b/src/sfnt.c index f5b84afa0a5..c5aeda11ff2 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -910,7 +910,7 @@ sfnt_read_cmap_table (int fd, struct sfnt_offset_subtable *subtable, /* Second, read each encoding subtable itself. */ *data = xmalloc (cmap->num_subtables - * sizeof **subtables); + * sizeof *data); for (i = 0; i < cmap->num_subtables; ++i) { @@ -923,7 +923,7 @@ sfnt_read_cmap_table (int fd, struct sfnt_offset_subtable *subtable, being unsupported.) Return now. */ for (j = 0; j < i; ++j) - xfree (data[j]); + xfree ((*data)[j]); xfree (*data); xfree (*subtables); commit 06cfa27e372be135646ed736ff48d9ad199c955c Merge: abe279cb173 4e8b50ec57b Author: Po Lu Date: Wed Mar 8 08:29:31 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit abe279cb173c2dc3c1fcf3378615a28b9b4abd0a Merge: 84d27fe53b2 fa83b236111 Author: Po Lu Date: Tue Mar 7 20:18:15 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 84d27fe53b2888b6668ba8510f377eb0eabeeb09 Author: Po Lu Date: Tue Mar 7 20:18:02 2023 +0800 Save build timestamps in Android builds * java/Makefile.in (install_temp/assets/build_info): New rule.:(emacs.apk-in): Depend on that file. * lisp/version.el (android-read-build-system) (android-read-build-time): New functions. (emacs-build-system, emacs-build-time): Use those functions on Android, as dumping is done after installation on Android. * src/fileio.c (Finsert_file_contents): * src/window.c (replace_buffer_in_windows): Don't call functions if they are not defined, which can happen during loadup. diff --git a/java/Makefile.in b/java/Makefile.in index c7fe6e07c77..1a7852487ef 100644 --- a/java/Makefile.in +++ b/java/Makefile.in @@ -222,8 +222,12 @@ install_temp/assets/version: && (git rev-parse --abbrev-ref HEAD \ || echo "Unknown") } 2> /dev/null > $@ +install_temp/assets/build_info: install_temp + $(AM_V_GEN) { hostname; date +%s; } > $@ + emacs.apk-in: install_temp install_temp/assets/directory-tree \ - install_temp/assets/version AndroidManifest.xml + install_temp/assets/version install_temp/assets/build_info \ + AndroidManifest.xml # Package everything. Specifying the assets on this command line is # necessary for AAssetManager_getNextFileName to work on old versions # of Android. Make sure not to generate R.java, as it's already been diff --git a/lisp/version.el b/lisp/version.el index 38a9f9c2be5..ca61f8cfeee 100644 --- a/lisp/version.el +++ b/lisp/version.el @@ -26,6 +26,31 @@ ;;; Code: + + +(defun android-read-build-system () + "Obtain the host name of the system on which Emacs was built. +Use the data stored in the special file `/assets/build_info'. +Value is the string ``Unknown'' upon failure, else the hostname +of the build system." + (with-temp-buffer + (insert-file-contents "/assets/build_info") + (let ((string (buffer-substring 1 (line-end-position)))) + (and (not (equal string "Unknown")) string)))) + +(defun android-read-build-time () + "Obtain the time at which Emacs was built. +Use the data stored in the special file `/assets/build_info'. +Value is nil upon failure, else the time in the same format as +returned by `current-time'." + (with-temp-buffer + (insert-file-contents "/assets/build_info") + (end-of-line) + (let ((number (read (current-buffer)))) + (time-convert number 'list)))) + + + (defconst emacs-major-version (progn (string-match "^[0-9]+" emacs-version) (string-to-number (match-string 0 emacs-version))) @@ -36,10 +61,15 @@ emacs-minor-version (string-to-number (match-string 1 emacs-version))) "Minor version number of this version of Emacs.") -(defconst emacs-build-system (system-name) +(defconst emacs-build-system (or (and (eq system-type 'android) + (android-read-build-system)) + (system-name)) "Name of the system on which Emacs was built, or nil if not available.") -(defconst emacs-build-time (if emacs-build-system (current-time)) +(defconst emacs-build-time (if emacs-build-system + (or (and (eq system-type 'android) + (android-read-build-time)) + (current-time))) "Time at which Emacs was dumped out, or nil if not available.") (defconst emacs-build-number 1 ; loadup.el may increment this diff --git a/src/fileio.c b/src/fileio.c index ae244b8f85a..88582704d7e 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4991,8 +4991,10 @@ because (1) it preserves some marker positions (in unchanged portions } } - /* Decode file format. */ - if (inserted > 0) + /* Decode file format. Don't do this if Qformat_decode is not + bound, which can happen when called early during loadup. */ + + if (inserted > 0 && !NILP (Fboundp (Qformat_decode))) { /* Don't run point motion or modification hooks when decoding. */ specpdl_ref count1 = SPECPDL_INDEX (); diff --git a/src/window.c b/src/window.c index f4e09f49eae..9a29ecb8807 100644 --- a/src/window.c +++ b/src/window.c @@ -3514,7 +3514,10 @@ DEFUN ("delete-other-windows-internal", Fdelete_other_windows_internal, void replace_buffer_in_windows (Lisp_Object buffer) { - call1 (Qreplace_buffer_in_windows, buffer); + /* When kill-buffer is called early during loadup, this function is + undefined. */ + if (!NILP (Fboundp (Qreplace_buffer_in_windows))) + call1 (Qreplace_buffer_in_windows, buffer); } /* If BUFFER is shown in a window, safely replace it with some other commit 83c29bd40e0c7e3975f575067a3112a0191b5590 Author: Po Lu Date: Tue Mar 7 16:49:45 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsWindow.java (onSomeKindOfMotionEvent): Dismiss splurious LeaveNotify events from button presses. * src/android.c (android_change_window_attributes) (android_change_gc, android_set_clip_rectangles) (android_reparent_window, android_clear_window, android_map_window) (android_unmap_window, android_resize_window, android_move_window) (android_swap_buffers, android_fill_rectangle, android_copy_area) (android_fill_polygon, android_draw_rectangle, android_draw_point) (android_draw_line, android_clear_area, android_bell) (android_set_input_focus, android_raise_window) (android_lower_window, android_set_dont_focus_on_map) (android_set_dont_accept_focus, android_get_keysym_name) (android_toggle_on_screen_keyboard, android_restart_emacs) (android_display_toast, android_update_ic, android_reset_ic) (android_set_fullscreen): Optimize by specifying the class explicitly when calling a method. diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 9d907d2b481..3569d93136b 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -906,9 +906,16 @@ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER) return true; case MotionEvent.ACTION_HOVER_EXIT: - EmacsNative.sendLeaveNotify (this.handle, (int) event.getX (), - (int) event.getY (), - event.getEventTime ()); + + /* If the exit event comes from a button press, its button + state will have extra bits compared to the last known + button state. Since the exit event will interfere with + tool bar button presses, ignore such splurious events. */ + + if ((event.getButtonState () & ~lastButtonState) == 0) + EmacsNative.sendLeaveNotify (this.handle, (int) event.getX (), + (int) event.getY (), + event.getEventTime ()); return true; case MotionEvent.ACTION_BUTTON_PRESS: diff --git a/src/android.c b/src/android.c index f49249de09f..11b0fa5e0f3 100644 --- a/src/android.c +++ b/src/android.c @@ -2966,15 +2966,19 @@ android_change_window_attributes (android_window handle, { jmethodID method; jobject window; + jint pixel; window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); if (value_mask & ANDROID_CW_BACK_PIXEL) { method = window_class.change_window_background; - (*android_java_env)->CallVoidMethod (android_java_env, - window, method, - (jint) attrs->background_pixel); + pixel = (jint) attrs->background_pixel; + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + window, + window_class.class, + method, pixel); + android_exception_check (); } } @@ -3352,10 +3356,14 @@ android_change_gc (struct android_gc *gc, values->ts_y_origin); if (mask) - (*android_java_env)->CallVoidMethod (android_java_env, - gcontext, - emacs_gc_mark_dirty, - (jboolean) clip_changed); + { + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + gcontext, + emacs_gc_class, + emacs_gc_mark_dirty, + (jboolean) clip_changed); + android_exception_check (); + } } void @@ -3417,10 +3425,12 @@ android_set_clip_rectangles (struct android_gc *gc, int clip_x_origin, emacs_gc_clip_y_origin, clip_y_origin); - (*android_java_env)->CallVoidMethod (android_java_env, - gcontext, - emacs_gc_mark_dirty, - (jboolean) true); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + gcontext, + emacs_gc_class, + emacs_gc_mark_dirty, + (jboolean) true); + android_exception_check (); /* Cache the clip rectangles on the C side for sfntfont-android.c. */ @@ -3448,9 +3458,10 @@ android_reparent_window (android_window w, android_window parent_handle, ANDROID_HANDLE_WINDOW); method = window_class.reparent_to; - (*android_java_env)->CallVoidMethod (android_java_env, window, - method, - parent, (jint) x, (jint) y); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window, + window_class.class, method, + parent, (jint) x, (jint) y); + android_exception_check (); } void @@ -3460,10 +3471,12 @@ android_clear_window (android_window handle) window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.clear_window, - window); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.clear_window, + window); + android_exception_check (); } void @@ -3475,8 +3488,11 @@ android_map_window (android_window handle) window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); map_window = window_class.map_window; - (*android_java_env)->CallVoidMethod (android_java_env, - window, map_window); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + window, + window_class.class, + map_window); + android_exception_check (); } void @@ -3488,8 +3504,11 @@ android_unmap_window (android_window handle) window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); unmap_window = window_class.unmap_window; - (*android_java_env)->CallVoidMethod (android_java_env, - window, unmap_window); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + window, + window_class.class, + unmap_window); + android_exception_check (); } void @@ -3502,9 +3521,13 @@ android_resize_window (android_window handle, unsigned int width, window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); resize_window = window_class.resize_window; - (*android_java_env)->CallVoidMethod (android_java_env, - window, resize_window, - (jint) width, (jint) height); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + window, + window_class.class, + resize_window, + (jint) width, + (jint) height); + android_exception_check (); } void @@ -3516,9 +3539,12 @@ android_move_window (android_window handle, int x, int y) window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); move_window = window_class.move_window; - (*android_java_env)->CallVoidMethod (android_java_env, - window, move_window, - (jint) x, (jint) y); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + window, + window_class.class, + move_window, + (jint) x, (jint) y); + android_exception_check (); } void @@ -3532,9 +3558,11 @@ android_swap_buffers (struct android_swap_info *swap_info, { window = android_resolve_handle (swap_info[i].swap_window, ANDROID_HANDLE_WINDOW); - (*android_java_env)->CallVoidMethod (android_java_env, - window, - window_class.swap_buffers); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + window, + window_class.class, + window_class.swap_buffers); + android_exception_check (); } } @@ -3618,14 +3646,15 @@ android_fill_rectangle (android_drawable handle, struct android_gc *gc, gcontext = android_resolve_handle (gc->gcontext, ANDROID_HANDLE_GCONTEXT); - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.fill_rectangle, - drawable, - gcontext, - (jint) x, (jint) y, - (jint) width, - (jint) height); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.fill_rectangle, + drawable, + gcontext, + (jint) x, (jint) y, + (jint) width, + (jint) height); } android_pixmap @@ -3752,15 +3781,16 @@ android_copy_area (android_drawable src, android_drawable dest, gcontext = android_resolve_handle (gc->gcontext, ANDROID_HANDLE_GCONTEXT); - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.copy_area, - src_object, - dest_object, - gcontext, - (jint) src_x, (jint) src_y, - (jint) width, (jint) height, - (jint) dest_x, (jint) dest_y); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.copy_area, + src_object, + dest_object, + gcontext, + (jint) src_x, (jint) src_y, + (jint) width, (jint) height, + (jint) dest_x, (jint) dest_y); } void @@ -3813,11 +3843,12 @@ android_fill_polygon (android_drawable drawable, struct android_gc *gc, ANDROID_DELETE_LOCAL_REF (point); } - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.fill_polygon, - drawable_object, - gcontext, array); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.fill_polygon, + drawable_object, + gcontext, array); ANDROID_DELETE_LOCAL_REF (array); } @@ -3833,12 +3864,13 @@ android_draw_rectangle (android_drawable handle, struct android_gc *gc, gcontext = android_resolve_handle (gc->gcontext, ANDROID_HANDLE_GCONTEXT); - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.draw_rectangle, - drawable, gcontext, - (jint) x, (jint) y, - (jint) width, (jint) height); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.draw_rectangle, + drawable, gcontext, + (jint) x, (jint) y, + (jint) width, (jint) height); } void @@ -3853,11 +3885,12 @@ android_draw_point (android_drawable handle, struct android_gc *gc, gcontext = android_resolve_handle (gc->gcontext, ANDROID_HANDLE_GCONTEXT); - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.draw_point, - drawable, gcontext, - (jint) x, (jint) y); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.draw_point, + drawable, gcontext, + (jint) x, (jint) y); } void @@ -3872,12 +3905,13 @@ android_draw_line (android_drawable handle, struct android_gc *gc, gcontext = android_resolve_handle (gc->gcontext, ANDROID_HANDLE_GCONTEXT); - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.draw_line, - drawable, gcontext, - (jint) x, (jint) y, - (jint) x2, (jint) y2); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.draw_line, + drawable, gcontext, + (jint) x, (jint) y, + (jint) x2, (jint) y2); } android_pixmap @@ -3941,11 +3975,12 @@ android_clear_area (android_window handle, int x, int y, window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.clear_area, - window, (jint) x, (jint) y, - (jint) width, (jint) height); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.clear_area, + window, (jint) x, (jint) y, + (jint) width, (jint) height); } android_pixmap @@ -4307,9 +4342,10 @@ android_put_image (android_pixmap handle, struct android_image *image) void android_bell (void) { - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.ring_bell); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.ring_bell); android_exception_check (); } @@ -4322,8 +4358,11 @@ android_set_input_focus (android_window handle, unsigned long time) window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); make_input_focus = window_class.make_input_focus; - (*android_java_env)->CallVoidMethod (android_java_env, window, - make_input_focus, (jlong) time); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + window, + service_class.class, + make_input_focus, + (jlong) time); android_exception_check (); } @@ -4336,8 +4375,10 @@ android_raise_window (android_window handle) window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); raise = window_class.raise; - (*android_java_env)->CallVoidMethod (android_java_env, window, - raise); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + window, + window_class.class, + raise); android_exception_check (); } @@ -4350,8 +4391,10 @@ android_lower_window (android_window handle) window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); lower = window_class.lower; - (*android_java_env)->CallVoidMethod (android_java_env, window, - lower); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + window, + window_class.class, + lower); android_exception_check (); } @@ -4767,9 +4810,11 @@ android_set_dont_focus_on_map (android_window handle, window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); method = window_class.set_dont_focus_on_map; - (*android_java_env)->CallVoidMethod (android_java_env, window, - method, - (jboolean) no_focus_on_map); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window, + window_class.class, + method, + (jboolean) no_focus_on_map); + android_exception_check (); } void @@ -4782,9 +4827,11 @@ android_set_dont_accept_focus (android_window handle, window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW); method = window_class.set_dont_accept_focus; - (*android_java_env)->CallVoidMethod (android_java_env, window, - method, - (jboolean) no_accept_focus); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window, + window_class.class, + method, + (jboolean) no_accept_focus); + android_exception_check (); } void @@ -4802,7 +4849,7 @@ android_get_keysym_name (int keysym, char *name_return, size_t size) buffer = (*android_java_env)->GetStringUTFChars (android_java_env, (jstring) string, NULL); - android_exception_check (); + android_exception_check_nonnull ((void *) buffer, string); strncpy (name_return, buffer, size - 1); (*android_java_env)->ReleaseStringUTFChars (android_java_env, @@ -4827,8 +4874,9 @@ android_toggle_on_screen_keyboard (android_window window, bool show) method = window_class.toggle_on_screen_keyboard; /* Now display the on screen keyboard. */ - (*android_java_env)->CallVoidMethod (android_java_env, object, - method, (jboolean) show); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, object, + window_class.class, + method, (jboolean) show); /* Check for out of memory errors. */ android_exception_check (); @@ -5645,9 +5693,10 @@ android_restart_emacs (void) { /* Try to call the Java side function. Normally, this should call System.exit to terminate this process. */ - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.restart_emacs); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.restart_emacs); /* Exit anyway, in case EmacsService did not do so. */ exit (0); @@ -5720,10 +5769,11 @@ android_display_toast (const char *text) android_exception_check (); /* Display the toast. */ - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.display_toast, - string); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.display_toast, + string); android_exception_check_1 (string); /* Delete the local reference to the string. */ @@ -5896,14 +5946,15 @@ android_update_ic (android_window window, ptrdiff_t selection_start, object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.update_ic, - object, - (jint) selection_start, - (jint) selection_end, - (jint) composing_region_start, - (jint) composing_region_end); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.update_ic, + object, + (jint) selection_start, + (jint) selection_end, + (jint) composing_region_start, + (jint) composing_region_end); android_exception_check (); } @@ -5932,10 +5983,11 @@ android_reset_ic (android_window window, enum android_ic_mode mode) object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.reset_ic, - object, (jint) mode); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + emacs_service, + service_class.class, + service_class.reset_ic, + object, (jint) mode); android_exception_check (); } @@ -5961,10 +6013,11 @@ android_set_fullscreen (android_window window, bool fullscreen) object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW); - (*android_java_env)->CallVoidMethod (android_java_env, - object, - window_class.set_fullscreen, - (jboolean) fullscreen); + (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, + object, + window_class.class, + window_class.set_fullscreen, + (jboolean) fullscreen); android_exception_check (); return 0; } commit 327c2658ac1d02202118e4e0e4649496b99285f3 Merge: 44cf1ed7e59 8179555730d Author: Po Lu Date: Tue Mar 7 15:25:28 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 44cf1ed7e593022df01a47b701e7796e9d70d2fb Author: Po Lu Date: Tue Mar 7 14:20:50 2023 +0800 Update Android port * src/lread.c (lread_fd, file_tell, infile, skip_dyn_bytes) (skip_dyn_eof, readbyte_from_stdio, safe_to_load_version) (close_infile_unwind, close_file_unwind_android_fd): New function. (Fload, Flocate_file_internal, openp): New argument PLATFORM. All callers changed. (skip_lazy_string): Add optimized versions of various functions for accessing Android assets. diff --git a/src/callproc.c b/src/callproc.c index ea9c946f158..8d3519fdab2 100644 --- a/src/callproc.c +++ b/src/callproc.c @@ -516,7 +516,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd, int ok; ok = openp (Vexec_path, args[0], Vexec_suffixes, &path, - make_fixnum (X_OK), false, false); + make_fixnum (X_OK), false, false, NULL); if (ok < 0) report_file_error ("Searching for program", args[0]); } diff --git a/src/charset.c b/src/charset.c index 7987ffa0c5e..8e909c5f03c 100644 --- a/src/charset.c +++ b/src/charset.c @@ -486,7 +486,8 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile, specpdl_ref count = SPECPDL_INDEX (); record_unwind_protect_nothing (); specbind (Qfile_name_handler_alist, Qnil); - fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false); + fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false, + NULL); fp = fd < 0 ? 0 : fdopen (fd, "r"); if (!fp) { diff --git a/src/emacs.c b/src/emacs.c index 2f953510a3d..3df98e6fae2 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -531,7 +531,8 @@ init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd) { Lisp_Object found; int yes = openp (Vexec_path, Vinvocation_name, Vexec_suffixes, - &found, make_fixnum (X_OK), false, false); + &found, make_fixnum (X_OK), false, false, + NULL); if (yes == 1) { /* Add /: to the front of the name diff --git a/src/image.c b/src/image.c index 2e6aa0ce0e3..a244b23ecd2 100644 --- a/src/image.c +++ b/src/image.c @@ -760,7 +760,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file) /* Search bitmap-file-path for the file, if appropriate. */ if (openp (Vx_bitmap_file_path, file, Qnil, &found, - make_fixnum (R_OK), false, false) + make_fixnum (R_OK), false, false, NULL) < 0) return -1; @@ -807,7 +807,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file) /* Search bitmap-file-path for the file, if appropriate. */ if (openp (Vx_bitmap_file_path, file, Qnil, &found, - make_fixnum (R_OK), false, false) + make_fixnum (R_OK), false, false, NULL) < 0) return -1; @@ -896,7 +896,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file) /* Search bitmap-file-path for the file, if appropriate. */ if (openp (Vx_bitmap_file_path, file, Qnil, &found, - make_fixnum (R_OK), false, false) + make_fixnum (R_OK), false, false, NULL) < 0) return -1; @@ -4148,7 +4148,8 @@ image_find_image_fd (Lisp_Object file, int *pfd) /* Try to find FILE in data-directory/images, then x-bitmap-file-path. */ fd = openp (search_path, file, Qnil, &file_found, - pfd ? Qt : make_fixnum (R_OK), false, false); + pfd ? Qt : make_fixnum (R_OK), false, false, + NULL); if (fd == -2) { /* The file exists locally, but has a file name handler. diff --git a/src/lisp.h b/src/lisp.h index 56ef338a5b1..f7ba6775975 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -4514,7 +4514,8 @@ LOADHIST_ATTACH (Lisp_Object x) extern Lisp_Object save_match_data_load (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object); extern int openp (Lisp_Object, Lisp_Object, Lisp_Object, - Lisp_Object *, Lisp_Object, bool, bool); + Lisp_Object *, Lisp_Object, bool, bool, + void **); enum { S2N_IGNORE_TRAILING = 1 }; extern Lisp_Object string_to_number (char const *, int, ptrdiff_t *); extern void map_obarray (Lisp_Object, void (*) (Lisp_Object, Lisp_Object), diff --git a/src/lread.c b/src/lread.c index 150d8a01e10..48f95ce5f40 100644 --- a/src/lread.c +++ b/src/lread.c @@ -62,6 +62,24 @@ #define DEFINE_SYMBOLS #include +#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY \ + || (__ANDROID_API__ < 9) + +#define lread_fd int +#define lread_fd_cmp(n) (fd == (n)) +#define lread_fd_p (fd >= 0) +#define lread_close emacs_close +#define lread_fstat fstat +#define lread_read_quit emacs_read_quit +#define lread_lseek lseek + +#define file_stream FILE * +#define file_seek fseek +#define file_stream_valid_p(p) (p) +#define file_stream_close emacs_fclose +#define file_stream_invalid NULL +#define file_get_char getc + #ifdef HAVE_FSEEKO #define file_offset off_t #define file_tell ftello @@ -70,6 +88,79 @@ #define file_offset long #define file_tell ftell #endif +#else + +#include "android.h" + +/* Use an Android file descriptor under Android instead, as this + allows loading directly from asset files without loading each asset + into memory and creating a separate file descriptor every time. + + Note that `struct android_fd_or_asset' as used here is different + from that returned from `android_open_asset'; if fd.asset is NULL, + then fd.fd is either a valid file descriptor or -1, meaning that + the file descriptor is invalid. + + However, lread requires the ability to seek inside asset files, + which is not provided under Android 2.2. So when building for that + particular system, fall back to the usual file descriptor-based + code. */ + +#define lread_fd struct android_fd_or_asset +#define lread_fd_cmp(n) (!fd.asset && fd.fd == (n)) +#define lread_fd_p (fd.asset || fd.fd >= 0) +#define lread_close android_close_asset +#define lread_fstat android_asset_fstat +#define lread_read_quit android_asset_read_quit +#define lread_lseek android_asset_lseek + +/* The invalid file stream. */ + +static struct android_fd_or_asset invalid_file_stream = + { + -1, + NULL, + }; + +#define file_stream struct android_fd_or_asset +#define file_offset off_t +#define file_tell(n) (android_asset_lseek ((n), 0, SEEK_CUR)) +#define file_seek android_asset_lseek +#define file_stream_valid_p(p) ((p).asset || (p).fd >= 0) +#define file_stream_close android_close_asset +#define file_stream_invalid invalid_file_stream + +/* Return a single character from the file input stream STREAM. + Value and errors are the same as getc. */ + +static int +file_get_char (file_stream stream) +{ + int c; + char byte; + ssize_t rc; + + retry: + rc = android_asset_read (stream, &byte, 1); + + if (rc == 0) + c = EOF; + else if (rc == -1) + { + if (errno == EINTR) + goto retry; + else + c = EOF; + } + else + c = (unsigned char) byte; + + return c; +} + +#define USE_ANDROID_ASSETS +#endif + #if IEEE_FLOATING_POINT # include # ifndef INFINITY @@ -113,7 +204,7 @@ #define file_tell ftell static struct infile { /* The input stream. */ - FILE *stream; + file_stream stream; /* Lookahead byte count. */ signed char lookahead; @@ -375,7 +466,7 @@ skip_dyn_bytes (Lisp_Object readcharfun, ptrdiff_t n) if (FROM_FILE_P (readcharfun)) { block_input (); /* FIXME: Not sure if it's needed. */ - fseek (infile->stream, n - infile->lookahead, SEEK_CUR); + file_seek (infile->stream, n - infile->lookahead, SEEK_CUR); unblock_input (); infile->lookahead = 0; } @@ -399,7 +490,7 @@ skip_dyn_eof (Lisp_Object readcharfun) if (FROM_FILE_P (readcharfun)) { block_input (); /* FIXME: Not sure if it's needed. */ - fseek (infile->stream, 0, SEEK_END); + file_seek (infile->stream, 0, SEEK_END); unblock_input (); infile->lookahead = 0; } @@ -480,10 +571,12 @@ readbyte_from_stdio (void) return infile->buf[--infile->lookahead]; int c; - FILE *instream = infile->stream; + file_stream instream = infile->stream; block_input (); +#if !defined USE_ANDROID_ASSETS + /* Interrupted reads have been observed while reading over the network. */ while ((c = getc (instream)) == EOF && errno == EINTR && ferror (instream)) { @@ -493,6 +586,35 @@ readbyte_from_stdio (void) clearerr (instream); } +#else + + { + char byte; + ssize_t rc; + + retry: + rc = android_asset_read (instream, &byte, 1); + + if (rc == 0) + c = EOF; + else if (rc == -1) + { + if (errno == EINTR) + { + unblock_input (); + maybe_quit (); + block_input (); + goto retry; + } + else + c = EOF; + } + else + c = (unsigned char) byte; + } + +#endif + unblock_input (); return (c == EOF ? -1 : c); @@ -1062,7 +1184,7 @@ #define UPDATE_BEG_END_STATE(ch) \ safe to load. Only files compiled with Emacs can be loaded. */ static int -safe_to_load_version (Lisp_Object file, int fd) +safe_to_load_version (Lisp_Object file, lread_fd fd) { struct stat st; char buf[512]; @@ -1071,12 +1193,12 @@ safe_to_load_version (Lisp_Object file, int fd) /* If the file is not regular, then we cannot safely seek it. Assume that it is not safe to load as a compiled file. */ - if (sys_fstat (fd, &st) == 0 && !S_ISREG (st.st_mode)) + if (lread_fstat (fd, &st) == 0 && !S_ISREG (st.st_mode)) return 0; /* Read the first few bytes from the file, and look for a line specifying the byte compiler version used. */ - nbytes = emacs_read_quit (fd, buf, sizeof buf); + nbytes = lread_read_quit (fd, buf, sizeof buf); if (nbytes > 0) { /* Skip to the next newline, skipping over the initial `ELC' @@ -1091,7 +1213,7 @@ safe_to_load_version (Lisp_Object file, int fd) version = 0; } - if (lseek (fd, 0, SEEK_SET) < 0) + if (lread_lseek (fd, 0, SEEK_SET) < 0) report_file_error ("Seeking to start of file", file); return version; @@ -1165,7 +1287,7 @@ close_infile_unwind (void *arg) { struct infile *prev_infile = arg; eassert (infile && infile != prev_infile); - emacs_fclose (infile->stream); + file_stream_close (infile->stream); infile = prev_infile; } @@ -1194,6 +1316,22 @@ loadhist_initialize (Lisp_Object filename) specbind (Qcurrent_load_list, Fcons (filename, Qnil)); } +#ifdef USE_ANDROID_ASSETS + +/* Like `close_file_unwind'. However, PTR is a pointer to an Android + file descriptor instead of a system file descriptor. */ + +static void +close_file_unwind_android_fd (void *ptr) +{ + struct android_fd_or_asset *fd; + + fd = ptr; + android_close_asset (*fd); +} + +#endif + DEFUN ("load", Fload, Sload, 1, 5, 0, doc: /* Execute a file of Lisp code named FILE. First try FILE with `.elc' appended, then try with `.el', then try @@ -1242,8 +1380,12 @@ DEFUN ("load", Fload, Sload, 1, 5, 0, (Lisp_Object file, Lisp_Object noerror, Lisp_Object nomessage, Lisp_Object nosuffix, Lisp_Object must_suffix) { - FILE *stream UNINIT; - int fd; + file_stream stream UNINIT; + lread_fd fd; +#ifdef USE_ANDROID_ASSETS + int rc; + void *asset; +#endif specpdl_ref fd_index UNINIT; specpdl_ref count = SPECPDL_INDEX (); Lisp_Object found, efound, hist_file_name; @@ -1284,7 +1426,12 @@ DEFUN ("load", Fload, Sload, 1, 5, 0, since it would try to load a directory as a Lisp file. */ if (SCHARS (file) == 0) { +#if !defined USE_ANDROID_ASSETS fd = -1; +#else + fd.asset = NULL; + fd.fd = -1; +#endif errno = ENOENT; } else @@ -1323,12 +1470,22 @@ DEFUN ("load", Fload, Sload, 1, 5, 0, suffixes = CALLN (Fappend, suffixes, Vload_file_rep_suffixes); } - fd = - openp (Vload_path, file, suffixes, &found, Qnil, load_prefer_newer, - no_native); +#if !defined USE_ANDROID_ASSETS + fd = openp (Vload_path, file, suffixes, &found, Qnil, + load_prefer_newer, no_native, NULL); +#else + asset = NULL; + rc = openp (Vload_path, file, suffixes, &found, Qnil, + load_prefer_newer, no_native, &asset); + fd.fd = rc; + fd.asset = asset; + + /* fd.asset will be non-NULL if this is actually an asset + file. */ +#endif } - if (fd == -1) + if (lread_fd_cmp (-1)) { if (NILP (noerror)) report_file_error ("Cannot open load file", file); @@ -1340,7 +1497,7 @@ DEFUN ("load", Fload, Sload, 1, 5, 0, Vuser_init_file = found; /* If FD is -2, that means openp found a magic file. */ - if (fd == -2) + if (lread_fd_cmp (-2)) { if (NILP (Fequal (found, file))) /* If FOUND is a different file name from FILE, @@ -1369,11 +1526,21 @@ DEFUN ("load", Fload, Sload, 1, 5, 0, #endif } +#if !defined USE_ANDROID_ASSETS if (0 <= fd) { fd_index = SPECPDL_INDEX (); record_unwind_protect_int (close_file_unwind, fd); } +#else + if (fd.asset || fd.fd >= 0) + { + /* Use a different kind of unwind_protect here. */ + fd_index = SPECPDL_INDEX (); + record_unwind_protect_ptr (close_file_unwind_android_fd, + &fd); + } +#endif #ifdef HAVE_MODULES bool is_module = @@ -1439,11 +1606,12 @@ DEFUN ("load", Fload, Sload, 1, 5, 0, if (is_elc /* version = 1 means the file is empty, in which case we can treat it as not byte-compiled. */ - || (fd >= 0 && (version = safe_to_load_version (file, fd)) > 1)) + || (lread_fd_p + && (version = safe_to_load_version (file, fd)) > 1)) /* Load .elc files directly, but not when they are remote and have no handler! */ { - if (fd != -2) + if (!lread_fd_cmp (-2)) { struct stat s1, s2; int result; @@ -1500,9 +1668,9 @@ DEFUN ("load", Fload, Sload, 1, 5, 0, { Lisp_Object val; - if (fd >= 0) + if (lread_fd_p) { - emacs_close (fd); + lread_close (fd); clear_unwind_protect (fd_index); } val = call4 (Vload_source_file_function, found, hist_file_name, @@ -1512,12 +1680,12 @@ DEFUN ("load", Fload, Sload, 1, 5, 0, } } - if (fd < 0) + if (!lread_fd_p) { /* We somehow got here with fd == -2, meaning the file is deemed to be remote. Don't even try to reopen the file locally; just force a failure. */ - stream = NULL; + stream = file_stream_invalid; errno = EINVAL; } else if (!is_module && !is_native_elisp) @@ -1528,7 +1696,15 @@ DEFUN ("load", Fload, Sload, 1, 5, 0, efound = ENCODE_FILE (found); stream = emacs_fopen (SSDATA (efound), fmode); #else +#if !defined USE_ANDROID_ASSETS stream = fdopen (fd, fmode); +#else + /* Android systems use special file descriptors which can point + into compressed data and double as file streams. FMODE is + unused. */ + ((void) fmode); + stream = fd; +#endif #endif } @@ -1540,15 +1716,15 @@ DEFUN ("load", Fload, Sload, 1, 5, 0, { /* `module-load' uses the file name, so we can close the stream now. */ - if (fd >= 0) + if (lread_fd_p) { - emacs_close (fd); + lread_close (fd); clear_unwind_protect (fd_index); } } else { - if (! stream) + if (!file_stream_valid_p (stream)) report_file_error ("Opening stdio stream", file); set_unwind_protect_ptr (fd_index, close_infile_unwind, infile); input.stream = stream; @@ -1684,7 +1860,8 @@ DEFUN ("locate-file-internal", Flocate_file_internal, Slocate_file_internal, 2, (Lisp_Object filename, Lisp_Object path, Lisp_Object suffixes, Lisp_Object predicate) { Lisp_Object file; - int fd = openp (path, filename, suffixes, &file, predicate, false, true); + int fd = openp (path, filename, suffixes, &file, predicate, false, true, + NULL); if (NILP (predicate) && fd >= 0) emacs_close (fd); return file; @@ -1825,14 +2002,20 @@ maybe_swap_for_eln (bool no_native, Lisp_Object *filename, int *fd, If NEWER is true, try all SUFFIXes and return the result for the newest file that exists. Does not apply to remote files, - or if a non-nil and non-t PREDICATE is specified. + platform-specific files, or if a non-nil and non-t PREDICATE is + specified. - if NO_NATIVE is true do not try to load native code. */ + If NO_NATIVE is true do not try to load native code. + + If PLATFORM is non-NULL and the file being loaded lies in a special + directory, such as the Android `/assets' directory, return a handle + to that directory in *PLATFORM instead of a file descriptor; in + that case, value is -3. */ int openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, Lisp_Object *storeptr, Lisp_Object predicate, bool newer, - bool no_native) + bool no_native, void **platform) { ptrdiff_t fn_size = 100; char buf[100]; @@ -1844,6 +2027,9 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, ptrdiff_t max_suffix_len = 0; int last_errno = ENOENT; int save_fd = -1; +#ifdef USE_ANDROID_ASSETS + struct android_fd_or_asset platform_fd; +#endif USE_SAFE_ALLOCA; /* The last-modified time of the newest matching file found. @@ -2013,7 +2199,30 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, fd = -1; else #endif - fd = emacs_open (pfn, O_RDONLY, 0); + { +#if !defined USE_ANDROID_ASSETS + fd = emacs_open (pfn, O_RDONLY, 0); +#else + if (platform) + { + platform_fd = android_open_asset (pfn, O_RDONLY, 0); + + if (platform_fd.asset + && platform_fd.asset != (void *) -1) + { + *storeptr = string; + goto handle_platform_fd; + } + + if (platform_fd.asset == (void *) -1) + fd = -1; + else + fd = platform_fd.fd; + } + else + fd = emacs_open (pfn, O_RDONLY, 0); +#endif + } if (fd < 0) { @@ -2081,6 +2290,16 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes, SAFE_FREE (); errno = last_errno; return -1; + +#ifdef USE_ANDROID_ASSETS + handle_platform_fd: + + /* Here, openp found a platform specific file descriptor. It can't + be a directory under Android, so return it in *PLATFORM and then + -3 as the file descriptor. */ + *platform = platform_fd.asset; + return -3; +#endif } @@ -3489,7 +3708,7 @@ skip_lazy_string (Lisp_Object readcharfun) ss->string = xrealloc (ss->string, ss->size); } - FILE *instream = infile->stream; + file_stream instream = infile->stream; ss->position = (file_tell (instream) - infile->lookahead); /* Copy that many bytes into the saved string. */ @@ -3499,7 +3718,7 @@ skip_lazy_string (Lisp_Object readcharfun) ss->string[i++] = c = infile->buf[--infile->lookahead]; block_input (); for (; i < nskip && c >= 0; i++) - ss->string[i] = c = getc (instream); + ss->string[i] = c = file_get_char (instream); unblock_input (); ss->length = i; diff --git a/src/process.c b/src/process.c index bdaaba70fea..0eff789e599 100644 --- a/src/process.c +++ b/src/process.c @@ -2008,7 +2008,7 @@ DEFUN ("make-process", Fmake_process, Smake_process, 0, MANY, 0, { tem = Qnil; openp (Vexec_path, program, Vexec_suffixes, &tem, - make_fixnum (X_OK), false, false); + make_fixnum (X_OK), false, false, NULL); if (NILP (tem)) report_file_error ("Searching for program", program); tem = Fexpand_file_name (tem, Qnil); diff --git a/src/sound.c b/src/sound.c index 145100cd433..a51cdb6d97a 100644 --- a/src/sound.c +++ b/src/sound.c @@ -1384,7 +1384,7 @@ DEFUN ("play-sound-internal", Fplay_sound_internal, Splay_sound_internal, 1, 1, /* Open the sound file. */ current_sound->fd = openp (list1 (Vdata_directory), attrs[SOUND_FILE], Qnil, &file, Qnil, - false, false); + false, false, NULL); if (current_sound->fd < 0) sound_perror ("Could not open sound file"); diff --git a/src/w32.c b/src/w32.c index 8d344d2e6da..f45750ea5c1 100644 --- a/src/w32.c +++ b/src/w32.c @@ -10328,7 +10328,8 @@ check_windows_init_file (void) names from UTF-8 to ANSI. */ init_file = build_string ("term/w32-win"); fd = - openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0, 0); + openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0, 0, + NULL); if (fd < 0) { Lisp_Object load_path_print = Fprin1_to_string (Vload_path, diff --git a/src/w32proc.c b/src/w32proc.c index 77a4ac1ff7e..edc4394b17f 100644 --- a/src/w32proc.c +++ b/src/w32proc.c @@ -1956,7 +1956,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp) program = build_string (cmdname); full = Qnil; openp (Vexec_path, program, Vexec_suffixes, &full, make_fixnum (X_OK), - 0, 0); + 0, 0, NULL); if (NILP (full)) { errno = EINVAL; commit a11ad7149bc1908d205e78ecfb240b76ce190bef Merge: 7c8cc9a633d 1e5393a57a3 Author: Po Lu Date: Tue Mar 7 08:43:36 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 7c8cc9a633d18ce0eb8ad14e86ec492ed8391136 Merge: c0a6f14f4a5 aad617870b4 Author: Po Lu Date: Mon Mar 6 16:06:37 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit c0a6f14f4a5069c28b7c90247546f1c5889a6d21 Author: Po Lu Date: Mon Mar 6 15:30:29 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsNative.java (EmacsNative): New function requestSelectionUpdate. * java/org/gnu/emacs/EmacsView.java (onCreateInputConnection): Call it instead of losing if getting the current selection fails. * src/android-asset.h (AAsset_seek): Define stub. * src/android.c (android_open): Take mode_t. (android_open_asset, android_close_asset, android_asset_read_quit) (android_asset_read, android_asset_lseek, android_asset_fstat): New functions. * src/android.h (struct android_fd_or_asset): Update prototypes. * src/androidgui.h (enum android_ime_operation): Add new operation to update the selection position. * src/androidterm.c (android_handle_ime_event): Handle new operation. (requestSelectionUpdate): New function. * src/fileio.c (close_file_unwind_emacs_fd): New function. (Fcopy_file, union read_non_regular, read_non_regular) (Finsert_file_contents): Use optimized codepath to insert Android asset files. * src/frame.h (enum text_conversion_operation): New operation. * src/textconv.c (really_request_point_update) (handle_pending_conversion_events_1, request_point_update): New functions. * src/textconv.h: Update prototypes. diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index 38370d60727..8e626b9534b 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -203,6 +203,7 @@ public static native void performEditorAction (short window, public static native ExtractedText getExtractedText (short window, ExtractedTextRequest req, int flags); + public static native void requestSelectionUpdate (short window); /* Return the current value of the selection, or -1 upon diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index f751eaaa3e4..90a2c912a5a 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -573,20 +573,35 @@ else if (child.getVisibility () != GONE) /* Set a reasonable inputType. */ info.inputType = InputType.TYPE_CLASS_TEXT; - /* Obtain the current position of point and set it as the - selection. */ - selection = EmacsNative.getSelection (window.handle); - - Log.d (TAG, "onCreateInputConnection: current selection is: " + selection); - /* If this fails or ANDROID_IC_MODE_NULL was requested, then don't initialize the input connection. */ - if (mode == EmacsService.IC_MODE_NULL || selection == null) + + if (mode == EmacsService.IC_MODE_NULL) { info.inputType = InputType.TYPE_NULL; return null; } + /* Obtain the current position of point and set it as the + selection. */ + selection = EmacsNative.getSelection (window.handle); + + if (selection != null) + Log.d (TAG, "onCreateInputConnection: current selection is: " + + selection[0] + ", by " + selection[1]); + else + { + Log.d (TAG, "onCreateInputConnection: current selection could" + + " not be retrieved."); + + /* If the selection could not be obtained, return 0 by 0. + However, ask for the selection position to be updated as + soon as possible. */ + + selection = new int[] { 0, 0, }; + EmacsNative.requestSelectionUpdate (window.handle); + } + if (mode == EmacsService.IC_MODE_ACTION) info.imeOptions |= EditorInfo.IME_ACTION_DONE; diff --git a/src/android-asset.h b/src/android-asset.h index 3b2f8105865..4fb309f1645 100644 --- a/src/android-asset.h +++ b/src/android-asset.h @@ -412,3 +412,11 @@ AAsset_read (AAsset *asset, void *buffer, size_t size) return android_asset_read_internal (asset, MIN (size, INT_MAX), buffer); } + +static off_t +AAsset_seek (AAsset *asset, off_t offset, int whence) +{ + /* Java InputStreams don't support seeking at all. */ + errno = ESPIPE; + return -1; +} diff --git a/src/android.c b/src/android.c index 9fc4143602c..f49249de09f 100644 --- a/src/android.c +++ b/src/android.c @@ -1568,7 +1568,7 @@ android_close_on_exec (int fd) contain all assets in the application package. */ int -android_open (const char *filename, int oflag, int mode) +android_open (const char *filename, int oflag, mode_t mode) { const char *name; AAsset *asset; @@ -5971,6 +5971,154 @@ android_set_fullscreen (android_window window, bool fullscreen) +/* External asset management interface. By using functions here + to read and write from files, Emacs can avoid opening a + shared memory file descriptor for each ``asset'' file. */ + +/* Like android_open. However, return a structure that can + either directly hold an AAsset or a file descriptor. + + Value is the structure upon success. Upon failure, value + consists of an uninitialized file descriptor, but its asset + field is set to -1, and errno is set accordingly. */ + +struct android_fd_or_asset +android_open_asset (const char *filename, int oflag, mode_t mode) +{ + const char *name; + struct android_fd_or_asset fd; + AAsset *asset; + + /* Initialize FD by setting its asset to an invalid + pointer. */ + fd.asset = (void *) -1; + + /* See if this is an asset. */ + + if (asset_manager && (name = android_get_asset_name (filename))) + { + /* Return failure for unsupported flags. */ + + if (oflag & O_WRONLY || oflag & O_RDWR) + { + errno = EROFS; + return fd; + } + + if (oflag & O_DIRECTORY) + { + errno = ENOTSUP; + return fd; + } + + /* Now try to open the asset. */ + asset = AAssetManager_open (asset_manager, name, + AASSET_MODE_STREAMING); + + if (!asset) + { + errno = ENOENT; + return fd; + } + + /* Return the asset. */ + fd.asset = asset; + return fd; + } + + /* If the file is not an asset, fall back to android_open and + get a regular file descriptor. */ + + fd.fd = android_open (filename, oflag, mode); + if (fd.fd < 1) + return fd; + + /* Set fd.asset to NULL, signifying that it is a file + descriptor. */ + fd.asset = NULL; + return fd; +} + +/* Like android_close. However, it takes a ``file descriptor'' + opened using android_open_asset. */ + +int +android_close_asset (struct android_fd_or_asset asset) +{ + if (!asset.asset) + return android_close (asset.fd); + + AAsset_close (asset.asset); + return 0; +} + +/* Like `emacs_read_quit'. However, it handles file descriptors + opened using `android_open_asset' as well. */ + +ssize_t +android_asset_read_quit (struct android_fd_or_asset asset, + void *buffer, size_t size) +{ + if (!asset.asset) + return emacs_read_quit (asset.fd, buffer, size); + + /* It doesn't seem possible to quit from inside AAsset_read, + sadly. */ + return AAsset_read (asset.asset, buffer, size); +} + +/* Like `read'. However, it handles file descriptors opened + using `android_open_asset' as well. */ + +ssize_t +android_asset_read (struct android_fd_or_asset asset, + void *buffer, size_t size) +{ + if (!asset.asset) + return read (asset.fd, buffer, size); + + /* It doesn't seem possible to quit from inside AAsset_read, + sadly. */ + return AAsset_read (asset.asset, buffer, size); +} + +/* Like `lseek', but it handles ``file descriptors'' opened with + android_open_asset. */ + +off_t +android_asset_lseek (struct android_fd_or_asset asset, off_t off, + int whence) +{ + if (!asset.asset) + return lseek (asset.fd, off, whence); + + return AAsset_seek (asset.asset, off, whence); +} + +/* Like `fstat'. */ + +int +android_asset_fstat (struct android_fd_or_asset asset, + struct stat *statb) +{ + if (!asset.asset) + return fstat (asset.fd, statb); + + /* Clear statb. */ + memset (statb, 0, sizeof *statb); + + /* Set the mode. */ + statb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH; + + /* Owned by root. */ + statb->st_uid = 0; + statb->st_gid = 0; + + /* Size of the file. */ + statb->st_size = AAsset_getLength (asset.asset); + return 0; +} + #else /* ANDROID_STUBIFY */ /* X emulation functions for Android. */ diff --git a/src/android.h b/src/android.h index 95206b77979..ed0089ad94e 100644 --- a/src/android.h +++ b/src/android.h @@ -46,7 +46,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. extern int android_select (int, fd_set *, fd_set *, fd_set *, struct timespec *); -extern int android_open (const char *, int, int); +extern int android_open (const char *, int, mode_t); extern char *android_user_full_name (struct passwd *); extern int android_fstat (int, struct stat *); extern int android_fstatat (int, const char *restrict, @@ -107,6 +107,30 @@ Copyright (C) 2023 Free Software Foundation, Inc. +/* External asset manager interface. */ + +struct android_fd_or_asset +{ + /* The file descriptor. */ + int fd; + + /* The asset. If set, FD is not a real file descriptor. */ + void *asset; +}; + +extern struct android_fd_or_asset android_open_asset (const char *, + int, mode_t); +extern int android_close_asset (struct android_fd_or_asset); +extern ssize_t android_asset_read_quit (struct android_fd_or_asset, + void *, size_t); +extern ssize_t android_asset_read (struct android_fd_or_asset, + void *, size_t); +extern off_t android_asset_lseek (struct android_fd_or_asset, off_t, int); +extern int android_asset_fstat (struct android_fd_or_asset, + struct stat *); + + + /* Very miscellaneous functions. */ struct android_battery_state diff --git a/src/androidgui.h b/src/androidgui.h index 6514c78d289..e1c80a71a59 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -430,6 +430,7 @@ #define ANDROID_IS_MODIFIER_KEY(key) \ ANDROID_IME_SET_POINT, ANDROID_IME_START_BATCH_EDIT, ANDROID_IME_END_BATCH_EDIT, + ANDROID_IME_REQUEST_SELECTION_UPDATE, }; struct android_ime_event diff --git a/src/androidterm.c b/src/androidterm.c index a6709ac8169..0cc2b35099c 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -630,6 +630,10 @@ android_handle_ime_event (union android_event *event, struct frame *f) case ANDROID_IME_END_BATCH_EDIT: end_batch_edit (f, event->ime.counter); break; + + case ANDROID_IME_REQUEST_SELECTION_UPDATE: + request_point_update (f, event->ime.counter); + break; } } @@ -5169,6 +5173,28 @@ NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object, return string; } +JNIEXPORT void JNICALL +NATIVE_NAME (requestSelectionUpdate) (JNIEnv *env, jobject object, + jshort window) +{ + JNI_STACK_ALIGNMENT_PROLOGUE; + + union android_event event; + + event.ime.type = ANDROID_INPUT_METHOD; + event.ime.serial = ++event_serial; + event.ime.window = window; + event.ime.operation = ANDROID_IME_REQUEST_SELECTION_UPDATE; + event.ime.start = 0; + event.ime.end = 0; + event.ime.length = 0; + event.ime.position = 0; + event.ime.text = NULL; + event.ime.counter = ++edit_counter; + + android_write_event (&event); +} + #ifdef __clang__ #pragma clang diagnostic pop #else diff --git a/src/fileio.c b/src/fileio.c index 7bc9800aaf8..ae244b8f85a 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -113,6 +113,37 @@ #define DRIVE_LETTER(x) c_tolower (x) #include "commands.h" +#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY + +/* Type describing a file descriptor used by functions such as + `insert-file-contents'. */ + +typedef int emacs_fd; + +/* Function used to read and open from such a file descriptor. */ + +#define emacs_fd_open emacs_open +#define emacs_fd_close emacs_close +#define emacs_fd_read emacs_read_quit +#define emacs_fd_lseek lseek +#define emacs_fd_fstat sys_fstat +#define emacs_fd_valid_p(fd) ((fd) >= 0) +#define emacs_fd_to_int(fds) (fds) + +#else /* HAVE_ANDROID && !defined ANDROID_STUBIFY */ + +typedef struct android_fd_or_asset emacs_fd; + +#define emacs_fd_open android_open_asset +#define emacs_fd_close android_close_asset +#define emacs_fd_read android_asset_read_quit +#define emacs_fd_lseek android_asset_lseek +#define emacs_fd_fstat android_asset_fstat +#define emacs_fd_valid_p(fd) ((fd).asset != ((void *) -1)) +#define emacs_fd_to_int(fds) ((fds).asset ? -1 : (fds).fd) + +#endif /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */ + /* True during writing of auto-save files. */ static bool auto_saving; @@ -299,6 +330,15 @@ close_file_unwind (int fd) emacs_close (fd); } +static void +close_file_unwind_emacs_fd (void *ptr) +{ + emacs_fd *fd; + + fd = ptr; + emacs_fd_close (*fd); +} + void fclose_unwind (void *arg) { @@ -2221,7 +2261,8 @@ DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, #else bool already_exists = false; mode_t new_mask; - int ifd, ofd; + emacs_fd ifd; + int ofd; struct stat st; #endif @@ -2265,22 +2306,24 @@ DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, report_file_error ("Copying permissions to", newname); } #else /* not WINDOWSNT */ - ifd = emacs_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0); + ifd = emacs_fd_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0); - if (ifd < 0) + if (!emacs_fd_valid_p (ifd)) report_file_error ("Opening input file", file); - record_unwind_protect_int (close_file_unwind, ifd); + record_unwind_protect_ptr (close_file_unwind_emacs_fd, &ifd); - if (sys_fstat (ifd, &st) != 0) + if (emacs_fd_fstat (ifd, &st) != 0) report_file_error ("Input file status", file); if (!NILP (preserve_permissions)) { #if HAVE_LIBSELINUX - if (is_selinux_enabled ()) + if (is_selinux_enabled () + && emacs_fd_to_int (ifd) != -1) { - conlength = fgetfilecon (ifd, &con); + conlength = fgetfilecon (emacs_fd_to_int (ifd), + &con); if (conlength == -1) report_file_error ("Doing fgetfilecon", file); } @@ -2329,7 +2372,8 @@ DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, maybe_quit (); - if (clone_file (ofd, ifd)) + if (emacs_fd_to_int (ifd) != -1 + && clone_file (ofd, emacs_fd_to_int (ifd))) newsize = st.st_size; else { @@ -2337,30 +2381,38 @@ DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, ssize_t copied; #ifndef MSDOS - for (newsize = 0; newsize < insize; newsize += copied) + newsize = 0; + + if (emacs_fd_to_int (ifd) != -1) { - /* Copy at most COPY_MAX bytes at a time; this is min - (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is - surely aligned well. */ - ssize_t ssize_max = TYPE_MAXIMUM (ssize_t); - ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30; - off_t intail = insize - newsize; - ptrdiff_t len = min (intail, copy_max); - copied = copy_file_range (ifd, NULL, ofd, NULL, len, 0); - if (copied <= 0) - break; - maybe_quit (); + for (; newsize < insize; newsize += copied) + { + /* Copy at most COPY_MAX bytes at a time; this is min + (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is + surely aligned well. */ + ssize_t ssize_max = TYPE_MAXIMUM (ssize_t); + ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30; + off_t intail = insize - newsize; + ptrdiff_t len = min (intail, copy_max); + copied = copy_file_range (emacs_fd_to_int (ifd), NULL, + ofd, NULL, len, 0); + if (copied <= 0) + break; + maybe_quit (); + } } #endif /* MSDOS */ /* Fall back on read+write if copy_file_range failed, or if the - input is empty and so could be a /proc file. read+write will - either succeed, or report an error more precisely than - copy_file_range would. */ + input is empty and so could be a /proc file, or if ifd is an + invention of android.c. read+write will either succeed, or + report an error more precisely than copy_file_range + would. */ if (newsize != insize || insize == 0) { char buf[MAX_ALLOCA]; - for (; (copied = emacs_read_quit (ifd, buf, sizeof buf)); + + for (; (copied = emacs_fd_read (ifd, buf, sizeof buf)); newsize += copied) { if (copied < 0) @@ -2408,8 +2460,10 @@ DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, } } - switch (!NILP (preserve_permissions) - ? qcopy_acl (SSDATA (encoded_file), ifd, + switch ((!NILP (preserve_permissions) + && emacs_fd_to_int (ifd) != -1) + ? qcopy_acl (SSDATA (encoded_file), + emacs_fd_to_int (ifd), SSDATA (encoded_newname), ofd, preserved_permissions) : (already_exists @@ -2449,7 +2503,9 @@ DEFUN ("copy-file", Fcopy_file, Scopy_file, 2, 6, if (emacs_close (ofd) < 0) report_file_error ("Write error", newname); - emacs_close (ifd); + /* Note that ifd is not closed twice because unwind_protects are + discarded at the end of this function. */ + emacs_fd_close (ifd); #ifdef MSDOS /* In DJGPP v2.0 and later, fstat usually returns true file mode bits, @@ -3826,7 +3882,7 @@ decide_coding_unwind (Lisp_Object unwind_data) { struct { - int fd; + emacs_fd fd; ptrdiff_t inserted, trytry; } s; GCALIGNED_UNION_MEMBER @@ -3837,10 +3893,10 @@ verify (GCALIGNED (union read_non_regular)); read_non_regular (Lisp_Object state) { union read_non_regular *data = XFIXNUMPTR (state); - int nbytes = emacs_read_quit (data->s.fd, - ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE - + data->s.inserted), - data->s.trytry); + int nbytes = emacs_fd_read (data->s.fd, + ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE + + data->s.inserted), + data->s.trytry); return make_fixnum (nbytes); } @@ -3989,7 +4045,7 @@ because (1) it preserves some marker positions (in unchanged portions { struct stat st; struct timespec mtime; - int fd; + emacs_fd fd; ptrdiff_t inserted = 0; int unprocessed; specpdl_ref count = SPECPDL_INDEX (); @@ -4067,8 +4123,8 @@ because (1) it preserves some marker positions (in unchanged portions orig_filename = filename; filename = ENCODE_FILE (filename); - fd = emacs_open (SSDATA (filename), O_RDONLY, 0); - if (fd < 0) + fd = emacs_fd_open (SSDATA (filename), O_RDONLY, 0); + if (!emacs_fd_valid_p (fd)) { save_errno = errno; if (NILP (visit)) @@ -4086,7 +4142,7 @@ because (1) it preserves some marker positions (in unchanged portions } specpdl_ref fd_index = SPECPDL_INDEX (); - record_unwind_protect_int (close_file_unwind, fd); + record_unwind_protect_ptr (close_file_unwind_emacs_fd, &fd); /* Replacement should preserve point as it preserves markers. */ if (!NILP (replace)) @@ -4096,7 +4152,7 @@ because (1) it preserves some marker positions (in unchanged portions XCAR (XCAR (window_markers))); } - if (sys_fstat (fd, &st) != 0) + if (emacs_fd_fstat (fd, &st) != 0) report_file_error ("Input file status", orig_filename); mtime = get_stat_mtime (&st); @@ -4117,7 +4173,7 @@ because (1) it preserves some marker positions (in unchanged portions xsignal2 (Qfile_error, build_string ("not a regular file"), orig_filename); - seekable = lseek (fd, 0, SEEK_CUR) < 0; + seekable = emacs_fd_lseek (fd, 0, SEEK_CUR) < 0; if (!NILP (beg) && !seekable) xsignal2 (Qfile_error, build_string ("cannot use a start position in a non-seekable file/device"), @@ -4196,17 +4252,17 @@ because (1) it preserves some marker positions (in unchanged portions int nread; if (st.st_size <= (1024 * 4)) - nread = emacs_read_quit (fd, read_buf, 1024 * 4); + nread = emacs_fd_read (fd, read_buf, 1024 * 4); else { - nread = emacs_read_quit (fd, read_buf, 1024); + nread = emacs_fd_read (fd, read_buf, 1024); if (nread == 1024) { int ntail; - if (lseek (fd, st.st_size - 1024 * 3, SEEK_CUR) < 0) + if (emacs_fd_lseek (fd, st.st_size - 1024 * 3, SEEK_CUR) < 0) report_file_error ("Setting file position", orig_filename); - ntail = emacs_read_quit (fd, read_buf + nread, 1024 * 3); + ntail = emacs_fd_read (fd, read_buf + nread, 1024 * 3); nread = ntail < 0 ? ntail : nread + ntail; } } @@ -4247,7 +4303,7 @@ because (1) it preserves some marker positions (in unchanged portions specpdl_ptr--; /* Rewind the file for the actual read done later. */ - if (lseek (fd, 0, SEEK_SET) < 0) + if (emacs_fd_lseek (fd, 0, SEEK_SET) < 0) report_file_error ("Setting file position", orig_filename); } } @@ -4306,7 +4362,7 @@ because (1) it preserves some marker positions (in unchanged portions if (beg_offset != 0) { - if (lseek (fd, beg_offset, SEEK_SET) < 0) + if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0) report_file_error ("Setting file position", orig_filename); } @@ -4314,7 +4370,7 @@ because (1) it preserves some marker positions (in unchanged portions match the text at the beginning of the buffer. */ while (true) { - int nread = emacs_read_quit (fd, read_buf, sizeof read_buf); + int nread = emacs_fd_read (fd, read_buf, sizeof read_buf); if (nread < 0) report_file_error ("Read error", orig_filename); else if (nread == 0) @@ -4349,7 +4405,7 @@ because (1) it preserves some marker positions (in unchanged portions there's no need to replace anything. */ if (same_at_start - BEGV_BYTE == end_offset - beg_offset) { - emacs_close (fd); + emacs_fd_close (fd); clear_unwind_protect (fd_index); /* Truncate the buffer to the size of the file. */ @@ -4372,14 +4428,14 @@ because (1) it preserves some marker positions (in unchanged portions break; /* How much can we scan in the next step? */ trial = min (curpos, sizeof read_buf); - if (lseek (fd, curpos - trial, SEEK_SET) < 0) + if (emacs_fd_lseek (fd, curpos - trial, SEEK_SET) < 0) report_file_error ("Setting file position", orig_filename); total_read = nread = 0; while (total_read < trial) { - nread = emacs_read_quit (fd, read_buf + total_read, - trial - total_read); + nread = emacs_fd_read (fd, read_buf + total_read, + trial - total_read); if (nread < 0) report_file_error ("Read error", orig_filename); else if (nread == 0) @@ -4499,7 +4555,7 @@ because (1) it preserves some marker positions (in unchanged portions /* First read the whole file, performing code conversion into CONVERSION_BUFFER. */ - if (lseek (fd, beg_offset, SEEK_SET) < 0) + if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0) report_file_error ("Setting file position", orig_filename); inserted = 0; /* Bytes put into CONVERSION_BUFFER so far. */ @@ -4510,8 +4566,8 @@ because (1) it preserves some marker positions (in unchanged portions /* Read at most READ_BUF_SIZE bytes at a time, to allow quitting while reading a huge file. */ - this = emacs_read_quit (fd, read_buf + unprocessed, - READ_BUF_SIZE - unprocessed); + this = emacs_fd_read (fd, read_buf + unprocessed, + READ_BUF_SIZE - unprocessed); if (this <= 0) break; @@ -4526,7 +4582,7 @@ because (1) it preserves some marker positions (in unchanged portions if (this < 0) report_file_error ("Read error", orig_filename); - emacs_close (fd); + emacs_fd_close (fd); clear_unwind_protect (fd_index); if (unprocessed > 0) @@ -4673,7 +4729,7 @@ because (1) it preserves some marker positions (in unchanged portions if (beg_offset != 0 || !NILP (replace)) { - if (lseek (fd, beg_offset, SEEK_SET) < 0) + if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0) report_file_error ("Setting file position", orig_filename); } @@ -4726,10 +4782,10 @@ because (1) it preserves some marker positions (in unchanged portions /* Allow quitting out of the actual I/O. We don't make text part of the buffer until all the reading is done, so a C-g here doesn't do any harm. */ - this = emacs_read_quit (fd, - ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE - + inserted), - trytry); + this = emacs_fd_read (fd, + ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE + + inserted), + trytry); } if (this <= 0) @@ -4756,7 +4812,7 @@ because (1) it preserves some marker positions (in unchanged portions else Fset (Qdeactivate_mark, Qt); - emacs_close (fd); + emacs_fd_close (fd); clear_unwind_protect (fd_index); if (read_quit < 0) diff --git a/src/frame.h b/src/frame.h index ca4cca17d74..e2900d1c15b 100644 --- a/src/frame.h +++ b/src/frame.h @@ -88,6 +88,7 @@ #define EMACS_FRAME_H TEXTCONV_SET_COMPOSING_REGION, TEXTCONV_SET_POINT_AND_MARK, TEXTCONV_DELETE_SURROUNDING_TEXT, + TEXTCONV_REQUEST_POINT_UPDATE, }; /* Structure describing a single edit being performed by the input diff --git a/src/textconv.c b/src/textconv.c index 91d386d4c61..c1ce83b1d7d 100644 --- a/src/textconv.c +++ b/src/textconv.c @@ -957,6 +957,26 @@ really_delete_surrounding_text (struct frame *f, ptrdiff_t left, unbind_to (count, Qnil); } +/* Update the interface with F's new point and mark. If a batch edit + is in progress, schedule the update for when it finishes + instead. */ + +static void +really_request_point_update (struct frame *f) +{ + /* If F's old selected window is no longer live, fail. */ + + if (!WINDOW_LIVE_P (f->old_selected_window)) + return; + + if (f->conversion.batch_edit_count > 0) + f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE; + else if (text_interface && text_interface->point_changed) + text_interface->point_changed (f, + XWINDOW (f->old_selected_window), + current_buffer); +} + /* Set point in F to POSITION. If MARK is not POSITION, activate the mark and set MARK to that as well. @@ -1163,6 +1183,10 @@ handle_pending_conversion_events_1 (struct frame *f, really_delete_surrounding_text (f, XFIXNUM (XCAR (data)), XFIXNUM (XCDR (data))); break; + + case TEXTCONV_REQUEST_POINT_UPDATE: + really_request_point_update (f); + break; } /* Signal success. */ @@ -1457,6 +1481,25 @@ delete_surrounding_text (struct frame *f, ptrdiff_t left, input_pending = true; } +/* Request an immediate call to INTERFACE->point_changed with the new + details of F's region unless a batch edit is in progress. */ + +void +request_point_update (struct frame *f, unsigned long counter) +{ + struct text_conversion_action *action, **last; + + action = xmalloc (sizeof *action); + action->operation = TEXTCONV_REQUEST_POINT_UPDATE; + action->data = Qnil; + action->next = NULL; + action->counter = counter; + for (last = &f->conversion.actions; *last; last = &(*last)->next) + ;; + *last = action; + input_pending = true; +} + /* Return N characters of text around point in F's old selected window. diff --git a/src/textconv.h b/src/textconv.h index 16d13deb092..9bc9e7d9bd1 100644 --- a/src/textconv.h +++ b/src/textconv.h @@ -138,6 +138,7 @@ #define TEXTCONV_SKIP_ACTIVE_REGION (1 << 1) ptrdiff_t, unsigned long); extern void delete_surrounding_text (struct frame *, ptrdiff_t, ptrdiff_t, unsigned long); +extern void request_point_update (struct frame *, unsigned long); extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *, ptrdiff_t *); extern bool conversion_disabled_p (void); commit 31883b8de119ea77f68332b842268b42063b1807 Author: Po Lu Date: Mon Mar 6 11:46:19 2023 +0800 * src/conf_post.h: Avoid macro redeclaration. diff --git a/src/conf_post.h b/src/conf_post.h index 506ddc11270..f31e012dc6e 100644 --- a/src/conf_post.h +++ b/src/conf_post.h @@ -468,5 +468,6 @@ #define VFORK() vfork () #ifdef REPLACEMENT_MB_CUR_MAX #include +#undef MB_CUR_MAX #define MB_CUR_MAX REPLACEMENT_MB_CUR_MAX #endif /* REPLACEMENT_MB_CUR_MAX */ commit 97ca0a855116797779450bfb758ea6c706348df3 Author: Po Lu Date: Mon Mar 6 11:25:51 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsService.java (sync): Delete function. * java/org/gnu/emacs/EmacsView.java (handleDirtyBitmap): Erase with window background. (onDetachedFromWindow): Only recycle bitmap if non-NULL. * java/org/gnu/emacs/EmacsWindow.java (background): New field. (changeWindowBackground): Set it. * src/android.c (struct android_emacs_service): Remove `sync'. (android_init_emacs_service): Likewise. (android_sync): Delete function. * src/androidfns.c (android_create_tip_frame): Set frame background color correctly. (Fx_show_tip): Make the tip frame visible. * src/androidgui.h: Update prototypes. * src/androidterm.c (handle_one_android_event): Handle tooltip movement correctly. diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index d05ebce75dc..f99d7a40067 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -481,25 +481,6 @@ invocation of app_process (through android-emacs) can return String.valueOf (keysym); } - public void - sync () - { - Runnable runnable; - - runnable = new Runnable () { - public void - run () - { - synchronized (this) - { - notify (); - } - } - }; - - syncRunnable (runnable); - } - /* Start the Emacs service if necessary. On Android 26 and up, diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index aefc79c4fb7..f751eaaa3e4 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -168,7 +168,7 @@ public final class EmacsView extends ViewGroup = Bitmap.createBitmap (measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888); - bitmap.eraseColor (0xffffffff); + bitmap.eraseColor (window.background | 0xff000000); /* And canvases. */ canvas = new Canvas (bitmap); @@ -507,7 +507,10 @@ else if (child.getVisibility () != GONE) synchronized (this) { /* Recycle the bitmap and call GC. */ - bitmap.recycle (); + + if (bitmap != null) + bitmap.recycle (); + bitmap = null; canvas = null; surfaceView.setBitmap (null, null); diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index ffc7476f010..9d907d2b481 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -129,6 +129,10 @@ private static class Coordinate /* Whether or not this window is fullscreen. */ public boolean fullscreen; + /* The window background pixel. This is used by EmacsView when + creating new bitmaps. */ + public volatile int background; + public EmacsWindow (short handle, final EmacsWindow parent, int x, int y, int width, int height, boolean overrideRedirect) @@ -183,6 +187,9 @@ private static class Coordinate /* scratchGC is used as the argument to a FillRectangles req. */ scratchGC.foreground = pixel; scratchGC.markDirty (false); + + /* Make the background known to the view as well. */ + background = pixel; } public Rect diff --git a/src/android.c b/src/android.c index 656971e154f..9fc4143602c 100644 --- a/src/android.c +++ b/src/android.c @@ -104,7 +104,6 @@ #define ANDROID_MAX_ASSET_FD 65535 jmethodID get_screen_height; jmethodID detect_mouse; jmethodID name_keysym; - jmethodID sync; jmethodID browse_url; jmethodID restart_emacs; jmethodID update_ic; @@ -2122,7 +2121,6 @@ #define FIND_METHOD(c_name, name, signature) \ FIND_METHOD (get_screen_height, "getScreenHeight", "(Z)I"); FIND_METHOD (detect_mouse, "detectMouse", "()Z"); FIND_METHOD (name_keysym, "nameKeysym", "(I)Ljava/lang/String;"); - FIND_METHOD (sync, "sync", "()V"); FIND_METHOD (browse_url, "browseUrl", "(Ljava/lang/String;)" "Ljava/lang/String;"); FIND_METHOD (restart_emacs, "restartEmacs", "()V"); @@ -4514,15 +4512,6 @@ android_translate_coordinates (android_window src, int x, ANDROID_DELETE_LOCAL_REF (coordinates); } -void -android_sync (void) -{ - (*android_java_env)->CallVoidMethod (android_java_env, - emacs_service, - service_class.sync); - android_exception_check (); -} - int android_wc_lookup_string (android_key_pressed_event *event, wchar_t *buffer_return, int wchars_buffer, diff --git a/src/androidfns.c b/src/androidfns.c index 4837b00a21e..5a23e8bd196 100644 --- a/src/androidfns.c +++ b/src/androidfns.c @@ -1883,9 +1883,10 @@ android_create_tip_frame (struct android_display_info *dpyinfo, unsigned long mask; block_input (); - mask = ANDROID_CW_OVERRIDE_REDIRECT; + mask = ANDROID_CW_OVERRIDE_REDIRECT | ANDROID_CW_BACK_PIXEL; attrs.override_redirect = true; + attrs.background_pixel = FRAME_BACKGROUND_PIXEL (f); tip_window = FRAME_ANDROID_WINDOW (f) = android_create_window (FRAME_DISPLAY_INFO (f)->root_window, @@ -2314,10 +2315,6 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, android_map_raised (FRAME_ANDROID_WINDOW (tip_f)); unblock_input (); - /* Synchronize with the UI thread. This is required to prevent ugly - black splotches. */ - android_sync (); - /* Garbage the tip frame too. */ SET_FRAME_GARBAGED (tip_f); @@ -2328,6 +2325,11 @@ DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0, unbind_to (count_1, Qnil); windows_or_buffers_changed = old_windows_or_buffers_changed; + /* MapNotify events are not sent on Android, so make the frame + visible. */ + + SET_FRAME_VISIBLE (tip_f, true); + start_timer: /* Let the tip disappear after timeout seconds. */ tip_timer = call3 (Qrun_at_time, timeout, Qnil, diff --git a/src/androidgui.h b/src/androidgui.h index 84419457a8a..6514c78d289 100644 --- a/src/androidgui.h +++ b/src/androidgui.h @@ -606,8 +606,6 @@ #define ANDROID_IS_MODIFIER_KEY(key) \ extern void android_map_raised (android_window); extern void android_translate_coordinates (android_window, int, int, int *, int *); -extern void android_sync (void); - extern int android_wc_lookup_string (android_key_pressed_event *, wchar_t *, int, int *, enum android_lookup_status *); diff --git a/src/androidterm.c b/src/androidterm.c index 814af87819b..a6709ac8169 100644 --- a/src/androidterm.c +++ b/src/androidterm.c @@ -676,6 +676,16 @@ handle_one_android_event (struct android_display_info *dpyinfo, if (!f) goto OTHER; + if (FRAME_TOOLTIP_P (f)) + { + if (FRAME_PIXEL_HEIGHT (f) != configureEvent.xconfigure.height + || FRAME_PIXEL_WIDTH (f) != configureEvent.xconfigure.width) + SET_FRAME_GARBAGED (f); + + FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height; + FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width; + } + int width = configureEvent.xconfigure.width; int height = configureEvent.xconfigure.height; commit 0dbbdd20f44c7757835a85a30763af18491f6eb1 Merge: 1cae4648593 6fb8a4dff7e Author: Po Lu Date: Sun Mar 5 19:59:06 2023 +0800 Merge remote-tracking branch 'origin/master' into feature/android commit 1cae464859341bfd5fc7d7ed4b503ef959af81dd Author: Po Lu Date: Sun Mar 5 19:58:28 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsActivity.java (onCreate): * java/org/gnu/emacs/EmacsContextMenu.java: * java/org/gnu/emacs/EmacsDocumentsProvider.java (getMimeType): * java/org/gnu/emacs/EmacsDrawLine.java (perform): * java/org/gnu/emacs/EmacsDrawRectangle.java (perform): * java/org/gnu/emacs/EmacsFillPolygon.java: * java/org/gnu/emacs/EmacsFontDriver.java: * java/org/gnu/emacs/EmacsHandleObject.java: * java/org/gnu/emacs/EmacsInputConnection.java: * java/org/gnu/emacs/EmacsMultitaskActivity.java (EmacsMultitaskActivity): * java/org/gnu/emacs/EmacsNative.java: * java/org/gnu/emacs/EmacsNoninteractive.java (EmacsNoninteractive, main): * java/org/gnu/emacs/EmacsOpenActivity.java (EmacsOpenActivity) (startEmacsClient): * java/org/gnu/emacs/EmacsSdk7FontDriver.java: * java/org/gnu/emacs/EmacsSdk8Clipboard.java: * java/org/gnu/emacs/EmacsService.java (EmacsService, onCreate): * java/org/gnu/emacs/EmacsView.java (EmacsView, onLayout): * java/org/gnu/emacs/EmacsWindow.java (EmacsWindow): * java/org/gnu/emacs/EmacsWindowAttachmentManager.java (EmacsWindowAttachmentManager): Remove redundant includes. Reorganize some functions around, remove duplicate `getLibDir' functions, and remove unused local variables. diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java index 13002d5d0e5..692d8a14e22 100644 --- a/java/org/gnu/emacs/EmacsActivity.java +++ b/java/org/gnu/emacs/EmacsActivity.java @@ -24,18 +24,22 @@ import java.util.ArrayList; import android.app.Activity; + import android.content.Context; import android.content.Intent; + import android.os.Build; import android.os.Bundle; + import android.util.Log; + import android.view.Menu; import android.view.View; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowInsets; import android.view.WindowInsetsController; -import android.widget.FrameLayout.LayoutParams; + import android.widget.FrameLayout; public class EmacsActivity extends Activity @@ -187,6 +191,7 @@ public class EmacsActivity extends Activity Intent intent; View decorView; ViewTreeObserver observer; + int matchParent; /* See if Emacs should be started with -Q. */ intent = getIntent (); @@ -194,8 +199,10 @@ public class EmacsActivity extends Activity = intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q", false); - params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT, - LayoutParams.MATCH_PARENT); + matchParent = FrameLayout.LayoutParams.MATCH_PARENT; + params + = new FrameLayout.LayoutParams (matchParent, + matchParent); /* Make the frame layout. */ layout = new FrameLayout (this); diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java index d1a624e68d9..dec5e148a8e 100644 --- a/java/org/gnu/emacs/EmacsContextMenu.java +++ b/java/org/gnu/emacs/EmacsContextMenu.java @@ -25,7 +25,6 @@ import android.content.Context; import android.content.Intent; -import android.os.Bundle; import android.os.Build; import android.view.Menu; @@ -35,8 +34,6 @@ import android.util.Log; -import android.widget.PopupMenu; - /* Context menu implementation. This object is built from JNI and describes a menu hiearchy. Then, `inflate' can turn it into an Android menu, which can be turned into a popup (or other kind of) diff --git a/java/org/gnu/emacs/EmacsDocumentsProvider.java b/java/org/gnu/emacs/EmacsDocumentsProvider.java index 901c3b909e0..f70da6040ff 100644 --- a/java/org/gnu/emacs/EmacsDocumentsProvider.java +++ b/java/org/gnu/emacs/EmacsDocumentsProvider.java @@ -160,6 +160,7 @@ public final class EmacsDocumentsProvider extends DocumentsProvider { String name, extension, mime; int extensionSeparator; + MimeTypeMap singleton; if (file.isDirectory ()) return Document.MIME_TYPE_DIR; @@ -170,8 +171,9 @@ public final class EmacsDocumentsProvider extends DocumentsProvider if (extensionSeparator > 0) { + singleton = MimeTypeMap.getSingleton (); extension = name.substring (extensionSeparator + 1); - mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension (extension); + mime = singleton.getMimeTypeFromExtension (extension); if (mime != null) return mime; diff --git a/java/org/gnu/emacs/EmacsDrawLine.java b/java/org/gnu/emacs/EmacsDrawLine.java index 0b23138a36c..92e03c48e26 100644 --- a/java/org/gnu/emacs/EmacsDrawLine.java +++ b/java/org/gnu/emacs/EmacsDrawLine.java @@ -21,13 +21,9 @@ import java.lang.Math; -import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; -import android.graphics.PorterDuff.Mode; -import android.graphics.PorterDuffXfermode; import android.graphics.Rect; -import android.graphics.Xfermode; public final class EmacsDrawLine { @@ -38,7 +34,6 @@ public final class EmacsDrawLine Rect rect; Canvas canvas; Paint paint; - int i; /* TODO implement stippling. */ if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED) diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java index ce5e94e4a76..3bd5779c54e 100644 --- a/java/org/gnu/emacs/EmacsDrawRectangle.java +++ b/java/org/gnu/emacs/EmacsDrawRectangle.java @@ -33,7 +33,6 @@ public final class EmacsDrawRectangle perform (EmacsDrawable drawable, EmacsGC gc, int x, int y, int width, int height) { - int i; Paint maskPaint, paint; Canvas maskCanvas; Bitmap maskBitmap; diff --git a/java/org/gnu/emacs/EmacsFillPolygon.java b/java/org/gnu/emacs/EmacsFillPolygon.java index d55a0b3aca8..ea8324c543c 100644 --- a/java/org/gnu/emacs/EmacsFillPolygon.java +++ b/java/org/gnu/emacs/EmacsFillPolygon.java @@ -21,7 +21,6 @@ import java.lang.Math; -import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; diff --git a/java/org/gnu/emacs/EmacsFontDriver.java b/java/org/gnu/emacs/EmacsFontDriver.java index e142a3121d3..ff52899a897 100644 --- a/java/org/gnu/emacs/EmacsFontDriver.java +++ b/java/org/gnu/emacs/EmacsFontDriver.java @@ -19,8 +19,6 @@ package org.gnu.emacs; -import java.util.List; - import android.os.Build; /* This code is mostly unused. See sfntfont-android.c for the code diff --git a/java/org/gnu/emacs/EmacsHandleObject.java b/java/org/gnu/emacs/EmacsHandleObject.java index a57a3bbdfa9..5b889895337 100644 --- a/java/org/gnu/emacs/EmacsHandleObject.java +++ b/java/org/gnu/emacs/EmacsHandleObject.java @@ -19,9 +19,6 @@ package org.gnu.emacs; -import java.util.List; -import java.util.ArrayList; -import java.lang.Object; import java.lang.IllegalStateException; /* This defines something that is a so-called ``handle''. Handles diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java index ed64c368857..7b40fcff99e 100644 --- a/java/org/gnu/emacs/EmacsInputConnection.java +++ b/java/org/gnu/emacs/EmacsInputConnection.java @@ -23,18 +23,13 @@ import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.SurroundingText; import android.view.inputmethod.TextSnapshot; import android.view.KeyEvent; -import android.text.Editable; - import android.util.Log; -/* Android input methods, take number six. - - See EmacsEditable for more details. */ +/* Android input methods, take number six. See textconv.c for more + details; this is more-or-less a thin wrapper around that file. */ public final class EmacsInputConnection extends BaseInputConnection { diff --git a/java/org/gnu/emacs/EmacsMultitaskActivity.java b/java/org/gnu/emacs/EmacsMultitaskActivity.java index dbdc99a3559..b1c48f03fba 100644 --- a/java/org/gnu/emacs/EmacsMultitaskActivity.java +++ b/java/org/gnu/emacs/EmacsMultitaskActivity.java @@ -19,7 +19,11 @@ package org.gnu.emacs; -public class EmacsMultitaskActivity extends EmacsActivity +/* This class only exists because EmacsActivity is already defined as + an activity, and the system wants a new class in order to define a + new activity. */ + +public final class EmacsMultitaskActivity extends EmacsActivity { } diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java index b1205353090..38370d60727 100644 --- a/java/org/gnu/emacs/EmacsNative.java +++ b/java/org/gnu/emacs/EmacsNative.java @@ -19,8 +19,6 @@ package org.gnu.emacs; -import java.lang.System; - import android.content.res.AssetManager; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java index f365037b311..aaba74d877c 100644 --- a/java/org/gnu/emacs/EmacsNoninteractive.java +++ b/java/org/gnu/emacs/EmacsNoninteractive.java @@ -46,21 +46,6 @@ @SuppressWarnings ("unchecked") public final class EmacsNoninteractive { - private static String - getLibraryDirectory (Context context) - { - int apiLevel; - - apiLevel = Build.VERSION.SDK_INT; - - if (apiLevel >= Build.VERSION_CODES.GINGERBREAD) - return context.getApplicationInfo().nativeLibraryDir; - else if (apiLevel >= Build.VERSION_CODES.DONUT) - return context.getApplicationInfo().dataDir + "/lib"; - - return "/data/data/" + context.getPackageName() + "/lib"; - } - public static void main (String[] args) { @@ -188,7 +173,7 @@ else if (apiLevel >= Build.VERSION_CODES.DONUT) /* Now configure Emacs. The class path should already be set. */ filesDir = context.getFilesDir ().getCanonicalPath (); - libDir = getLibraryDirectory (context); + libDir = EmacsService.getLibraryDirectory (context); cacheDir = context.getCacheDir ().getCanonicalPath (); } catch (Exception e) @@ -198,6 +183,7 @@ else if (apiLevel >= Build.VERSION_CODES.DONUT) System.err.println ("and that Emacs needs adjustments in order to"); System.err.println ("obtain required system internal resources."); System.err.println ("Please report this bug to bug-gnu-emacs@gnu.org."); + e.printStackTrace (); System.exit (1); } diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index ac643ae8a13..51335ddb2dd 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -46,7 +46,6 @@ import android.app.AlertDialog; import android.app.Activity; -import android.content.Context; import android.content.ContentResolver; import android.content.DialogInterface; import android.content.Intent; @@ -301,23 +300,6 @@ private class EmacsClientThread extends Thread }); } - public String - getLibraryDirectory () - { - int apiLevel; - Context context; - - context = getApplicationContext (); - apiLevel = Build.VERSION.SDK_INT; - - if (apiLevel >= Build.VERSION_CODES.GINGERBREAD) - return context.getApplicationInfo().nativeLibraryDir; - else if (apiLevel >= Build.VERSION_CODES.DONUT) - return context.getApplicationInfo().dataDir + "/lib"; - - return "/data/data/" + context.getPackageName() + "/lib"; - } - public void startEmacsClient (String fileName) { @@ -327,7 +309,7 @@ else if (apiLevel >= Build.VERSION_CODES.DONUT) EmacsClientThread thread; File file; - libDir = getLibraryDirectory (); + libDir = EmacsService.getLibraryDirectory (this); builder = new ProcessBuilder (libDir + "/libemacsclient.so", fileName, "--reuse-frame", "--timeout=10", "--no-wait"); diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java index ae91c299de8..6df102f18a2 100644 --- a/java/org/gnu/emacs/EmacsSdk7FontDriver.java +++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java @@ -20,7 +20,6 @@ package org.gnu.emacs; import java.io.File; -import java.io.IOException; import java.util.LinkedList; import java.util.List; @@ -32,8 +31,6 @@ import android.util.Log; -import android.os.Build; - public class EmacsSdk7FontDriver extends EmacsFontDriver { private static final String TOFU_STRING = "\uDB3F\uDFFD"; diff --git a/java/org/gnu/emacs/EmacsSdk8Clipboard.java b/java/org/gnu/emacs/EmacsSdk8Clipboard.java index 818a722a908..5a40128b0ac 100644 --- a/java/org/gnu/emacs/EmacsSdk8Clipboard.java +++ b/java/org/gnu/emacs/EmacsSdk8Clipboard.java @@ -19,7 +19,9 @@ package org.gnu.emacs; -/* Importing the entire package avoids the deprecation warning. */ +/* Importing the entire package instead of just the legacy + ClipboardManager class avoids the deprecation warning. */ + import android.text.*; import android.content.Context; diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java index d9cb25f3e9c..d05ebce75dc 100644 --- a/java/org/gnu/emacs/EmacsService.java +++ b/java/org/gnu/emacs/EmacsService.java @@ -24,22 +24,15 @@ import java.io.UnsupportedEncodingException; import java.util.List; -import java.util.ArrayList; -import android.graphics.Canvas; -import android.graphics.Bitmap; import android.graphics.Point; -import android.view.View; import android.view.InputDevice; import android.view.KeyEvent; -import android.annotation.TargetApi; - import android.app.Notification; import android.app.NotificationManager; import android.app.NotificationChannel; -import android.app.PendingIntent; import android.app.Service; import android.content.ClipboardManager; @@ -51,9 +44,6 @@ import android.content.pm.PackageManager; import android.content.res.AssetManager; -import android.database.Cursor; -import android.database.MatrixCursor; - import android.hardware.input.InputManager; import android.net.Uri; @@ -68,9 +58,6 @@ import android.os.VibratorManager; import android.os.VibrationEffect; -import android.provider.DocumentsContract; -import android.provider.DocumentsContract.Document; - import android.util.Log; import android.util.DisplayMetrics; @@ -87,7 +74,6 @@ class Holder public final class EmacsService extends Service { public static final String TAG = "EmacsService"; - public static final int MAX_PENDING_REQUESTS = 256; public static volatile EmacsService SERVICE; public static boolean needDashQ; @@ -107,6 +93,22 @@ public final class EmacsService extends Service information. */ public static final boolean DEBUG_IC = false; + /* Return the directory leading to the directory in which native + library files are stored on behalf of CONTEXT. */ + + public static String + getLibraryDirectory (Context context) + { + int apiLevel; + + apiLevel = Build.VERSION.SDK_INT; + + if (apiLevel >= Build.VERSION_CODES.GINGERBREAD) + return context.getApplicationInfo ().nativeLibraryDir; + + return context.getApplicationInfo ().dataDir + "/lib"; + } + @Override public int onStartCommand (Intent intent, int flags, int startId) @@ -178,23 +180,6 @@ public final class EmacsService extends Service } } - private String - getLibraryDirectory () - { - int apiLevel; - Context context; - - context = getApplicationContext (); - apiLevel = Build.VERSION.SDK_INT; - - if (apiLevel >= Build.VERSION_CODES.GINGERBREAD) - return context.getApplicationInfo().nativeLibraryDir; - else if (apiLevel >= Build.VERSION_CODES.DONUT) - return context.getApplicationInfo().dataDir + "/lib"; - - return "/data/data/" + context.getPackageName() + "/lib"; - } - @Override public void onCreate () @@ -219,7 +204,7 @@ else if (apiLevel >= Build.VERSION_CODES.DONUT) /* Configure Emacs with the asset manager and other necessary parameters. */ filesDir = app_context.getFilesDir ().getCanonicalPath (); - libDir = getLibraryDirectory (); + libDir = getLibraryDirectory (this); cacheDir = app_context.getCacheDir ().getCanonicalPath (); /* Now provide this application's apk file, so a recursive diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java index 617836d8811..aefc79c4fb7 100644 --- a/java/org/gnu/emacs/EmacsView.java +++ b/java/org/gnu/emacs/EmacsView.java @@ -20,7 +20,6 @@ package org.gnu.emacs; import android.content.Context; -import android.content.res.ColorStateList; import android.text.InputType; @@ -116,6 +115,7 @@ public final class EmacsView extends ViewGroup super (EmacsService.SERVICE); Object tem; + Context context; this.window = window; this.damageRegion = new Region (); @@ -133,7 +133,8 @@ public final class EmacsView extends ViewGroup setDefaultFocusHighlightEnabled (false); /* Obtain the input method manager. */ - tem = getContext ().getSystemService (Context.INPUT_METHOD_SERVICE); + context = getContext (); + tem = context.getSystemService (Context.INPUT_METHOD_SERVICE); imManager = (InputMethodManager) tem; } @@ -281,7 +282,6 @@ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST int count, i; View child; Rect windowRect; - int wantedWidth, wantedHeight; count = getChildCount (); @@ -422,7 +422,6 @@ else if (child.getVisibility () != GONE) } } - /* The following two functions must not be called if the view has no parent, or is parented to an activity. */ diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index ea4cf48090d..ffc7476f010 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -31,19 +31,16 @@ import android.graphics.Rect; import android.graphics.Canvas; import android.graphics.Bitmap; -import android.graphics.Point; import android.graphics.PixelFormat; import android.view.View; import android.view.ViewManager; -import android.view.ViewGroup; import android.view.Gravity; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.InputDevice; import android.view.WindowManager; -import android.content.Intent; import android.util.Log; import android.os.Build; @@ -87,7 +84,7 @@ private static class Coordinate public EmacsWindow parent; /* List of all children in stacking order. This must be kept - consistent! */ + consistent with their Z order! */ public ArrayList children; /* Map between pointer identifiers and last known position. Used to @@ -105,9 +102,8 @@ private static class Coordinate last button press or release event. */ public int lastButtonState, lastModifiers; - /* Whether or not the window is mapped, and whether or not it is - deiconified. */ - private boolean isMapped, isIconified; + /* Whether or not the window is mapped. */ + private boolean isMapped; /* Whether or not to ask for focus upon being mapped, and whether or not the window should be focusable. */ @@ -122,7 +118,8 @@ private static class Coordinate private WindowManager windowManager; /* The time of the last KEYCODE_VOLUME_DOWN release. This is used - to quit Emacs. */ + to quit Emacs upon two rapid clicks of the volume down + button. */ private long lastVolumeButtonRelease; /* Linked list of character strings which were recently sent as @@ -1103,14 +1100,12 @@ else if (EmacsWindow.this.isMapped) public void noticeIconified () { - isIconified = true; EmacsNative.sendIconified (this.handle); } public void noticeDeiconified () { - isIconified = false; EmacsNative.sendDeiconified (this.handle); } diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java index 1548bf28087..30f29250970 100644 --- a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java +++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java @@ -20,7 +20,6 @@ package org.gnu.emacs; import java.util.ArrayList; -import java.util.LinkedList; import java.util.List; import android.content.Intent; @@ -74,8 +73,8 @@ public interface WindowConsumer public EmacsWindowAttachmentManager () { - consumers = new LinkedList (); - windows = new LinkedList (); + consumers = new ArrayList (); + windows = new ArrayList (); } public void commit 26b3b8433d933c9f8b26b83ca96ac1bb47711907 Author: Po Lu Date: Sun Mar 5 15:55:24 2023 +0800 Update Android port * java/org/gnu/emacs/EmacsOpenActivity.java (onCreate): Don't set the style here. * java/res/values-v11/style.xml: * java/res/values-v14/style.xml: * java/res/values-v29/style.xml: * java/res/values/style.xml: Define styles for the emacsclient wrapper. * src/keyboard.c (read_key_sequence): Don't disable text conversion if use_mouse_menu or if a menu bar prefix key is being displayed. diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java index fddd5331d2f..ac643ae8a13 100644 --- a/java/org/gnu/emacs/EmacsOpenActivity.java +++ b/java/org/gnu/emacs/EmacsOpenActivity.java @@ -380,11 +380,6 @@ else if (apiLevel >= Build.VERSION_CODES.DONUT) return; } - /* Set an appropriate theme. */ - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) - setTheme (android.R.style.Theme_DeviceDefault); - /* Now see if the action specified is supported by Emacs. */ if (action.equals ("android.intent.action.VIEW") diff --git a/java/res/values-v11/style.xml b/java/res/values-v11/style.xml index 50cf96e8bc5..b114758bf0d 100644 --- a/java/res/values-v11/style.xml +++ b/java/res/values-v11/style.xml @@ -20,4 +20,5 @@ + + diff --git a/java/res/values/style.xml b/java/res/values/style.xml new file mode 100644 index 00000000000..396b7d8da39 --- /dev/null +++ b/java/res/values/style.xml @@ -0,0 +1,25 @@ + + + + +