zsh-workers
 help / color / mirror / code / Atom feed
Search results ordered by [date|relevance]  view[summary|nested|Atom feed]
thread overview below | download mbox.gz: |
* Re: Why sourcing a file is not faster than doing a loop with eval, zle -N
@ 2017-12-23 22:19  3% Joey Pabalinas
  0 siblings, 0 replies; 200+ results
From: Joey Pabalinas @ 2017-12-23 22:19 UTC (permalink / raw)
  To: psprint; +Cc: zsh-workers, p.w.stephenson

[-- Attachment #1: Type: text/plain, Size: 849 bytes --]

On December 23, 2017 3:01 PM, Sebastian Gniazdowski wrote:
> We could do a pair of calls, first fgets(), then fgetc(), and this way
> solve this problem?

Wouldn't work because `fgets()` just returns the string as well as storing it
in the buffer instead of doing the _sane_ thing and returning the number of
characters read. Because of that it's impossible to know if there are actually
any useful characters in our buffer past the first '\0' (if you tried to read
past that you would just be asking for a buffer overrun).

Sadly, `fgets()` is of limited use for our purposes if the lines may have
embedded '\0' characters. A possible alternative is something like
POSIX `getline()` but I don't know if something like that would be kosher
for Zsh.

If it is, though, give a holler and I will cook up a patch.

-- 
Joey Pabalinas

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[relevance 3%]

* Room for optimization, source "..." vs. eval "$(<...)"
@ 2017-12-24  9:43  1% Sebastian Gniazdowski
  0 siblings, 0 replies; 200+ results
From: Sebastian Gniazdowski @ 2017-12-24  9:43 UTC (permalink / raw)
  To: zsh-workers

[-- Attachment #1: Type: text/plain, Size: 2112 bytes --]

Hello
(All test results and data are in:
https://github.com/zdharma/hacking-private/tree/master/eval-room )

I've compared time of 'source srctest.zsh' and 'eval "$(<srctest.zsh)" (the file has "a=1; b=2; c=3;" 300k times):

% time ./script1_srcopt.zsh  # source
./script1_srcopt.zsh  1.49s user 0.30s system 99% cpu 1.794 total

% time ./script2_srcopt.zsh  # eval
./script2_srcopt.zsh  1,01s user 0,08s system 98% cpu 1,105 total

This points that there should be much room for optimization?

Next I've run callgrind on each script, generated annotate, tweaked both results so that functions are 1-to-1 left and right. Diff generated with icdiff:

https://raw.githubusercontent.com/zdharma/hacking-private/master/eval-room/source_vs_eval.png

Some conclusions:

1. Difference is 0.9 * 10^9 instructions (eval has less).
2. Parser runs in almost *identical* way.
3. Biggest difference is in malloc-related functions:
   - szone_free_definite_size - 140 million,
   - szone_malloc_should_clear – 70 million,
   - szone_size – 83 million,
   - free – 40 million.
4. There's significant difference for memmove: 64 million.
5. For zhalloc it's 17 million.
6. Quite large difference for execlist: 27.8 million.
7. ingetc: 16.5 million.
8. strlen: 13.6 million.
9. For mutex functions, it's 52.5 million difference.

Epilogue, after Stephane catched the system-time use, we have 2x or more faster Zsh:

% time ./script1_srcopt.zsh # switched to zsh-5.3
./script1_srcopt.zsh  2,69s user 1,96s system 99% cpu 4,669 total

This is like a historical event IMO because frameworks can grow in size and/or stop being laggy.

Maybe the above results hint that something similar-easy is still possible? Or could POSIX getline() use be implemented (Joey Pabalinas offers a patch), to gain another milliseconds? It's pure gold for nothing.

PS. A plugin manager could cheat, set 0=... and do eval "$(<...)", but many plugins today use ${(%):-%N} instead of 0, and there's probably no way to cheat that?

--  
Sebastian Gniazdowski
psprint /at/ zdharma.org

[-- Attachment #2: annotate_eval.txt --]
[-- Type: text/plain, Size: 26585 bytes --]

--------------------------------------------------------------------------------
Profile data file 'callgrind.out.36512' (creator: callgrind-3.13.0)
--------------------------------------------------------------------------------
I1 cache:
D1 cache:
LL cache:
Timerange: Basic block 0 - 1012833493
Trigger: Program termination
Profiled target:  zsh-5.4.2-dev-0 -f ./script2_srcopt.zsh (PID 36512, part 1)
Events recorded:  Ir
Events shown:     Ir
Event sort order: Ir
Thresholds:       100
Include dirs:
User annotated:
Auto-annotation:  off

--------------------------------------------------------------------------------
           Ir
--------------------------------------------------------------------------------
5,175,788,248  PROGRAM TOTALS

--------------------------------------------------------------------------------
         Ir  file:function
--------------------------------------------------------------------------------
468,137,788  _pthread_mutex_unlock_slow
378,604,490  ingetc
319,594,930  _pthread_mutex_lock_slow
270,001,086  gettokstr
253,955,336  itype_end
239,446,048  par_cmd
235,201,386  zshlex
 71,973,633  szone_free_definite_size
124,536,168  _platform_memmove
167,496,197  zhalloc
 87,551,154  szone_size
 63,158,063  szone_malloc_should_clear
120,600,000  addvars
103,972,946  strlen
 84,600,094  execlist'2
103,499,199  fgetc
      1,307  shingetline
 90,017,029  fetchvalue
 85,500,255  readoutput
 84,900,625  exalias
 40,509,585  free
 81,000,206  remnulargs
 77,400,257  ecstrcode
 75,600,213  inungetc
 71,100,204  par_sublist
 70,200,054  ecgetstr
 69,307,579  assignsparam
    860,984  tiny_malloc_from_free_list
 64,876,963  hasher
 63,000,610  has_token
 58,516,581  strcpy
    981,478  tiny_free_list_add_ptr
 59,400,000  execsimple
 58,506,441  assignstrvalue
 58,500,121  hasbraces
 54,900,075  set_list_code
 54,019,424  par_pline
 40,500,165  haswilds
 39,600,103  par_list
 37,800,924  dupstring
 35,100,321  checkalias
 32,400,420  add
 31,509,205  funlockfile
 31,509,205  flockfile
 31,501,272  zjoin
 31,500,294  untokenize
 31,500,056  setunderscore
 28,816,663  gethashnode2
 28,016,792  _platform_strcmp
 27,929,526  zalloc
 27,906,987  getparamnode
 25,745,359  _platform_bzero$VARIANT$Merom
 25,228,168  malloc_zone_malloc
 22,506,524  _platform_strchr$VARIANT$Generic
 22,503,600  isident
 21,600,435  zshlex_raw_add
 21,600,397  gethashnode
 19,801,980  strsetfn
 18,909,429  ztrdup
 18,021,476  malloc
 18,017,131  pthread_mutex_lock
 17,114,970  watchlog_match
 16,202,556  strrchr
 16,200,048  zshlex_raw_back
 15,300,761  check_warn_pm
 14,400,048  input_hasalias
 13,518,444  pthread_mutex_unlock
 13,500,345  hcalloc
 12,600,064  ugetnode
 11,702,577  __vsnprintf_chk
  9,112,785  _os_lock_spin_lock
  8,400,296  nohw
  8,400,044  nohwe
  6,300,649  zsfree
  4,721,799  ImageLoaderMachOCompressed::trieWalk(unsigned char const*, unsigned char const*, char const*)
  4,505,135  szone_malloc
  3,645,314  os_lock_lock
  3,645,314  os_lock_unlock
  3,645,114  _os_lock_spin_unlock
  2,750,686  get_node_from_uniquing_table
    754,596  ImageLoaderMachO::findExportedSymbol(char const*, bool, ImageLoader const**) const'2
    577,706  large_entry_for_pointer_no_lock
    551,413  szone_realloc
    462,042  ImageLoaderMachOCompressed::libReExported(unsigned int) const
    343,351  ImageLoaderMachOCompressed::rebase(ImageLoader::LinkContext const&)
    323,702  tiny_free_list_remove_ptr
    286,059  ImageLoaderMachOCompressed::findExportedSymbol(char const*, ImageLoader const**) const
    283,122  szone_size_try_large
    225,784  realloc
    184,512  ImageLoaderMachOCompressed::eachBind(ImageLoader::LinkContext const&, unsigned long (ImageLoaderMachOCompressed::*)(ImageLoader::LinkContext const&, unsigned long, unsigned char, char const*, unsigned char, long, long, char const*, ImageLoaderMachOCompressed::LastLookup*, bool))
    178,553  zrealloc
    174,990  ImageLoaderMachO::findExportedSymbol(char const*, bool, ImageLoader const**) const
    166,828  dyld_stub_binder'2
    166,727  dyld::loadPhase5(char const*, char const*, dyld::LoadContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >*)
    159,994  strcmp
    157,500  malloc_zone_realloc
    154,744  get_tiny_free_size
    148,036  mach_vm_allocate
    146,902  szone_good_size
    144,709  get_tiny_previous_free_msize
    138,705  ImageLoaderMachOCompressed::doBindFastLazySymbol(unsigned int, ImageLoader::LinkContext const&, void (*)(), void (*)())
    124,502  _Read_RuneMagi
    118,878  ImageLoaderMachOCompressed::exportedSymbolAddress(ImageLoader::LinkContext const&, ImageLoader::Symbol const*, ImageLoader const*, bool) const
     98,479  _platform_strncmp
     98,319  mmap
     95,459  ImageLoaderMachOCompressed::resolve(ImageLoader::LinkContext const&, char const*, unsigned char, long, ImageLoader const**, ImageLoaderMachOCompressed::LastLookup*, bool)
     92,075  ImageLoader::hash(char const*)
     86,952  __fread
     82,152  ImageLoaderMachOCompressed::libImage(unsigned int) const
     79,290  vm_allocate
     77,693  freeheap
     71,218  getsectiondata
     65,961  munmap
     61,109  _mapStrHash(_NXMapTable*, void const*)
     57,224  ImageLoaderMachO::bindLocation(ImageLoader::LinkContext const&, unsigned long, unsigned long, ImageLoader const*, unsigned char, char const*, long, char const*)
     55,996  ImageLoader::recursiveLoadLibraries(ImageLoader::LinkContext const&, bool, ImageLoader::RPathChain const&)'2
     55,356  ImageLoader::matchInstallPath() const
     54,359  bcmp
     48,031  ImageLoaderMachOCompressed::bindAt(ImageLoader::LinkContext const&, unsigned long, unsigned char, char const*, unsigned char, long, long, char const*, ImageLoaderMachOCompressed::LastLookup*, bool)
     46,376  ImageLoaderMachOCompressed::resolveTwolevel(ImageLoader::LinkContext const&, ImageLoader const*, bool, char const*, bool, ImageLoader const**)
     44,283  dyld_stub_binder
     42,261  ImageLoaderMachO::parseLoadCmds(ImageLoader::LinkContext const&)
     37,750  __srefill0
     36,490  __findenv
     33,453  fread
     32,826  arc4_stir
     30,304  ImageLoader::recursiveUpdateDepth(unsigned int)'2
     30,112  strncmp
     29,104  pthread_once
     28,222  ImageLoaderMachO::sniffLoadCommands(macho_header const*, char const*, bool, bool*, unsigned int*, unsigned int*, ImageLoader::LinkContext const&, linkedit_data_command const**, encryption_info_command const**)
     27,762  ImageLoader::compare(ImageLoader const*) const
     25,004  addhashnode2
     23,822  _qsort'2
     23,606  dyld::loadPhase3(char const*, char const*, dyld::LoadContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >*)
     23,339  ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&)'2
     23,318  __srefill1
     21,540  ImageLoaderMachOCompressed::segmentCommandOffsets() const
     21,412  ImageLoaderMachO::registerInterposing()
     21,320  ImageLoaderMachO::getExportedSymbolAddress(ImageLoader::Symbol const*, ImageLoader::LinkContext const&, ImageLoader const*, bool) const
     21,148  _kernelrpc_mach_vm_allocate_trap
     20,928  __srget
     20,774  ImageLoaderMachO::segActualLoadAddress(unsigned int) const
     20,524  _NXMapMember(_NXMapTable*, void const*, void**)
     18,611  ImageLoaderMachO::doGetDependentLibraries(ImageLoader::DependentLibraryInfo*)
     17,812  _sread
     17,562  void std::__1::__insertion_sort<method_t::SortBySELAddress&, entsize_list_tt<method_t, method_list_t, 3u>::iterator>(entsize_list_tt<method_t, method_list_t, 3u>::iterator, entsize_list_tt<method_t, method_list_t, 3u>::iterator, method_t::SortBySELAddress&)
     17,216  dyld::load(char const*, dyld::LoadContext const&)
     16,980  copySwiftV1MangledName(char const*, bool)
     16,365  __mmap
     16,226  _mapStrIsEqual(_NXMapTable*, void const*, void const*)
     16,192  dyld::checkandAddImage(ImageLoader*, dyld::LoadContext const&)
     16,133  ImageLoader::recursiveBind(ImageLoader::LinkContext const&, bool, bool)'2
     15,844  pthread_rwlock_unlock
     15,705  __munmap
     15,579  dyld::notifyBatchPartial(dyld_image_states, bool, char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*))
     15,548  _readdir_unlocked$INODE64
     15,173  NXMapInsert
     15,059  dyld::loadPhase0(char const*, char const*, dyld::LoadContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >*)
     14,612  ImageLoaderMachO::getInstallPath() const
     14,326  __srefill
     14,313  pthread_mutex_init
     14,274  ImageLoader::interposedAddress(ImageLoader::LinkContext const&, unsigned long, ImageLoader const*, ImageLoader const*)
     14,184  _qsort
     13,720  metafy
     13,498  dyld::loadPhase1(char const*, char const*, dyld::LoadContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >*)
     13,486  ImageLoaderMachO::setupLazyPointerHandler(ImageLoader::LinkContext const&)
     13,431  dyld::loadPhase4(char const*, char const*, dyld::LoadContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >*)
     13,102  ImageLoader::recursiveRebase(ImageLoader::LinkContext const&)'2
     12,970  realizeClass(objc_class*)'2
     12,711  ImageLoader::ImageLoader(char const*, unsigned int)
     12,700  dyld::notifySingle(dyld_image_states, ImageLoader const*)
     12,530  ImageLoader::recursiveGetDOFSections(ImageLoader::LinkContext const&, std::__1::vector<ImageLoader::DOFInfo, std::__1::allocator<ImageLoader::DOFInfo> >&)'2
     12,286  _pthread_rwlock_lock
     12,057  __vsnprintf_chk'2
     11,770  dyld::findLoadedImage(stat const&)
     11,355  dyld::fastBindLazySymbol(ImageLoader**, unsigned long)
     11,286  ImageLoader::setPathUnowned(char const*)
     11,220  __sel_registerName(char const*, int, int)
     11,206  ImageLoaderMachO::mapSegments(int, unsigned long long, unsigned long long, unsigned long long, ImageLoader::LinkContext const&)
     11,179  dyld::findMappedRange(unsigned long)
     11,078  getsegmentdata
     10,782  dyld::imageSorter(void const*, void const*)
     10,758  ImageLoader::statMatch(stat const&) const
     10,410  ImageLoaderMachOCompressed::libIsUpward(unsigned int) const
      9,516  autofeatures
      9,098  ImageLoaderMachO::ImageLoaderMachO(macho_header const*, char const*, unsigned int, unsigned int*, unsigned int)
      8,969  createparamtable
      8,910  malloc_zone_calloc
      8,900  ImageLoaderMachO::sdkVersion() const
      8,692  realizeClass(objc_class*)
      8,680  ImageLoaderMachO::getRPaths(ImageLoader::LinkContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >&) const
      8,672  tzload
      7,770  __sread
      7,200  dyld::addMappedRange(ImageLoader*, unsigned long, unsigned long)
      7,120  ImageLoaderMachO::segSize(unsigned int) const
      7,108  createparam
      6,953  NXMapInsert'2
      6,930  ImageLoaderMachO::segActualEndAddress(unsigned int) const
      6,910  __collate_load_tables
      6,821  ImageLoader::recursiveSpinLock(ImageLoader::recursive_lock&)
      6,799  _dyld_fast_stub_entry(void*, long)
      6,780  remapClass(objc_class*)
      6,696  malloc_zone_from_ptr
      6,672  fixupMethodList(method_list_t*, bool, bool)
      6,428  mkenvstr
      6,390  ImageLoaderMachOCompressed::incrementCoalIterator(ImageLoader::CoalIterator&)
      6,342  ImageLoaderMachO::usesTwoLevelNameSpace() const
      6,324  addhashnode
      6,323  _platform_memchr$VARIANT$Generic
      6,304  ImageLoaderMachO::segFileOffset(unsigned int) const
      6,192  ImageLoaderMachO::assignSegmentAddresses(ImageLoader::LinkContext const&)
      6,020  NXHashInsert
      6,004  readdir$INODE64
      5,760  ImageLoader::getRealPath() const
      5,740  devname_r
      5,712  dyld::libraryLocator(char const*, bool, char const*, ImageLoader::RPathChain const*)
      5,642  ImageLoaderMachOCompressed::instantiateFromFile(char const*, int, unsigned char const*, unsigned long, unsigned long long, unsigned long long, stat const&, unsigned int, unsigned int, linkedit_data_command const*, encryption_info_command const*, ImageLoader::LinkContext const&)
      5,630  readClass(objc_class*, bool, bool)
      5,590  read$NOCANCEL
      5,586  dyld::addImage(ImageLoader*)
      5,371  scanmatchtable
      5,355  ImageLoaderMachO::doGetLibraryInfo()
      5,044  NXHashInsert'2
      4,998  ImageLoaderMachOCompressed::setLibImage(unsigned int, ImageLoader*, bool, bool)
      4,882  ImageLoaderMachO::needsAddedLibSystemDepency(unsigned int, macho_header const*)
      4,832  ImageLoaderMachO::segFileSize(unsigned int) const
      4,742  addSubclass(objc_class*, objc_class*)
      4,642  calloc
      4,626  ImageLoaderMachO::segWriteable(unsigned int) const
      4,608  NXMapGet
      4,568  ztrdup_metafy
      4,556  ImageLoaderMachO::segUnaccessible(unsigned int) const
      4,314  _read_images
      4,223  _hash_bucket
      4,160  ImageLoaderMachO::segPreferredLoadAddress(unsigned int) const
      4,060  ImageLoaderMachO::machHeader() const
      4,035  OSAtomicCompareAndSwap64$VARIANT$mp
      4,024  __part_load_locale
      4,014  small_malloc_from_free_list
      4,010  dyld::loadPhase6(int, stat const&, char const*, dyld::LoadContext const&)
      3,822  NXMapRemove
      3,810  szone_calloc
      3,768  dyld::fatFindBest(fat_header const*, unsigned long long*, unsigned long long*)
      3,726  ImageLoaderMachO::preFetchDATA(int, unsigned long long, ImageLoader::LinkContext const&)
      3,605  ImageLoaderMachO::inSharedCache() const
      3,597  ImageLoaderMachOCompressed::markLINKEDIT(ImageLoader::LinkContext const&, int)
      3,575  ImageLoader::lastModified() const
      3,552  search_method_list(method_list_t const*, objc_selector*)
      3,552  _xpc_serializer_append
      3,540  prepareMethodLists(objc_class*, method_list_t**, int, bool, bool)
      3,520  ImageLoaderMachO::instantiateFromFile(char const*, int, unsigned char const*, unsigned long long, unsigned long long, stat const&, ImageLoader::LinkContext const&)
      3,430  ImageLoaderMachO::isDylib() const
      3,360  objc_msgSend'2
      3,314  setemulate
      3,306  RWLOCK_GETSEQ_ADDR
      3,306  NXNextHashState
      3,263  _simple_getenv
      3,245  ImageLoaderMachO::doGetDOFSections(ImageLoader::LinkContext const&, std::__1::vector<ImageLoader::DOFInfo, std::__1::allocator<ImageLoader::DOFInfo> >&)
      3,233  ImageLoaderMachOCompressed::updateUsesCoalIterator(ImageLoader::CoalIterator&, unsigned long, ImageLoader*, ImageLoader::LinkContext const&)
      3,207  fcntl
      3,188  getMethodNoSuper_nolock(objc_class*, objc_selector*)
      2,990  memchr
      2,963  ImageLoaderMachO::getUUID(unsigned char*) const
      2,832  _pthread_cond_signal
      2,816  list_array_tt<unsigned long, protocol_list_t>::attachLists(protocol_list_t* const*, unsigned int)
      2,816  class_createInstance
      2,791  slot_name
      2,737  zshcalloc
      2,726  allocBuckets(void*, unsigned int)
      2,718  memcpy
      2,640  lookUpImpOrForward
      2,597  szone_free
      2,589  colonsplit
      2,546  ImageLoader::weakBind(ImageLoader::LinkContext const&)
      2,430  ImageLoaderMachO::usablePrebinding(ImageLoader::LinkContext const&) const
      2,419  _nc_table_insert_type
      2,366  _xpc_dictionary_insert
      2,360  _nc_table_find_get_key
      2,352  objc_destructInstance
      2,340  ImageLoaderMachO::segExecutable(unsigned int) const
      2,290  ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&)
      2,289  zinsertlinknode
      2,288  objc_class::setRequiresRawIsa(bool)
      2,230  freeBuckets(NXHashTable*, int)
      2,214  cache_fill
      2,210  ImageLoaderMachO::segReadable(unsigned int) const
      2,208  ImageLoaderMachO::segName(unsigned int) const
      2,198  ImageLoaderMachO::validateFirstPages(linkedit_data_command const*, int, unsigned char const*, unsigned long, long long, ImageLoader::LinkContext const&)
      2,184  map_images_nolock
      2,156  objc_object::sidetable_clearDeallocating()
      2,150  dyld::findInSharedCacheImage(char const*, bool, stat const*, macho_header const**, char const**, long*)
      2,118  addImagesToAllImages(unsigned int, dyld_image_info const*)
      2,070  ImageLoaderMachOCompressed::doBind(ImageLoader::LinkContext const&, bool)
      1,984  ImageLoaderMachOCompressed::findExportedSymbol(char const*, ImageLoader const**) const'2
      1,941  allocate_pages
      1,940  objc_class::setHasCustomAWZ(bool)
      1,932  rwlock_tt<false>::write()
      1,929  __si_module_static_cache_block_invoke
      1,920  list_array_tt<method_t, method_list_t>::attachLists(method_list_t* const*, unsigned int)
      1,914  ImageLoaderMachO::loadCodeSignature(linkedit_data_command const*, int, unsigned long long, ImageLoader::LinkContext const&)
      1,898  getPreoptimizedClass
      1,889  env_unsetenv
      1,875  objc_image_info* getDataSection<objc_image_info>(mach_header_64 const*, char const*, unsigned long*, unsigned long*)
      1,866  strnlen
      1,862  attachCategories(objc_class*, locstamped_category_list_t*, bool)
      1,845  dyld::isCompatibleMachO(unsigned char const*, char const*)
      1,714  large_malloc
      1,702  mach_absolute_time
      1,680  add_autoparam
      1,670  bcopy
      1,656  _xpc_has_prefix
      1,649  objc_class::setHasCustomRR(bool)
      1,616  _class_initialize'2
      1,615  ImageLoaderMachOCompressed::isSubframeworkOf(ImageLoader::LinkContext const&, ImageLoader const*) const
      1,615  ImageLoaderMachOCompressed::hasSubLibrary(ImageLoader::LinkContext const&, ImageLoader const*) const
      1,608  dyld::registerImageStateSingleChangeHandler(dyld_image_states, char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*))
      1,606  addNamedClass(objc_class*, char const*, objc_class*)
      1,600  _os_object_alloc_realized
      1,596  _NXMapRehash(_NXMapTable*)
      1,580  lstat$INODE64
      1,572  cache_getImp
      1,567  _voucher_init
      1,548  _xpc_serializer_read
      1,533  getClass(char const*)
      1,529  _object_remove_assocations
      1,503  objc_opt::objc_clsopt_t::getClassesAndHeaders(char const*, void**, void**) const'2
      1,490  ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&)
      1,472  _pthread_mutex_check_init_slow
      1,446  void std::__1::__stable_sort<method_t::SortBySELAddress&, entsize_list_tt<method_t, method_list_t, 3u>::iterator>(entsize_list_tt<method_t, method_list_t, 3u>::iterator, entsize_list_tt<method_t, method_list_t, 3u>::iterator, method_t::SortBySELAddress&, std::__1::iterator_traits<entsize_list_tt<method_t, method_list_t, 3u>::iterator>::difference_type, std::__1::iterator_traits<entsize_list_tt<method_t, method_list_t, 3u>::iterator>::value_type*, long)
      1,438  emptyhashtable
      1,435  addenv
      1,432  ImageLoaderMachO::isPrebindable() const
      1,419  lookUpImpOrForward'2
      1,408  list_array_tt<property_t, property_list_t>::attachLists(property_list_t* const*, unsigned int)
      1,407  dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*)
      1,400  mach_vm_map
      1,393  createoptiontable
      1,376  xmmap
      1,346  environ_init
      1,342  LI_ils_create
      1,320  ImageLoaderMachO::crashIfInvalidCodeSignature()
      1,308  _dyld_func_lookup
      1,287  unattachedCategoriesForClass(objc_class*, bool)
      1,276  ___ZN10objc_class17setRequiresRawIsaEb_block_invoke
      1,274  objc::DenseMapBase<objc::DenseMap<DisguisedPtr<objc_object>, unsigned long, true, objc::DenseMapInfo<DisguisedPtr<objc_object> > >, DisguisedPtr<objc_object>, unsigned long, objc::DenseMapInfo<DisguisedPtr<objc_object> >, true>::find(DisguisedPtr<objc_object> const&)
      1,272  dyld::findExportedSymbol(char const*, bool, ImageLoader::Symbol const**, ImageLoader const**)
      1,262  __bzero
      1,260  ImageLoaderMachO::participatesInCoalescing() const
      1,260  rwlock_tt<false>::unlockWrite()
      1,257  addbuiltins
      1,251  _NXHashRehashToCapacity
      1,246  addNonSharedCacheImageUUID(dyld_uuid_info const&)
      1,233  mach_msg
      1,219  dyld::parseColonList(char const*, char const*)
      1,215  ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&)
      1,212  strstr
      1,170  small_free_list_add_ptr
      1,165  uname
      1,138  _platform_memcmp
      1,137  _xpc_dictionary_apply_node_f
      1,127  rwlock_tt<false>::read()
      1,097  objc_class::setInitialized()
      1,095  getClass_impl(char const*)
      1,092  _class_initialize
      1,090  getProtocol(char const*)
      1,064  std::__1::__call_once(unsigned long volatile&, void*, void (*)(void*))
      1,056  ImageLoaderMachO::reserveAnAddressRange(unsigned long, ImageLoader::LinkContext const&)
      1,031  objc_opt::objc_clsopt_t::getClassesAndHeaders(char const*, void**, void**) const
      1,029  bool objc::DenseMapBase<objc::DenseMap<DisguisedPtr<objc_object>, unsigned long, true, objc::DenseMapInfo<DisguisedPtr<objc_object> > >, DisguisedPtr<objc_object>, unsigned long, objc::DenseMapInfo<DisguisedPtr<objc_object> >, true>::LookupBucketFor<DisguisedPtr<objc_object> >(DisguisedPtr<objc_object> const&, std::__1::pair<DisguisedPtr<objc_object>, unsigned long> const*&) const
      1,028  classref** getDataSection<classref*>(mach_header_64 const*, char const*, unsigned long*, unsigned long*)
      1,010  unmeta
        988  add_autobin
        986  ImageLoaderMachO::doRebase(ImageLoader::LinkContext const&)
        985  _fetchInitializingClassList(signed char)
        968  dyld::my_open(char const*, int, int)
        945  ImageLoaderMachO::doImageInit(ImageLoader::LinkContext const&)
        936  strtol_l
        927  dyld::registerAddCallback(void (*)(mach_header const*, long))
        925  log_and_fill_cache(objc_class*, void (*)(), objc_selector*, objc_object*, objc_class*)
        910  ImageLoader::hasHiddenExports() const
        905  ImageLoaderMachO::segmentCount() const
        896  std::__1::locale::__imp::install(std::__1::locale::facet*, long)
        882  object_dispose
        880  free_large
        876  NXPtrHash
        840  std::__1::vector<std::__1::locale::facet*, std::__1::__sso_allocator<std::__1::locale::facet*, 28ul> >::__append(unsigned long)
        831  dyld::checkEnvironmentVariables(char const**)
        810  getsectbynamefromheader_64
        804  ImageLoader::recursiveLoadLibraries(ImageLoader::LinkContext const&, bool, ImageLoader::RPathChain const&)
        803  stringsubst
        800  _os_lock_handoff_lock
        799  _class_getNonMetaClass
        785  arr_init
        783  ___xpc_activity_setup_control_channel_block_invoke
        773  _pthread_fork_child_postinit
        752  std::__1::__vector_base<char const*, std::__1::allocator<char const*> >::~__vector_base()
        751  loadlocale
        748  ImageLoaderMachO::segHasPreferredLoadAddress(unsigned int) const
        740  OSAtomicCompareAndSwap32$VARIANT$mp
        735  rwlock_tt<false>::unlockRead()
        735  sel_registerNameNoLock
        729  strdup
        714  getenv
        709  gettokstr'2
        704  dlopen
        704  vm_alloc
        704  category_t** getDataSection<category_t*>(mach_header_64 const*, char const*, unsigned long*, unsigned long*)
        697  _objc_fetch_pthread_data
        693  _xpc_dictionary_serialize_apply
        691  __vfprintf
        686  objc_addRegisteredClass
        685  protocols()
        683  _st_localsub
        672  large_entry_free_no_lock
        666  std::__1::locale::id::__get()
        660  dyld::my_stat(char const*, stat*)
        660  ImageLoaderMachO::segmentsCanSlide() const
        658  void std::__1::vector<dyld_uuid_info, std::__1::allocator<dyld_uuid_info> >::__push_back_slow_path<dyld_uuid_info const>(dyld_uuid_info const&)
        650  __error
        639  crashlog_header_name
        637  _os_object_dealloc
        634  _xpc_dictionary_dispose
        630  inittyptab
        630  _xpc_dictionary_apply_wire_f
        612  getVersionLoadCommandInfo(mach_header const*, unsigned int*, unsigned int*, unsigned int*)
        609  xpc_dictionary_set_value
        608  _xpc_dyld_image_callback
        608  execcmd_exec
        603  stringsubst'2
        597  _finishInitializing(objc_class*, objc_class*)
        595  _voucher_activity_heap_init
        590  spin_lock
        576  __sinit
        570  setupvals
        569  ImageLoader::setPath(char const*)
        564  vm_copy
        560  _xpc_malloc
        549  _xpc_collect_environment
        540  ispwd
        537  configuration_profile_copy_property_list'2
        536  ___extract_user_dict_block_invoke
        534  cache_t::isConstantEmptyCache()
        528  operator new(unsigned long)
        528  -[OS_xpc_object _xref_dispose]'2
        525  _xpc_serializer_apply'2
        520  tlv_load_notification
        511  voucher_mach_msg_set
        508  _xpc_serializer_pack
        500  client_registration_create
        500  arc4random
        496  __sfp
        491  add_autocond
        480  small_free_list_remove_ptr
        478  _xpc_dictionary_node_free
        474  _malloc_initialize
        473  _xpc_serializer_unpack
        469  setlocale
        467  notify_register_check
        466  dyld::clearAllDepths()
        454  _os_object_release
        450  dyld::updateAllImages(dyld_image_states, unsigned int, dyld_image_info const*)
        441  _xpc_serializer_dictionary_apply
        436  assignnparam
        435  __fcntl
        430  pread
        429  _xpc_dispose'2
        428  add_dep
        416  set_widearray
        416  std::__1::vector<char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*), std::__1::allocator<char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*)> >::insert(std::__1::__wrap_iter<char const* (* const*)(dyld_image_states, unsigned int, dyld_image_info const*)>, char const* (* const&)(dyld_image_states, unsigned int, dyld_image_info const*))
        407  NXHashGet
        405  ImageLoader::setMapped(ImageLoader::LinkContext const&)
        404  protocol_t** getDataSection<protocol_t*>(mach_header_64 const*, char const*, unsigned long*, unsigned long*)
        404  hbegin
        402  _xpc_dictionary_look_up_table

[-- Attachment #3: annotate_source.txt --]
[-- Type: text/plain, Size: 26501 bytes --]

--------------------------------------------------------------------------------
Profile data file 'callgrind.out.36403' (creator: callgrind-3.13.0)
--------------------------------------------------------------------------------
I1 cache:
D1 cache:
LL cache:
Timerange: Basic block 0 - 1157924742
Trigger: Program termination
Profiled target:  zsh-5.4.2-dev-0 -f ./script1_srcopt.zsh (PID 36403, part 1)
Events recorded:  Ir
Events shown:     Ir
Event sort order: Ir
Thresholds:       100
Include dirs:
User annotated:
Auto-annotation:  off

--------------------------------------------------------------------------------
           Ir
--------------------------------------------------------------------------------
6,018,711,819  PROGRAM TOTALS

--------------------------------------------------------------------------------
         Ir  file:function
--------------------------------------------------------------------------------
499,337,372  _pthread_mutex_unlock_slow
395,103,281  ingetc
340,894,646  _pthread_mutex_lock_slow
270,001,086  gettokstr
253,955,336  itype_end
239,400,268  par_cmd
235,501,204  zshlex
212,451,886  szone_free_definite_size
189,088,608  _platform_memmove
184,805,094  zhalloc
170,627,916  szone_size
133,142,800  szone_malloc_should_clear
120,600,000  addvars
117,622,898  strlen
112,500,000  execlist'2
103,499,107  fgetc
 99,001,300  shingetline
 90,017,029  fetchvalue
 00,000,000  readoutput
 84,900,239  exalias
 81,009,495  free
 00,000,000  remnulargs
 77,400,137  ecstrcode
 75,600,063  inungetc
 71,100,076  par_sublist
 70,200,054  ecgetstr
 69,307,579  assignsparam
 67,695,989  tiny_malloc_from_free_list
 64,876,963  hasher
 63,000,610  has_token
 62,416,583  strcpy
 59,816,558  tiny_free_list_add_ptr
 59,400,000  execsimple
 58,506,441  assignstrvalue
 58,200,032  par_event'2
 54,900,025  set_list_code
 54,000,060  par_pline
 50,428,084  malloc_zone_malloc
 46,529,510  zalloc
 37,800,903  dupstring
 36,021,416  malloc
 35,100,094  checkalias
 33,609,177  flockfile
 33,609,177  funlockfile
 32,400,228  add
 31,500,045  setunderscore
 28,816,663  gethashnode2
 27,906,987  getparamnode
 27,600,092  bld_eprog
 25,503,477  watchlog_match
 25,209,450  ztrdup
 23,509,751  _platform_strcmp
 23,417,919  _platform_bzero$VARIANT$Merom
 23,100,203  par_event
 22,506,524  _platform_strchr$VARIANT$Generic
 22,503,600  isident
 22,500,640  __error
 21,600,221  gethashnode
 21,600,171  zshlex_raw_add
 20,700,345  hbegin
 20,400,078  loop'2
 19,801,980  strsetfn
 19,217,115  pthread_mutex_lock
 18,007,685  _os_lock_spin_lock
 17,400,290  freeheap
 16,500,281  init_parse
 16,202,388  strrchr
 16,200,012  zshlex_raw_back
 15,300,761  check_warn_pm
 14,700,000  execode'2
 14,418,432  pthread_mutex_unlock
 14,400,024  input_hasalias
 14,100,136  inputsetline
 13,800,230  hend
 13,500,030  hcalloc
 13,256,505  get_tiny_previous_free_msize
 12,902,575  __vsnprintf_chk
 12,600,036  ugetnode
  9,005,025  szone_malloc
  8,700,087  zrealloc
  8,700,029  __strcpy_chk
  8,400,656  zsfree
  8,400,280  nohw
  8,400,028  nohwe
  7,800,156  signal_mask
  7,203,274  os_lock_unlock
  7,203,274  os_lock_lock
  7,203,074  _os_lock_spin_unlock
  7,200,136  parse_event
  6,900,023  stpcpy
  6,000,040  freeeprog
  5,700,109  zfree
  5,403,333  get_node_from_uniquing_table
  5,100,032  copy_ecstr
  4,800,032  ferror
  4,688,220  ImageLoaderMachOCompressed::trieWalk(unsigned char const*, unsigned char const*, char const*)
  4,200,056  __chk_overlap
  3,900,000  arrlen
  3,600,096  signal_block
  3,600,072  signal_unblock
  3,300,055  lexinit
  3,300,022  useeprog
  3,000,070  sigprocmask
  2,221,567  tiny_free_list_remove_ptr
  1,200,024  intr
  1,046,371  get_tiny_free_size
    748,577  ImageLoaderMachO::findExportedSymbol(char const*, bool, ImageLoader const**) const'2
    612,055  __vsnprintf_chk'2
    458,334  ImageLoaderMachOCompressed::libReExported(unsigned int) const
    343,351  ImageLoaderMachOCompressed::rebase(ImageLoader::LinkContext const&)
    284,003  ImageLoaderMachOCompressed::findExportedSymbol(char const*, ImageLoader const**) const
    184,512  ImageLoaderMachOCompressed::eachBind(ImageLoader::LinkContext const&, unsigned long (ImageLoaderMachOCompressed::*)(ImageLoader::LinkContext const&, unsigned long, unsigned char, char const*, unsigned char, long, long, char const*, ImageLoaderMachOCompressed::LastLookup*, bool))
    173,696  ImageLoaderMachO::findExportedSymbol(char const*, bool, ImageLoader const**) const
    166,828  dyld_stub_binder'2
    166,727  dyld::loadPhase5(char const*, char const*, dyld::LoadContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >*)
    159,994  strcmp
    137,007  ImageLoaderMachOCompressed::doBindFastLazySymbol(unsigned int, ImageLoader::LinkContext const&, void (*)(), void (*)())
    124,502  _Read_RuneMagi
    118,188  ImageLoaderMachOCompressed::exportedSymbolAddress(ImageLoader::LinkContext const&, ImageLoader::Symbol const*, ImageLoader const*, bool) const
     98,479  _platform_strncmp
     95,057  ImageLoaderMachOCompressed::resolve(ImageLoader::LinkContext const&, char const*, unsigned char, long, ImageLoader const**, ImageLoaderMachOCompressed::LastLookup*, bool)
     92,075  ImageLoader::hash(char const*)
     86,952  __fread
     81,702  ImageLoaderMachOCompressed::libImage(unsigned int) const
     71,218  getsectiondata
     61,109  _mapStrHash(_NXMapTable*, void const*)
     57,008  ImageLoaderMachO::bindLocation(ImageLoader::LinkContext const&, unsigned long, unsigned long, ImageLoader const*, unsigned char, char const*, long, char const*)
     55,996  ImageLoader::recursiveLoadLibraries(ImageLoader::LinkContext const&, bool, ImageLoader::RPathChain const&)'2
     55,356  ImageLoader::matchInstallPath() const
     54,359  bcmp
     48,031  ImageLoaderMachOCompressed::bindAt(ImageLoader::LinkContext const&, unsigned long, unsigned char, char const*, unsigned char, long, long, char const*, ImageLoaderMachOCompressed::LastLookup*, bool)
     46,112  ImageLoaderMachOCompressed::resolveTwolevel(ImageLoader::LinkContext const&, ImageLoader const*, bool, char const*, bool, ImageLoader const**)
     42,261  ImageLoaderMachO::parseLoadCmds(ImageLoader::LinkContext const&)
     41,487  dyld_stub_binder
     37,750  __srefill0
     36,490  __findenv
     33,453  fread
     32,826  arc4_stir
     30,304  ImageLoader::recursiveUpdateDepth(unsigned int)'2
     30,112  strncmp
     29,104  pthread_once
     28,222  ImageLoaderMachO::sniffLoadCommands(macho_header const*, char const*, bool, bool*, unsigned int*, unsigned int*, ImageLoader::LinkContext const&, linkedit_data_command const**, encryption_info_command const**)
     27,762  ImageLoader::compare(ImageLoader const*) const
     25,004  addhashnode2
     23,822  _qsort'2
     23,606  dyld::loadPhase3(char const*, char const*, dyld::LoadContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >*)
     23,339  ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&)'2
     23,318  __srefill1
     21,504  ImageLoaderMachOCompressed::segmentCommandOffsets() const
     21,412  ImageLoaderMachO::registerInterposing()
     21,200  ImageLoaderMachO::getExportedSymbolAddress(ImageLoader::Symbol const*, ImageLoader::LinkContext const&, ImageLoader const*, bool) const
     20,928  __srget
     20,672  ImageLoaderMachO::segActualLoadAddress(unsigned int) const
     20,524  _NXMapMember(_NXMapTable*, void const*, void**)
     18,611  ImageLoaderMachO::doGetDependentLibraries(ImageLoader::DependentLibraryInfo*)
     17,812  _sread
     17,562  void std::__1::__insertion_sort<method_t::SortBySELAddress&, entsize_list_tt<method_t, method_list_t, 3u>::iterator>(entsize_list_tt<method_t, method_list_t, 3u>::iterator, entsize_list_tt<method_t, method_list_t, 3u>::iterator, method_t::SortBySELAddress&)
     17,216  dyld::load(char const*, dyld::LoadContext const&)
     16,980  copySwiftV1MangledName(char const*, bool)
     16,226  _mapStrIsEqual(_NXMapTable*, void const*, void const*)
     16,192  dyld::checkandAddImage(ImageLoader*, dyld::LoadContext const&)
     16,133  ImageLoader::recursiveBind(ImageLoader::LinkContext const&, bool, bool)'2
     15,844  pthread_rwlock_unlock
     15,579  dyld::notifyBatchPartial(dyld_image_states, bool, char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*))
     15,548  _readdir_unlocked$INODE64
     15,173  NXMapInsert
     15,059  dyld::loadPhase0(char const*, char const*, dyld::LoadContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >*)
     14,612  ImageLoaderMachO::getInstallPath() const
     14,326  __srefill
     14,313  pthread_mutex_init
     14,196  ImageLoader::interposedAddress(ImageLoader::LinkContext const&, unsigned long, ImageLoader const*, ImageLoader const*)
     14,184  _qsort
     13,720  metafy
     13,498  dyld::loadPhase1(char const*, char const*, dyld::LoadContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >*)
     13,486  ImageLoaderMachO::setupLazyPointerHandler(ImageLoader::LinkContext const&)
     13,431  dyld::loadPhase4(char const*, char const*, dyld::LoadContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >*)
     13,102  ImageLoader::recursiveRebase(ImageLoader::LinkContext const&)'2
     12,970  realizeClass(objc_class*)'2
     12,711  ImageLoader::ImageLoader(char const*, unsigned int)
     12,700  dyld::notifySingle(dyld_image_states, ImageLoader const*)
     12,530  ImageLoader::recursiveGetDOFSections(ImageLoader::LinkContext const&, std::__1::vector<ImageLoader::DOFInfo, std::__1::allocator<ImageLoader::DOFInfo> >&)'2
     12,286  _pthread_rwlock_lock
     11,770  dyld::findLoadedImage(stat const&)
     11,286  ImageLoader::setPathUnowned(char const*)
     11,220  __sel_registerName(char const*, int, int)
     11,206  ImageLoaderMachO::mapSegments(int, unsigned long long, unsigned long long, unsigned long long, ImageLoader::LinkContext const&)
     11,205  dyld::fastBindLazySymbol(ImageLoader**, unsigned long)
     11,179  dyld::findMappedRange(unsigned long)
     11,078  getsegmentdata
     10,782  dyld::imageSorter(void const*, void const*)
     10,758  ImageLoader::statMatch(stat const&) const
     10,410  ImageLoaderMachOCompressed::libIsUpward(unsigned int) const
      9,516  autofeatures
      9,098  ImageLoaderMachO::ImageLoaderMachO(macho_header const*, char const*, unsigned int, unsigned int*, unsigned int)
      8,969  createparamtable
      8,910  malloc_zone_calloc
      8,900  ImageLoaderMachO::sdkVersion() const
      8,692  realizeClass(objc_class*)
      8,680  ImageLoaderMachO::getRPaths(ImageLoader::LinkContext const&, std::__1::vector<char const*, std::__1::allocator<char const*> >&) const
      8,672  tzload
      7,770  __sread
      7,200  dyld::addMappedRange(ImageLoader*, unsigned long, unsigned long)
      7,120  ImageLoaderMachO::segSize(unsigned int) const
      7,108  createparam
      6,953  NXMapInsert'2
      6,930  ImageLoaderMachO::segActualEndAddress(unsigned int) const
      6,910  __collate_load_tables
      6,821  ImageLoader::recursiveSpinLock(ImageLoader::recursive_lock&)
      6,780  remapClass(objc_class*)
      6,709  _dyld_fast_stub_entry(void*, long)
      6,696  malloc_zone_from_ptr
      6,672  fixupMethodList(method_list_t*, bool, bool)
      6,428  mkenvstr
      6,390  ImageLoaderMachOCompressed::incrementCoalIterator(ImageLoader::CoalIterator&)
      6,324  addhashnode
      6,323  _platform_memchr$VARIANT$Generic
      6,304  ImageLoaderMachO::segFileOffset(unsigned int) const
      6,258  ImageLoaderMachO::usesTwoLevelNameSpace() const
      6,192  ImageLoaderMachO::assignSegmentAddresses(ImageLoader::LinkContext const&)
      6,020  NXHashInsert
      6,004  readdir$INODE64
      5,760  ImageLoader::getRealPath() const
      5,740  devname_r
      5,712  dyld::libraryLocator(char const*, bool, char const*, ImageLoader::RPathChain const*)
      5,642  ImageLoaderMachOCompressed::instantiateFromFile(char const*, int, unsigned char const*, unsigned long, unsigned long long, unsigned long long, stat const&, unsigned int, unsigned int, linkedit_data_command const*, encryption_info_command const*, ImageLoader::LinkContext const&)
      5,630  readClass(objc_class*, bool, bool)
      5,590  read$NOCANCEL
      5,586  dyld::addImage(ImageLoader*)
      5,371  scanmatchtable
      5,355  ImageLoaderMachO::doGetLibraryInfo()
      5,349  mmap
      5,044  NXHashInsert'2
      4,998  ImageLoaderMachOCompressed::setLibImage(unsigned int, ImageLoader*, bool, bool)
      4,882  ImageLoaderMachO::needsAddedLibSystemDepency(unsigned int, macho_header const*)
      4,832  ImageLoaderMachO::segFileSize(unsigned int) const
      4,742  addSubclass(objc_class*, objc_class*)
      4,642  calloc
      4,626  ImageLoaderMachO::segWriteable(unsigned int) const
      4,608  NXMapGet
      4,568  ztrdup_metafy
      4,556  ImageLoaderMachO::segUnaccessible(unsigned int) const
      4,314  _read_images
      4,223  _hash_bucket
      4,160  ImageLoaderMachO::segPreferredLoadAddress(unsigned int) const
      4,060  ImageLoaderMachO::machHeader() const
      4,035  OSAtomicCompareAndSwap64$VARIANT$mp
      4,024  __part_load_locale
      4,010  dyld::loadPhase6(int, stat const&, char const*, dyld::LoadContext const&)
      3,822  NXMapRemove
      3,810  szone_calloc
      3,768  dyld::fatFindBest(fat_header const*, unsigned long long*, unsigned long long*)
      3,726  ImageLoaderMachO::preFetchDATA(int, unsigned long long, ImageLoader::LinkContext const&)
      3,605  ImageLoaderMachO::inSharedCache() const
      3,597  ImageLoaderMachOCompressed::markLINKEDIT(ImageLoader::LinkContext const&, int)
      3,575  ImageLoader::lastModified() const
      3,552  search_method_list(method_list_t const*, objc_selector*)
      3,552  _xpc_serializer_append
      3,540  prepareMethodLists(objc_class*, method_list_t**, int, bool, bool)
      3,520  ImageLoaderMachO::instantiateFromFile(char const*, int, unsigned char const*, unsigned long long, unsigned long long, stat const&, ImageLoader::LinkContext const&)
      3,430  ImageLoaderMachO::isDylib() const
      3,360  objc_msgSend'2
      3,314  setemulate
      3,306  NXNextHashState
      3,306  RWLOCK_GETSEQ_ADDR
      3,263  _simple_getenv
      3,245  ImageLoaderMachO::doGetDOFSections(ImageLoader::LinkContext const&, std::__1::vector<ImageLoader::DOFInfo, std::__1::allocator<ImageLoader::DOFInfo> >&)
      3,241  fcntl
      3,233  ImageLoaderMachOCompressed::updateUsesCoalIterator(ImageLoader::CoalIterator&, unsigned long, ImageLoader*, ImageLoader::LinkContext const&)
      3,188  getMethodNoSuper_nolock(objc_class*, objc_selector*)
      3,173  small_malloc_from_free_list
      2,990  memchr
      2,963  ImageLoaderMachO::getUUID(unsigned char*) const
      2,832  _pthread_cond_signal
      2,816  list_array_tt<unsigned long, protocol_list_t>::attachLists(protocol_list_t* const*, unsigned int)
      2,816  class_createInstance
      2,791  slot_name
      2,737  zshcalloc
      2,726  allocBuckets(void*, unsigned int)
      2,718  memcpy
      2,640  lookUpImpOrForward
      2,589  colonsplit
      2,546  ImageLoader::weakBind(ImageLoader::LinkContext const&)
      2,430  ImageLoaderMachO::usablePrebinding(ImageLoader::LinkContext const&) const
      2,419  _nc_table_insert_type
      2,366  _xpc_dictionary_insert
      2,360  _nc_table_find_get_key
      2,352  objc_destructInstance
      2,340  ImageLoaderMachO::segExecutable(unsigned int) const
      2,290  ImageLoaderMachO::doModInitFunctions(ImageLoader::LinkContext const&)
      2,289  zinsertlinknode
      2,288  objc_class::setRequiresRawIsa(bool)
      2,230  freeBuckets(NXHashTable*, int)
      2,214  cache_fill
      2,210  ImageLoaderMachO::segReadable(unsigned int) const
      2,208  ImageLoaderMachO::segName(unsigned int) const
      2,198  ImageLoaderMachO::validateFirstPages(linkedit_data_command const*, int, unsigned char const*, unsigned long, long long, ImageLoader::LinkContext const&)
      2,184  map_images_nolock
      2,156  objc_object::sidetable_clearDeallocating()
      2,150  dyld::findInSharedCacheImage(char const*, bool, stat const*, macho_header const**, char const**, long*)
      2,118  addImagesToAllImages(unsigned int, dyld_image_info const*)
      2,070  ImageLoaderMachOCompressed::doBind(ImageLoader::LinkContext const&, bool)
      1,984  ImageLoaderMachOCompressed::findExportedSymbol(char const*, ImageLoader const**) const'2
      1,940  objc_class::setHasCustomAWZ(bool)
      1,932  rwlock_tt<false>::write()
      1,929  __si_module_static_cache_block_invoke
      1,920  list_array_tt<method_t, method_list_t>::attachLists(method_list_t* const*, unsigned int)
      1,914  ImageLoaderMachO::loadCodeSignature(linkedit_data_command const*, int, unsigned long long, ImageLoader::LinkContext const&)
      1,898  getPreoptimizedClass
      1,889  env_unsetenv
      1,875  objc_image_info* getDataSection<objc_image_info>(mach_header_64 const*, char const*, unsigned long*, unsigned long*)
      1,872  inittyptab
      1,866  strnlen
      1,862  attachCategories(objc_class*, locstamped_category_list_t*, bool)
      1,845  dyld::isCompatibleMachO(unsigned char const*, char const*)
      1,702  mach_absolute_time
      1,680  add_autoparam
      1,670  bcopy
      1,656  _xpc_has_prefix
      1,649  objc_class::setHasCustomRR(bool)
      1,616  _class_initialize'2
      1,615  ImageLoaderMachOCompressed::isSubframeworkOf(ImageLoader::LinkContext const&, ImageLoader const*) const
      1,615  ImageLoaderMachOCompressed::hasSubLibrary(ImageLoader::LinkContext const&, ImageLoader const*) const
      1,608  dyld::registerImageStateSingleChangeHandler(dyld_image_states, char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*))
      1,606  addNamedClass(objc_class*, char const*, objc_class*)
      1,600  _os_object_alloc_realized
      1,596  _NXMapRehash(_NXMapTable*)
      1,580  lstat$INODE64
      1,572  cache_getImp
      1,567  _voucher_init
      1,548  _xpc_serializer_read
      1,533  getClass(char const*)
      1,529  _object_remove_assocations
      1,503  objc_opt::objc_clsopt_t::getClassesAndHeaders(char const*, void**, void**) const'2
      1,490  ImageLoader::recursiveInitialization(ImageLoader::LinkContext const&, unsigned int, ImageLoader::InitializerTimingList&, ImageLoader::UninitedUpwards&)
      1,472  _pthread_mutex_check_init_slow
      1,446  void std::__1::__stable_sort<method_t::SortBySELAddress&, entsize_list_tt<method_t, method_list_t, 3u>::iterator>(entsize_list_tt<method_t, method_list_t, 3u>::iterator, entsize_list_tt<method_t, method_list_t, 3u>::iterator, method_t::SortBySELAddress&, std::__1::iterator_traits<entsize_list_tt<method_t, method_list_t, 3u>::iterator>::difference_type, std::__1::iterator_traits<entsize_list_tt<method_t, method_list_t, 3u>::iterator>::value_type*, long)
      1,438  emptyhashtable
      1,435  addenv
      1,432  ImageLoaderMachO::isPrebindable() const
      1,419  lookUpImpOrForward'2
      1,408  list_array_tt<property_t, property_list_t>::attachLists(property_list_t* const*, unsigned int)
      1,407  dyld::_main(macho_header const*, unsigned long, int, char const**, char const**, char const**, unsigned long*)
      1,393  createoptiontable
      1,376  xmmap
      1,346  environ_init
      1,342  LI_ils_create
      1,320  ImageLoaderMachO::crashIfInvalidCodeSignature()
      1,308  _dyld_func_lookup
      1,287  unattachedCategoriesForClass(objc_class*, bool)
      1,276  ___ZN10objc_class17setRequiresRawIsaEb_block_invoke
      1,274  objc::DenseMapBase<objc::DenseMap<DisguisedPtr<objc_object>, unsigned long, true, objc::DenseMapInfo<DisguisedPtr<objc_object> > >, DisguisedPtr<objc_object>, unsigned long, objc::DenseMapInfo<DisguisedPtr<objc_object> >, true>::find(DisguisedPtr<objc_object> const&)
      1,272  dyld::findExportedSymbol(char const*, bool, ImageLoader::Symbol const**, ImageLoader const**)
      1,262  __bzero
      1,260  rwlock_tt<false>::unlockWrite()
      1,260  mach_vm_allocate
      1,260  ImageLoaderMachO::participatesInCoalescing() const
      1,257  addbuiltins
      1,256  set_widearray
      1,251  _NXHashRehashToCapacity
      1,246  addNonSharedCacheImageUUID(dyld_uuid_info const&)
      1,219  dyld::parseColonList(char const*, char const*)
      1,215  ImageLoaderMachO::doInitialization(ImageLoader::LinkContext const&)
      1,212  strstr
      1,203  zjoin
      1,165  uname
      1,138  _platform_memcmp
      1,137  _xpc_dictionary_apply_node_f
      1,127  rwlock_tt<false>::read()
      1,114  unmeta
      1,097  objc_class::setInitialized()
      1,095  getClass_impl(char const*)
      1,092  _class_initialize
      1,090  getProtocol(char const*)
      1,064  std::__1::__call_once(unsigned long volatile&, void*, void (*)(void*))
      1,056  ImageLoaderMachO::reserveAnAddressRange(unsigned long, ImageLoader::LinkContext const&)
      1,031  objc_opt::objc_clsopt_t::getClassesAndHeaders(char const*, void**, void**) const
      1,029  bool objc::DenseMapBase<objc::DenseMap<DisguisedPtr<objc_object>, unsigned long, true, objc::DenseMapInfo<DisguisedPtr<objc_object> > >, DisguisedPtr<objc_object>, unsigned long, objc::DenseMapInfo<DisguisedPtr<objc_object> >, true>::LookupBucketFor<DisguisedPtr<objc_object> >(DisguisedPtr<objc_object> const&, std::__1::pair<DisguisedPtr<objc_object>, unsigned long> const*&) const
      1,028  classref** getDataSection<classref*>(mach_header_64 const*, char const*, unsigned long*, unsigned long*)
        988  add_autobin
        986  ImageLoaderMachO::doRebase(ImageLoader::LinkContext const&)
        985  _fetchInitializingClassList(signed char)
        968  dyld::my_open(char const*, int, int)
        945  ImageLoaderMachO::doImageInit(ImageLoader::LinkContext const&)
        936  strtol_l
        927  dyld::registerAddCallback(void (*)(mach_header const*, long))
        925  log_and_fill_cache(objc_class*, void (*)(), objc_selector*, objc_object*, objc_class*)
        910  ImageLoader::hasHiddenExports() const
        905  ImageLoaderMachO::segmentCount() const
        896  std::__1::locale::__imp::install(std::__1::locale::facet*, long)
        891  allocate_pages
        882  object_dispose
        882  munmap
        876  NXPtrHash
        870  __mmap
        861  mach_msg
        840  std::__1::vector<std::__1::locale::facet*, std::__1::__sso_allocator<std::__1::locale::facet*, 28ul> >::__append(unsigned long)
        831  dyld::checkEnvironmentVariables(char const**)
        810  getsectbynamefromheader_64
        804  ImageLoader::recursiveLoadLibraries(ImageLoader::LinkContext const&, bool, ImageLoader::RPathChain const&)
        800  _os_lock_handoff_lock
        799  _class_getNonMetaClass
        785  arr_init
        783  ___xpc_activity_setup_control_channel_block_invoke
        773  _pthread_fork_child_postinit
        752  std::__1::__vector_base<char const*, std::__1::allocator<char const*> >::~__vector_base()
        751  loadlocale
        748  ImageLoaderMachO::segHasPreferredLoadAddress(unsigned int) const
        740  OSAtomicCompareAndSwap32$VARIANT$mp
        735  rwlock_tt<false>::unlockRead()
        735  sel_registerNameNoLock
        729  strdup
        714  getenv
        704  vm_alloc
        704  category_t** getDataSection<category_t*>(mach_header_64 const*, char const*, unsigned long*, unsigned long*)
        704  dlopen
        697  _objc_fetch_pthread_data
        693  _xpc_dictionary_serialize_apply
        691  __vfprintf
        686  objc_addRegisteredClass
        685  protocols()
        683  _st_localsub
        672  mach_vm_map
        666  std::__1::locale::id::__get()
        660  ImageLoaderMachO::segmentsCanSlide() const
        660  dyld::my_stat(char const*, stat*)
        660  vm_allocate
        658  void std::__1::vector<dyld_uuid_info, std::__1::allocator<dyld_uuid_info> >::__push_back_slow_path<dyld_uuid_info const>(dyld_uuid_info const&)
        639  crashlog_header_name
        637  _os_object_dealloc
        634  _xpc_dictionary_dispose
        630  _xpc_dictionary_apply_wire_f
        612  getVersionLoadCommandInfo(mach_header const*, unsigned int*, unsigned int*, unsigned int*)
        609  xpc_dictionary_set_value
        608  _xpc_dyld_image_callback
        597  _finishInitializing(objc_class*, objc_class*)
        595  _voucher_activity_heap_init
        590  spin_lock
        587  execcmd_exec
        576  __sinit
        570  setupvals
        569  ImageLoader::setPath(char const*)
        560  _xpc_malloc
        549  _xpc_collect_environment
        540  ispwd
        537  configuration_profile_copy_property_list'2
        536  ___extract_user_dict_block_invoke
        534  cache_t::isConstantEmptyCache()
        528  operator new(unsigned long)
        528  -[OS_xpc_object _xref_dispose]'2
        525  _xpc_serializer_apply'2
        520  tlv_load_notification
        511  voucher_mach_msg_set
        508  _xpc_serializer_pack
        500  arc4random
        500  client_registration_create
        496  __sfp
        491  add_autocond
        478  _xpc_dictionary_node_free
        474  _malloc_initialize
        473  _xpc_serializer_unpack
        469  setlocale
        467  notify_register_check
        466  dyld::clearAllDepths()
        454  _os_object_release
        450  dyld::updateAllImages(dyld_image_states, unsigned int, dyld_image_info const*)
        441  _xpc_serializer_dictionary_apply
        440  __fcntl
        436  assignnparam
        430  pread
        429  _xpc_dispose'2
        428  add_dep
        416  std::__1::vector<char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*), std::__1::allocator<char const* (*)(dyld_image_states, unsigned int, dyld_image_info const*)> >::insert(std::__1::__wrap_iter<char const* (* const*)(dyld_image_states, unsigned int, dyld_image_info const*)>, char const* (* const&)(dyld_image_states, unsigned int, dyld_image_info const*))
        407  NXHashGet
        405  ImageLoader::setMapped(ImageLoader::LinkContext const&)
        404  protocol_t** getDataSection<protocol_t*>(mach_header_64 const*, char const*, unsigned long*, unsigned long*)
        402  _xpc_dictionary_look_up_table

^ permalink raw reply	[relevance 1%]

* [PATH] _tar: a few updates for long options
@ 2017-12-26 10:29  5% Jun T
  0 siblings, 0 replies; 200+ results
From: Jun T @ 2017-12-26 10:29 UTC (permalink / raw)
  To: zsh-workers

I've fond _tar needs some update for long options.
This is not related with the locale-dependent help output.


diff --git a/Completion/Unix/Command/_tar b/Completion/Unix/Command/_tar
index 4a2404873..ecf02fd83 100644
--- a/Completion/Unix/Command/_tar
+++ b/Completion/Unix/Command/_tar
@@ -99,10 +99,16 @@ if [[ "$PREFIX" = --* ]]; then
 
   # ...long options after `--'.
 
-  _arguments -- '--owner*:user:_users' \
+  _arguments --  '--owner=*:user:_users' \
+		 '--group=*:group:_groups' \
+		 '--atime-preserve*::method:(replace system)' \
+		 '--*-script=NAME:script file:_files' \
+		 '--format=*:format:(gnu oldgnu pax posix ustar v7)' \
+		 '--quoting-style=*:quoting style:(literal shell shell-always c c-maybe escape locale clocale)' \
+		 '--totals*=SIGNAL*::signal:(HUP QUIT INT USR1 USR2)' \
                  '*=(PROG|COMMAND)*:program:_command_names -e' \
 		 '*=ARCHIVE*:archive: _tar_archive' \
-		 '*=NAME*:file:_files' \
+		 '*=FILE*:file:_files' \
 		 '*=DIR*:directory:_files -/' \
 		 '*=CONTROL*::version control:(t numbered nil existing never simple)'
 



^ permalink raw reply	[relevance 5%]

* Re: [BUG] getopts OPTIND
  @ 2018-01-09 23:58  3%     ` dana
  2018-01-10  1:32  2%       ` Francisco de Zuviría Allende
  0 siblings, 1 reply; 200+ results
From: dana @ 2018-01-09 23:58 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: franciscodezuviria, zsh-workers

On 9 Jan 2018, at 16:57, Bart Schaefer <schaefer@brasslantern.com> wrote:
>There's a whole thread from 2015 related to this, beginning with workers/35317

Oops, sure enough. Sorry.

Well, i'm not sure about POSIX correctness, but for whatever it's worth, i can
confirm that all of the following are in agreement as to how it should behave:

ash (BusyBox)
bash
dash
ksh93
mksh
posh

The ksh that comes with OpenBSD (derived from pdksh i think) seems to stop
updating OPTIND after it encounters an invalid option until it finds either a
legal one or the end of the argument list; after that it's set back to the
correct value.

yash just seems broken somehow, it gives me nonsense.

dana


^ permalink raw reply	[relevance 3%]

* Re: [BUG] getopts OPTIND
  2018-01-09 23:58  3%     ` dana
@ 2018-01-10  1:32  2%       ` Francisco de Zuviría Allende
  0 siblings, 0 replies; 200+ results
From: Francisco de Zuviría Allende @ 2018-01-10  1:32 UTC (permalink / raw)
  To: dana; +Cc: Bart Schaefer, zsh-workers

Such quick, much wow!

On Jan 9, 2018 8:58 PM, "dana" <dana@dana.is> wrote:
>
> On 9 Jan 2018, at 16:57, Bart Schaefer <schaefer@brasslantern.com> wrote:
> >There's a whole thread from 2015 related to this, beginning with workers/35317
>

OK, I just read workers/35317
and also the previous thread http://www.zsh.org/mla/workers//2015/msg00196.html
and also the previous from 2007 http://www.zsh.org/mla/users/2007/msg01191.html

2 key responses from Peter Stephenson:

2007:

>     This is the way you'd normally use it:  you don't know how many options
>    there are, if any, until getopts returns 1.
>
>     However, it's still a bit fishy.  The internal option index handling logic
>     has always been a bit curious; I've lost count of the number of bugs we've
>     had in it.

And also

On Thu, 28 May 2015 18:17:40 +0100
Peter Stephenson <p.stephenson@xxxxxxxxxxx> wrote:

> This is documented behaviour (well, sort of -- the documentation didn't
> say quite what we actually do) so this needs another compatibility fix.
> POSIX_BUILTINS seems appropriate.

(and then submits a cool patch)

I checked man zshbuiltins

       getopts optstring name [ arg ... ]
(...)
              The first option to be examined may be changed by
explicitly assigning to OPTIND.  OPTIND has an initial  value  of
             1,  and  is  normally  set  to  1  upon  entry  to a
shell function and restored upon exit (this is disabled by the
             POSIX_BUILTINS option).

and man zshoptions

       POSIX_BUILTINS <K> <S>
(...)
              Furthermore, the getopts builtin behaves in a
POSIX-compatible fashion in that the associated  variable  OPTIND  is
             not made local to functions.


Now here things get funky. If I try this:

(
    testfunc() {
        echo POSIX_BUILTINS is $options[posixbuiltins]
        echo $*;
        for i in 1 2 3 4; do
            echo "OPTIND is $OPTIND, `(shift "$(($OPTIND - 1))"; echo
next $1)`";
            echo 'I do getopts :a: varname'; getopts ':a:' varname;
        done
    }

    (
        setopt POSIX_BUILTINS
        testfunc -a -w -e -r
    )
    (
        unsetopt POSIX_BUILTINS
        testfunc -a -w -e -r
    )
)

I get:

POSIX_BUILTINS is on
-a -w -e -r
OPTIND is 1, next -a
I do getopts :a: varname
OPTIND is 3, next -e
I do getopts :a: varname
OPTIND is 3, next -e
I do getopts :a: varname
OPTIND is 4, next -r
I do getopts :a: varname
POSIX_BUILTINS is off
-a -w -e -r
OPTIND is 1, next -a
I do getopts :a: varname
OPTIND is 3, next -e
I do getopts :a: varname
OPTIND is 3, next -e
I do getopts :a: varname
OPTIND is 4, next -r
I do getopts :a: varname

hmmmmmmm... I's it a bug?

> Well, i'm not sure about POSIX correctness, but for whatever it's worth, i can
> confirm that all of the following are in agreement as to how it should behave:
>
> ash (BusyBox)
> bash
> dash
> ksh93
> mksh
> posh
>

I think there are 2 things to discuss, one is this (possible) bug.
The other is if POSIX_BUILTINS should be on by default or not.
If off by default, New users will get bugs in their code if they
migrate from bash
If on by default, downstream projects will get bugs in their working code

This options does other things as well:

              When this option is set the command builtin can be used
to execute shell builtin commands.   Parameter  assignments
             specified  before  shell  functions  and  special
builtins are kept after the command completes unless the special
             builtin is prefixed with the command builtin.  Special
builtins are ., :, break,  continue,  declare,  eval,  exit,
             export, integer, local, readonly, return, set, shift,
source, times, trap and unset.

             In  addition,  various error conditions associated with
the above builtins or exec cause a non-interactive shell to
             exit and an interactive shell to return to its top-level
processing.

So this is a broather subject and may be better discussed in a new
thread with a suitable title


^ permalink raw reply	[relevance 2%]

* PATCH: update options in assorted completions
@ 2018-01-22 13:57  2% Oliver Kiddle
  0 siblings, 0 replies; 200+ results
From: Oliver Kiddle @ 2018-01-22 13:57 UTC (permalink / raw)
  To: Zsh workers

A number of completions are quite outdated. Examples include: ip,
btrfs, iptables, links, lynx, samba, many macOS completions, many
Debian completions, rpm, dnf, image magick, growisofs, django, rrdtool.

It's always best if updates are made by someone who actually uses a
command/OS so if, unlike me, you use any of these, please consider
helping to bring them up-to-date.

Below are option updates for recently updated commands based on
the usual help output diffs. Relevant versions are:
cryptsetup 2.0.0
ethtool    4.13
tune2fs    1.43.8
cowsay     3.03
git        2.16
gsettings  2.55.1
gzip       1.9 / pigz 2.4
ssh        7.6
sudo       1.8.22b1
tidy       5.6.0
tmux       2.6
truss      freebsd 10.4/11.1
wget       1.19.2

Some of the git options are older – git rev-list -h is out-of-date so I
had to look through the full manpage. ssh and tmux have removed some old
options/commands so this may not be ideal if you're still running old
versions of them.

Oliver

diff --git a/Completion/Linux/Command/_cryptsetup b/Completion/Linux/Command/_cryptsetup
index 00b0f2f95..ea7152ea1 100644
--- a/Completion/Linux/Command/_cryptsetup
+++ b/Completion/Linux/Command/_cryptsetup
@@ -25,6 +25,7 @@ _arguments -s \
   '(-i --iter-time)'{-i+,--iter-time=}'[set password processing duration]:duration (milliseconds)' \
   '(-q --batch-mode)'{-q,--batch-mode}"[don't ask for confirmation]" \
   '(-t --timeout)'{-t+,--timeout=}'[set password prompt timeout]:timeout (seconds)' \
+  '--progress-frequency=[specify progress line update interval]:interval (seconds)' \
   '(-T --tries)'{-T+,--tries=}'[set maximum number of retries]:number of retries' \
   '--align-payload=[set payload alignment]:sectors' \
   '--header-backup-file=[specify file with LUKS header and keyslots backup]:file:_files' \
@@ -39,10 +40,30 @@ _arguments -s \
   '--tcrypt-system[device is system TCRYPT drive (with bootloader)]' \
   '--tcrypt-backup[use backup (secondary) TCRYPT header]' \
   '--veracrypt[scan also for VeraCrypt compatible device]' \
+  '--veracrypt-pim=[specify personal iteration multiplier for VeraCrypt compatible device]:multiplier' \
+  '--veracrypt-query-pim[query personal iteration multiplier for VeraCrypt compatible device]' \
   '(-M --type)'{-M+,--type=}'[specify type of device metadata]:type:(luks plain loopaes tcrypt)' \
   '--force-password[disable password quality check (if enabled)]' \
   '--perf-same_cpu_crypt[use dm-crypt same_cpu_crypt performance compatibility option]' \
   '--perf-submit_from_crypt_cpus[use dm-crypt submit_from_crypt_cpus performance compatibility option]' \
+  '--deferred[device removal is deferred until the last user closes it]' \
+  '--pbkdf=[specify PBKDF algorithm for LUKS2]:algorithm:(argon2i argon2id pbkdf2)' \
+  '--pbkdf-memory=[specify PBKDF memory cost limit]:limit (kilobytes)' \
+  '--pbkdf-parallel=[specify PBKDF parallel cost]:threads' \
+  '--pbkdf-force-iterations=[specify PBKDF iterations cost]:cost' \
+  '--priority=[specify keyslot priority]:priority:(ignore normal prefer)' \
+  '--disable-locks[disable locking of on-disk metadata]' \
+  '--disable-keyring[disable loading volume keys via kernel keyring]' \
+  '(-I --integrity)'{-I+,--integrity=}'[specify data integrity algorithm (LUKS2 only)]:algorithm' \
+  '--integrity-no-journal[disable journal for integrity device]' \
+  "--integrity-no-wipe[don't wipe device after format]" \
+  "--token-only[don't ask for passphrase if activation by token fails]" \
+  '--token-id=[specify token number]:number [any]' \
+  '--key-description=[specify key description]:description' \
+  '--sector-size=[specify encryption sector size]:size [512 bytes]' \
+  '--persistent[set activation flags persistent for device]' \
+  '--label=[set label for the LUKS2 device]:label' \
+  '--subsystem=[set subsystem label for the LUKS2 device]:subsystem' \
   '(- : *)--version[show version information]' \
   '(- : *)'{-\?,--help}'[display help information]' \
   '(- : *)--usage[display brief usage]' \
@@ -59,6 +80,8 @@ case $state in
       'benchmark:benchmark cipher'
       'repair:try to repair on-disk metadata'
       'erase:erase all keyslots'
+      'convert:convert LUKS from/to LUKS2 format'
+      'config:set permanent configuration options for LUKS2'
       'luksFormat:initialize a LUKS partition'
       'luksAddKey:add a new key'
       'luksRemoveKey:remove a key'
@@ -86,7 +109,7 @@ case $state in
       benchmark) args=( '--cipher=:cipher' );;
       luksKillSlot) args=( $device ':key slot number' );;
       remove|status|resize|*lose|luksSuspend|luksResume) args=( $mapping );;
-      erase|repair|(luks(AddKey|Erase|RemoveKey|DelKey|UUID|Dump)|isLuks))
+      erase|convert|config|repair|(luks(AddKey|Erase|RemoveKey|DelKey|UUID|Dump)|isLuks))
 	args=( $device )
       ;;
       luks(Format|AddKey|RemoveKey|ChangeKey))
diff --git a/Completion/Linux/Command/_ethtool b/Completion/Linux/Command/_ethtool
index 612841c1f..7c62fd7f7 100644
--- a/Completion/Linux/Command/_ethtool
+++ b/Completion/Linux/Command/_ethtool
@@ -64,7 +64,7 @@ if [[ -n $state ]]; then
   rx-usecs|rx-frames|rx-usecs-irq|rx-frames-irq|tx-usecs|tx-frames) ;&
   tx-usecs-irq|tx-frames-irq|stats-block-usecs|pkt-rate-low|rx-usecs-low) ;&
   rx-frames-low|tx-usecs-low|tx-frames-low|pkt-rate-high|rx-usecs-high) ;&
-  rx-frames-high|tx-usecs-high|tx-frames-high|sample-interval|rx-mini) ;&
+  rx-frames-high|tx-usecs-high|tx-frames-high|sample-interval|dmac|rx-mini) ;&
   rx-jumbo|offset|length|magic|value|phyad|proto|tos|tclass|l4proto|src-port) ;&
   dst-port|spi|l4data|vlan-etype|vlan|user-def|action|vf|queue|loc|delete) ;&
   other|combined|tx-timer|count)
@@ -143,7 +143,7 @@ if [[ -n $state ]]; then
     -C|--coalesce)
       _wanted settings expl 'coalescing setting' compadd -F line -M 'r:|-=* r:|=*' - \
         adaptive-{r,t}x {r,t}x-{usecs,frames}{,-irq,-high,-low} \
-        stats-block-usecs pkt-rate-{low,high} sample-interval
+        stats-block-usecs pkt-rate-{low,high} sample-interval dmac
     ;;
     -G|--set-ring)
       _values -S ' ' -w 'ring parameter' \
diff --git a/Completion/Linux/Command/_tune2fs b/Completion/Linux/Command/_tune2fs
index 1fc1709e4..af120e279 100644
--- a/Completion/Linux/Command/_tune2fs
+++ b/Completion/Linux/Command/_tune2fs
@@ -25,7 +25,6 @@ _arguments -s -S \
   '-M+[set the last-mounted directory for the filesystem]:directory:_files -/' \
   '-o+[mount options]:options:_values -s , debug bsdgroups user_xattr acl uid16 journal_data journal_data_ordered journal_data_writeback nobarrier block_validity discard nodelalloc' \
   '-O+[set or clear filesystem features]: :_values -s , feature dir_index dir_nlink encrypt extentextent extra_isize filetype flex_bg has_journalhuge_file large_file metadata_csum mmp project quota read-only resize_inode sparse_super uninit_bg' \
-  '-p+[set MMP check interval]:interval (seconds) [5]' \
   '-r+[set the number of reserved filesystem blocks]:number' \
   '-Q+[set quota feature on the superblock]: :_values -s , "quota option" usrquota grpquota prjquota' \
   '-s+[set sparse super feature]:state:((0\:off 1\:on))' \
diff --git a/Completion/Unix/Command/_cowsay b/Completion/Unix/Command/_cowsay
index 19e73811c..8a1b4c4c9 100644
--- a/Completion/Unix/Command/_cowsay
+++ b/Completion/Unix/Command/_cowsay
@@ -3,26 +3,29 @@
 local context state line
 typeset -A opt_args
 
-_arguments \
-  '-e:eye string:' \
-  '-f:cowfile:->cowfile' \
-  '-T:tongue string:' \
-  '-W:wrap column:' \
-  '-b[borg mode]' \
-  '-d[dead mode]' \
-  '-g[greedy mode]' \
-  '-h[help]' \
-  '-l[list]' \
-  '-n[no wordwrap]' \
-  '-p[paranoia mode]' \
-  '-s[stoned mode]' \
-  '-t[tired mode]' \
-  '-w[wired mode]' \
-  '-y[youthful mode]' \
-  ':message:' && return 0
+_arguments -s -S -A "-*" \
+  "(H mode)-e+[specify cow's eyes]:eye string [oo]" \
+  '(H)-f+[specify cowfile]:cowfile:->cowfile' \
+  "(H mode)-T+[specify cow's tongue]:tongue string" \
+  '(H)-W+[specify width for message word wrap]:wrap column [40]' \
+  '(H)-n[no wordwrap]' \
+  '*:message' \
+  + 'H' \
+  '(- *)-h[display usage information]' \
+  '(- *)-l[list all cowfiles]' \
+  + '(mode)' \
+  '(H -e -T)-b[borg mode]' \
+  '(H -e -T)-d[dead mode]' \
+  '(H -e -T)-g[greedy mode]' \
+  '(H -e -T)-p[paranoia mode]' \
+  '(H -e -T)-s[stoned mode]' \
+  '(H -e -T)-t[tired mode]' \
+  '(H -e -T)-w[wired mode]' \
+  '(H -e -T)-y[youthful mode]' && return
 
 case "$state" in
   (cowfile)
-  compadd ${=${(f)"$($service -l 2>/dev/null)"}:#*\:} || _files
+    _wanted -C option-f-1 cowfiles expl 'cow file' compadd \
+      ${=${(f)"$(_call_program cowfiles $words[1] -l)"}:#*\:} || _files -g "*.cow(-.)"
   ;;
 esac
diff --git a/Completion/Unix/Command/_git b/Completion/Unix/Command/_git
index a46da5b3c..30ca80835 100644
--- a/Completion/Unix/Command/_git
+++ b/Completion/Unix/Command/_git
@@ -60,9 +60,10 @@ _git-add () {
     '(-i --interactive : -)'{-i,--interactive}'[add contents interactively to index]' \
     '(-p --patch)'{-p,--patch}'[like -i but go directly into patch mode for specified files]' \
     '(-e --edit)'{-e,--edit}'[open diff against index in editor]' \
-    '(-A --all --no-ignore-removal -u --update --no-all --ignore-removal)'{-A,--all,--no-ignore-removal}'[add, modify, and remove index entries to match the working tree]' \
-    '(-A --all --no-ignore-removal -u --update --no-all --ignore-removal)'{--no-all,--ignore-removal}'[like "--all" but ignore removals]' \
+    '(-A --all --no-ignore-removal -u --update --no-all --ignore-removal --renormalize)'{-A,--all,--no-ignore-removal}'[add, modify, and remove index entries to match the working tree]' \
+    '(-A --all --no-ignore-removal -u --update --no-all --ignore-removal --renormalize)'{--no-all,--ignore-removal}'[like "--all" but ignore removals]' \
     '(-A --all --no-ignore-removal -u --update --no-all --ignore-removal)'{-u,--update}'[update the index just where it already has an entry matching <pathspec>]' \
+    '(-A --all --no-ignore-removal -u --update --no-all --ignore-removal)--renormalize[renormalize EOL of tracked files (implies -u)]' \
     '(-N --intent-to-add)'{-N,--intent-to-add}'[record only that path will be added later]' \
     '--refresh[do not add files, but refresh their stat() info in index]' \
     '--ignore-errors[continue adding if an error occurs]' \
@@ -1207,7 +1208,6 @@ _git-merge () {
     '--abort[restore the original branch and abort the merge operation]' \
     '--continue[continue the current in-progress merge]' \
     '--progress[force progress reporting]' \
-    '--signoff[add Signed-off-by:]' \
     '--verify[verify commit-msg hook]' \
     '*: : __git_commits -O expl:git_commit_opts'
 }
@@ -1751,7 +1751,7 @@ _git-status () {
                                                                                      normal\:"show untracked files and directories" \
                                                                                      all\:"also show untracked files in untracked directories (default)"))' \
     '--ignore-submodules[ignore changes to submodules]:: :__git_ignore_submodules_whens' \
-    '--ignored[show ignored files as well]' \
+    '--ignored=-[show ignored files as well]:mode [traditional]:(traditional matching no)' \
     '(-z --null --column --no-column)'{-z,--null}'[use NUL termination on output]' \
     '(--no-column -z --null)--column=-[display in columns]::column.status option:((always\:"always show in columns" never\:"never show in columns" auto\:"show in columns if the output is to the terminal" column\:"fill columns before rows (default)" row\:"fill rows before columns" plain\:"show in one column" dense\:"make unequal size columns to utilize more space" nodense\:"make equal size columns"))' \
     '(--column)--no-column[do not display in columns]' \
@@ -2126,10 +2126,11 @@ _git-config () {
     '(--global --system         -f --file --blob)--local[use local config file]' \
     '(--global --system --local           --blob)'{-f+,--file=}'[use given config file]:config file:_files' \
     '(--global --system --local -f --file)--blob=[read config from given blob object]:blob:__git_blobs' \
-    '(       --int --bool-or-int --path)--bool[setting is a boolean]' \
-    '(--bool       --bool-or-int --path)--int[setting is an integer]' \
-    '(--bool --int               --path)--bool-or-int[setting is an integer]' \
-    '(--bool --int --bool-or-int       )--path[setting is a path]' \
+    '(       --int --bool-or-int --path --expiry-date)--bool[setting is a boolean]' \
+    '(--bool       --bool-or-int --path --expiry-date)--int[setting is an integer]' \
+    '(--bool --int               --path --expiry-date)--bool-or-int[setting is an integer]' \
+    '(--bool --int --bool-or-int        --expiry-date)--path[setting is a path]' \
+    '(--bool --int --bool-or-int --path              )--expiry-date[setting is an expiry date]' \
     '(-z --null)'{-z,--null}'[end values with NUL and newline between key and value]' \
     '(--get --get-all --get-urlmatch --replace-all --add --unset --unset-all --rename-section --remove-section -e --edit --get-color --get-colorbool)--name-only[show variable names only]' \
     '(--includes)'--no-includes"[don't respect \"include.*\" directives]" \
@@ -4669,6 +4670,8 @@ _git-pack-objects () {
     '--keep-true-parents[pack parents hidden by grafts]' \
     '--use-bitmap-index[use a bitmap index if available to speed up counting objects]' \
     '--write-bitmap-index[write a bitmap index together with the pack index]' \
+    '--filter=[omit certain objects from pack file]:filter:_git_rev-list_filters' \
+    '--missing=[specify how missing objects are handled]:action:(error allow-any print)' \
     ':base-name:_files'
 }
 
@@ -4781,6 +4784,10 @@ _git-update-index () {
     '--untracked-cache[enable/disable untracked cache]' \
     '--test-untracked-cache[test if the filesystem supports untracked cache]' \
     '--force-untracked-cache[enable untracked cache without testing the filesystem]' \
+    '--force-write-index[write out the index even if is not flagged as changed]' \
+    '--fsmonitor[enable or disable file system monitor]' \
+    '--fsmonitor-valid[mark files as fsmonitor valid]' \
+    '--no-fsmonitor-valid[clear fsmonitor valid bit]' \
     $z_opt \
     '*:: :_files'
 }
@@ -4972,7 +4979,8 @@ _git-ls-files () {
     '--exclude-standard[skip files in standard Git exclusion lists]' \
     '--error-unmatch[if any file does not appear in index, treat this as an error]' \
     '(-s --stage -u --unmerged)--with-tree=[treat paths removed since given tree-ish as still present]: :__git_tree_ishs' \
-    '-v[identify each files status (hmrck?)]' \
+    '(-f)-v[indicate status of each file using lowercase for assume changed files]' \
+    '(-v)-f[indicate status of each file using lowercase for fsmonitor clean files]' \
     '--full-name[force paths to be output relative to the project top directory]' \
     '--recurse-submodules[recurse through submodules]' \
     '--abbrev=[set minimum SHA1 display-length]: :__git_guard_number length' \
@@ -5069,7 +5077,14 @@ _git-rev-list () {
 
   _arguments -C -S \
     $revision_options \
-    '(--pretty)--header[display contents of commit in raw-format]' \
+    '--no-filter[turn off any previous --filter argument]' \
+    '--filter-print-omitted[print a list of objects omitted by --filter]' \
+    '--filter=[omit certain objects from pack file]:filter:_git_rev-list_filters' \
+    '--missing=[specify how missing objects are handled]:action:(error allow-any print)' \
+    '(--count --pretty --header --left-right --abbrev-commit --abbrev --parent --children)--quiet[print nothing; exit status indicates if objects are fully connected]' \
+    '--use-bitmap-index[try to speed traversal using pack bitmap index if available]' \
+    '--progress=-[show progress reports as objects are considered]:header' \
+    '(--pretty --quiet)--header[display contents of commit in raw-format]' \
     '--timestamp[print raw commit timestamp]' \
     '(         --bisect-vars --bisect-all)--bisect[show only middlemost commit object]' \
     '(--bisect)--bisect-vars[same as --bisect, displaying shell-evalable code]' \
@@ -5092,6 +5107,15 @@ _git-rev-list () {
   return ret
 }
 
+(( $+functions[_git_rev-list_filters] )) ||
+_git_rev-list_filters() {
+  _values 'filter' \
+    'blob\:none[omit all blobs]' \
+    'blob\:limit[omit blobs larger than specified size]:size' \
+    'sparse\:oid[uses a sparse-checkout specification contained in the blob]:blob-ish' \
+    'sparse\:path[uses a sparse-checkout specification contained in path]:path:_directories'
+}
+
 (( $+functions[_git-show-index] )) ||
 _git-show-index () {
   _message 'no arguments allowed; accepts index file on standard input'
@@ -5812,16 +5836,24 @@ __git_extract_aliases () {
 __git_date_formats () {
   declare -a date_formats
 
+  if compset -P 'format:'; then
+    _strftime
+    return
+  fi
+
   date_formats=(
     relative:'show dates relative to the current time'
     local:'show timestamps in local timezone'
-    iso:'show timestamps in ISO 8601 format'
-    rfc:'show timestamps in RFC 2822 format'
+    iso{,8601}:'show timestamps in ISO 8601 format'
+    iso-strict:'show timestamps in strict ISO 8601 format'
+    rfc{,2822}:'show timestamps in RFC 2822 format'
     short:'show only date but not time'
     raw:'show date in internal raw git format (%s %z)'
-    default:'show timestamp in the original timezone')
+    unix:'show date as a Unix epoch timestamp'
+    default:'show timestamp in the original timezone'
+  )
 
-  _describe -t date-formats 'date format' date_formats $*
+  _describe -t date-formats 'date format' date_formats -- '( format\:custom\ format )' -S :
 }
 
 (( $+functions[__git_gpg_secret_keys] )) ||
@@ -6983,6 +7015,8 @@ __git_setup_log_options () {
   # TODO: Need to implement -<n> for limiting the number of commits to show.
   log_options=(
     '(- *)-h[display help]'
+    '--decorate-refs=[only decorate refs that match pattern]:pattern'
+    "--decorate-refs-exclude=[don't decorate refs that match pattern]:pattern"
     '(           --no-decorate)--decorate=-[print out ref names of any commits that are shown]: :__git_log_decorate_formats'
     '(--decorate              )--no-decorate[do not print out ref names of any commits that are shown]'
     '(          --no-follow)--follow[follow renames]'
@@ -7204,16 +7238,19 @@ __git_setup_revision_options () {
     '*--author=[limit commits to those by given author]:author'
     '*--committer=[limit commits to those by given committer]:committer'
     '*--grep=[limit commits to those with log messages matching the given pattern]:pattern'
-    '--all-match[limit commits to ones matching all --grep, --author, and --committer]'
+    '--all-match[limit commits to those matching all --grep, --author, and --committer]'
+    '--invert-grep[limit commits to those not matching --grep, --author and --committer]'
     '(-i --regexp-ignore-case)'{-i,--regexp-ignore-case}'[match regexps ignoring case]'
-    '(-E --extended-regexp)'{-E,--extended-regexp}'[use POSIX extended regexps]'
-    '(-F --fixed-strings)'{-F,--fixed-strings}'[do not interpret patterns as regexps]'
+    '!(-E --extended-regexp -F --fixed-strings -P --perl-regexp)--basic-regexp'
+    '(-E --extended-regexp -F --fixed-strings -P --perl-regexp)'{-E,--extended-regexp}'[use POSIX extended regexps]'
+    '(-E --extended-regexp -F --fixed-strings -P --perl-regexp)'{-F,--fixed-strings}"[don't interpret patterns as regexps]"
+    '(-E --extended-regexp -F --fixed-strings -P --perl-regexp)'{-P,--perl-regexp}'[use perl regular expression]'
     '--remove-empty[stop when given path disappears from tree]'
-    '--merges[display only merge commits]'
-    '--no-merges[do not display commits with more than one parent]'
-    '(--min-parents --no-min-parents)--min-parents=-[show only commits having at least <n> commits]: :__git_guard_number "minimum number of parents"'
-    '(--min-parents --no-min-parents)--no-min-parents[reset limit]'
-    '(--max-parents --no-max-parents)--max-parents=-[show only commits having at most <n> commits]: :__git_guard_number "maximum number of parents"'
+    '(--no-merges --min-parents)--merges[display only merge commits]'
+    "(--merges --max-parents)--no-merges[don't display commits with more than one parent]"
+    '(--min-parents --no-min-parents --merges)--min-parents=-[show only commits with at least specified number of commits]: :__git_guard_number "number of parents"'
+    '(--min-parents --no-min-parents --merges)--no-min-parents[reset limit]'
+    '(--max-parents --no-max-parents --no-merges)--max-parents=-[show only commits with at most specified number of commits]: :__git_guard_number "number of parents"'
     '(--max-parents --no-max-parents)--no-max-parents[reset limit]'
     '--first-parent[follow only first parent from merge commits]'
     '*--not[reverses meaning of ^ prefix for revisions that follow]'
@@ -7242,18 +7279,20 @@ __git_setup_revision_options () {
     '(-g --walk-reflogs)--reverse[display commits in reverse order]'
     '(          --objects-edge)--objects[display object ids of objects referenced by listed commits]'
     '(--objects               )--objects-edge[display object ids of objects referenced by listed and excluded commits]'
-    '(          --do-walk)--no-walk[only display given revs, do not traverse their ancestors]'
+    "(          --do-walk)--no-walk=-[only display given revs, don't traverse their ancestors]::order:(sorted unsorted)"
     '(--no-walk          )--do-walk[only display given revs, traversing their ancestors]'
     '(              --cherry-pick)--cherry-mark[like --cherry-pick but mark equivalent commits instead of omitting them]'
     '(--cherry-pick              )--cherry-pick[omit any commit that introduces the same change as another commit on "the other side" of a symmetric range]'
     '(            --right-only)--left-only[list only commits on the left side of a symmetric range]'
     '(--left-only             )--right-only[list only commits on the right side of a symmetric range]'
-    '(--left-only --right-only --cherry-pick --cherry-mark)--cherry[synonym for --right-only --cherry-mark --no-merges]'
+    '(--left-only --right-only --cherry-pick --cherry-mark --no-merges --merges --max-parents)--cherry[synonym for --right-only --cherry-mark --no-merges]'
     '(-c --cc            )--full-diff[show full commit diffs when using log -p, not only those affecting the given path]'
     '--log-size[print log message size in bytes before the message]'
     '--use-mailmap[use mailmap file to map author and committer names and email]'
 
     '--reflog[show all commits from reflogs]'
+    '--single-worktree[examine the current working tree only]'
+    '--stdin[additionally read commits from standard input]'
     '--default[use argument as default revision]:default revision:__git_revisions'
     # TODO: --early-output is undocumented.
     '--early-output=-[undocumented]::undocumented'
@@ -7278,6 +7317,7 @@ __git_setup_merge_options () {
     '(-n --no-stat)--stat[show a diffstat at the end of the merge]'
     '(--stat -n --no-stat)'{-n,--no-stat}'[do not show diffstat at the end of the merge]'
     '(         --no-squash)--squash[merge, but do not commit]'
+    '--signoff[add Signed-off-by:]'
     '(--squash            )--no-squash[merge and commit]'
     '--ff-only[refuse to merge unless HEAD is up to date or merge can be resolved as a fast-forward]'
     '(-S --gpg-sign --no-gpg-sign)'{-S-,--gpg-sign=-}'[GPG-sign the commit]::key id'
diff --git a/Completion/Unix/Command/_gsettings b/Completion/Unix/Command/_gsettings
index 890b56403..2724be0ea 100644
--- a/Completion/Unix/Command/_gsettings
+++ b/Completion/Unix/Command/_gsettings
@@ -3,7 +3,7 @@
 local curcontext="$curcontext" state line expl ret=1
 local subcmds
 
-_arguments \
+_arguments -A "-*" \
   '(- 1 *)--version[show version information]' \
   '--schemadir[specify location of schemata]:directory:_directories' \
   ':command:->subcmds' \
@@ -20,6 +20,9 @@ if [[ $state = subargs ]]; then
       _arguments ':schema:->schemata' ':key:->keys' ':value'
     ;;
     (list|reset)-(keys|recursively|children)) state=schemata ;;
+    list-schemas)
+      _arguments '--print-paths'
+    ;;
     *) _default && ret=0 ;;
   esac
 fi
diff --git a/Completion/Unix/Command/_gzip b/Completion/Unix/Command/_gzip
index f9fd17819..42d4ae705 100644
--- a/Completion/Unix/Command/_gzip
+++ b/Completion/Unix/Command/_gzip
@@ -13,7 +13,9 @@ unpigz|pigz)
     '(-b --blocksize)'{-b+,--blocksize}'[set compression block size]:size (KiB)'
     '(-p --processes)'{-p,--processes}'[specify number of processes to use]'
     '(-z --zlib)'{-z,--zlib}'[compress to zlib (.zz) format]'
-    '(-T --no-time)'{-T,--no-time}"[don't store/restore modification time in/from header]"
+    '(-m --no-time)'{-m,--no-time}"[don't store/restore modification time in/from header]"
+    '(-M --time)'{-M,--time}"[store/restore modification time in/from header]"
+    '(--synchronous)-Y[force output file write to permanent storage]'
   )
 ;|
 pigz)
@@ -23,7 +25,7 @@ pigz)
     '(--rsyncable)-R[make rsync-friendly archive]'
     "($excl)"{-F,--first}'[do iterations first, before block split]'
     "($excl)"{-I+,--iterations}'[specify number of iterations for optimization]:iterations [15]'
-    "($excl)"{-M+,--maxsplits}'[specify maximum number of split blocks]:split blocks [15]'
+    "($excl)"{-J+,--maxsplits}'[specify maximum number of split blocks]:split blocks [15]'
     "($excl)"{-O,--oneblock}"[don't split into smaller blocks]"
   )
 ;|
@@ -62,6 +64,7 @@ gzip|pigz)
     '(-R)--rsyncable[make rsync-friendly archive]' \
     '(--suffix)-S+[specify suffix for compressed files]:suffix:' \
     '(-S)--suffix=[specify suffix for compressed files]:suffix:' \
+    '(-Y)--synchronous[force output file write to permanent storage]' \
     '(--test)-t[test compressed file integrity]' \
     '(-t)--test[test compressed file integrity]' \
     '(--verbose)-v[verbose mode]' \
diff --git a/Completion/Unix/Command/_ssh b/Completion/Unix/Command/_ssh
index e5d51dd29..8a122a5b3 100644
--- a/Completion/Unix/Command/_ssh
+++ b/Completion/Unix/Command/_ssh
@@ -8,19 +8,16 @@ _ssh () {
   typeset -A opt_args
 
   common=(
-    '(-2)-1[forces ssh to try protocol version 1 only]'
-    '(-1)-2[forces ssh to try protocol version 2 only]'
     '(-6)-4[forces ssh to use IPv4 addresses only]'
     '(-4)-6[forces ssh to use IPv6 addresses only]'
     '-C[compress data]'
-    # for protocol version 2, this can be a comma-separated list
-    '-c+[select encryption cipher]:encryption cipher:(idea des 3des blowfish arcfour tss none)'
+    '-c+[select encryption cipher]:encryption cipher:->ciphers'
     '-F+[specify alternate config file]:config file:_files'
     '*-i+[select identity file]:SSH identity file:_files -g "*(-.^AR)"'
     '*-o+[specify extra options]:option string:->option'
   )
   common_transfer=(
-    '-l+[limit used bandwidth]:bandwidth in Kbit/s:'
+    '-l+[limit used bandwidth]:bandwidth (Kbit/s)'
     '-P+[specify port on remote host]:port number on remote host'
     '-p[preserve modification times, access times and modes]'
     '-q[disable progress meter and warnings]'
@@ -49,8 +46,8 @@ _ssh () {
       '*-L+[specify local port forwarding]:local port forwarding:->forward' \
       '-l+[specify login name]:login name:_ssh_users' \
       '-M[master mode for connection sharing]' \
-      '(-1)-m+[specify mac algorithms]:mac spec:->macs' \
-      '(-1)-N[do not execute a remote command (protocol version 2 only)]' \
+      '-m+[specify mac algorithms]: :->macs' \
+      "-N[don't execute a remote command]" \
       '-n[redirect stdin from /dev/null]' \
       '-O+[control an active connection multiplexing master process]:multiplex control command:((check\:"check master process is running" exit\:"request the master to exit" forward\:"request forward without command execution" stop\:"request the master to stop accepting further multiplexing requests" cancel\:"cancel existing forwardings with -L and/or -R" proxy))' \
       '-P[use non privileged port]' \
@@ -59,8 +56,8 @@ _ssh () {
       '*-R+[specify remote port forwarding]:remote port forwarding:->forward' \
       '-S+[specify location of control socket for connection sharing]:path to control socket:_files' \
       '-Q+[query parameters]:parameter type:((cipher\:"supported symmetric ciphers" cipher-auth\:"supported symmetric ciphers that support authenticated encryption" mac\:"supported message integrity codes" kex\:"key exchange algorithms" key\:"key types" protocol-version\:"supported SSH protocol versions"))' \
-      '(-1)-s[invoke subsystem]' \
-      '(-1 -t)-T[disable pseudo-tty allocation (protocol version 2 only)]' \
+      '-s[invoke subsystem]' \
+      '(-t)-T[disable pseudo-tty allocation]' \
       '(-T)-t[force pseudo-tty allocation]' \
       '-V[show version number]' \
       '(-q)*-v[verbose mode (multiple increase verbosity, up to 3)]' \
@@ -95,6 +92,7 @@ _ssh () {
       '-l[list all identities]' \
       '-s+[add keys provided by the PKCS#11 shared library]:library:_files -g "*.(so|dylib)(|.<->)(-.)"' \
       '-t+[set maximum lifetime for identity]:maximum lifetime (in seconds or time format):' \
+      '-q[be quiet after a successful operation]' \
       '-X[unlock the agent]' \
       '-x[lock the agent with a password]' \
       '*:SSH identity file:_files'
@@ -177,8 +175,14 @@ _ssh () {
       "($cmn)-s[$sdesc]:CA key:_files" \
       "$p1($cmn -f -u)-I+[specify key identifier to include in certificate]:key id" \
       "$p1($cmn -f -u)-h[generate host certificate instead of a user certificate]" \
+      "$p1($cmn -f -u -D)-U[indicate that CA key is held by ssh-agent]" \
+      "$p1($cmn -f -u -U)-D+[indicate the CA key is stored in a PKCS#11 token]:PKCS11 shared library:_files -g '*.(so|dylib)(|.<->)(-.)'" \
       "$p1($cmn -f -u)-n+[specify user/host principal names to include in certificate]:principals" \
-      "$p1($cmn -f -u)-O+[specify a certificate option]:option" \
+      "$p1($cmn -f -u)*-O+[specify a certificate option]: : _values 'option'
+        clear critical\:name extension\:name force-command\:command\:_cmdstring
+	no-agent-forwarding no-port-forwarding no-pty no-user-rc no-x11-forwarding
+	permit-agent-forwarding permit-port-forwarding permit-pty permit-user-rc
+	permit-x11-forwarding source-address\:source\ address" \
       "$p1($cmn -f -u)-V+[specify certificate validity interval]:interval" \
       "($cmn -I -h -n -O -V)-k[generate a KRL file]" \
       "$p1($cmn -I -h -n -O -V)-u[update a KRL]"
@@ -217,7 +221,7 @@ _ssh () {
           ;;
         esac
         case "${IPREFIX#-o}" in
-        (#i)(afstokenpassing|batchmode|canonicalizefallbacklocal|challengeresponseauthentication|checkhostip|clearallforwardings|compression|enablesshkeysign|exitonforwardfailure|fallbacktorsh|forward(agent|x11)|forwardx11trusted|gatewayports|gssapiauthentication|gssapidelegatecredentials|gssapitrustdns|hashknownhosts|hostbasedauthentication|identitiesonly|kbdinteractiveauthentication|(tcp|)keepalive|nohostauthenticationforlocalhost|passwordauthentication|permitlocalcommand|proxyusefdpass|pubkeyauthentication|rhosts(|rsa)authentication|rsaauthentication|streamlocalbindunlink|usersh|kerberos(authentication|tgtpassing)|useprivilegedport|visualhostkey)=*)
+        (#i)(afstokenpassing|batchmode|canonicalizefallbacklocal|challengeresponseauthentication|checkhostip|clearallforwardings|compression|enablesshkeysign|exitonforwardfailure|fallbacktorsh|forward(agent|x11)|forwardx11trusted|gatewayports|gssapiauthentication|gssapidelegatecredentials|gssapikeyexchange|gssapirenewalforcesrekey|gssapitrustdns|hashknownhosts|hostbasedauthentication|identitiesonly|kbdinteractiveauthentication|(tcp|)keepalive|nohostauthenticationforlocalhost|passwordauthentication|permitlocalcommand|proxyusefdpass|pubkeyauthentication|rhosts(|rsa)authentication|rsaauthentication|streamlocalbindunlink|usersh|kerberos(authentication|tgtpassing)|useprivilegedport|visualhostkey)=*)
           _wanted values expl 'truth value' compadd yes no && ret=0
           ;;
         (#i)addressfamily=*)
@@ -239,32 +243,7 @@ _ssh () {
           _message -e 'CNAME rule list (source_domain_list:target_domain_list, each pattern list comma separated)' && ret=0
           ;;
         (#i)ciphers=*)
-          _values -s , 'encryption cipher' \
-              '3des-cbc' \
-              'aes128-cbc' \
-              'aes192-cbc' \
-              'aes256-cbc' \
-              'aes128-ctr' \
-              'aes192-ctr' \
-              'aes256-ctr' \
-              'arcfour128' \
-              'arcfour256' \
-              'arcfour' \
-              'blowfish-cbc' \
-              'cast128-cbc' \
-              \
-              'rijndael128-cbc' \
-              'rijndael192-cbc' \
-              'rijndael256-cbc' \
-              'rijndael-cbc@lysator.liu.se' \
-              && ret=0
-          ;;
-        (#i)cipher=*)
-          _wanted values expl 'encryption cipher (protocol version 1)' \
-              compadd blowfish 3des des idea arcfour tss none && ret=0
-          ;;
-        (#i)compressionlevel=*)
-          _values 'compression level' {1..9} && ret=0
+          state=ciphers
           ;;
         (#i)connectionattempts=*)
           _message -e 'connection attempts' && ret=0
@@ -304,21 +283,7 @@ _ssh () {
           _wanted hosts expl 'real host name to log into' _ssh_hosts && ret=0
           ;;
         (#i)(hostbasedkeytypes|hostkeyalgorithms|pubkeyacceptedkeytypes)=*)
-          _values -s , 'key types' \
-              'ecdsa-sha2-nistp256-cert-v01@openssh.com' \
-              'ecdsa-sha2-nistp384-cert-v01@openssh.com' \
-              'ecdsa-sha2-nistp521-cert-v01@openssh.com' \
-              'ssh-ed25519-cert-v01@openssh.com' \
-              'ssh-rsa-cert-v01@openssh.com' \
-              'ssh-dss-cert-v01@openssh.com' \
-              'ssh-rsa-cert-v00@openssh.com' \
-              'ssh-dss-cert-v00@openssh.com' \
-              'ecdsa-sha2-nistp256' \
-              'ecdsa-sha2-nistp384' \
-              'ecdsa-sha2-nistp521' \
-              'ssh-ed25519' \
-              'ssh-rsa' \
-              'ssh-dss' && ret=0
+	  _wanted key-types expl 'key type' _sequence compadd - $(_call_program key-types ssh -Q key) && ret=0
           ;;
         (#i)identityfile=*)
           _description files expl 'SSH identity file'
@@ -350,12 +315,9 @@ _ssh () {
           _values -s , 'keyboard-interactive authentication methods' \
               'bsdauth' 'pam' 'skey' && ret=0
           ;;
-        (#i)kexalgorithms=*)
-          _values -s , 'KEX algorithms' \
-              ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 \
-              diffie-hellman-group-exchange-sha256 \
-              diffie-hellman-group-exchange-sha1 \
-              diffie-hellman-group14-sha1 diffie-hellman-group1-sha1 && ret=0
+        (#i)(kexalgorithms|gssapikexalgorithms)=*)
+          _wanted algorithms expl 'key exchange algorithm' _sequence compadd - \
+              $(_call_program algorithms ssh -Q kex) && ret=0
           ;;
         (#i)localcommand=*)
           _description commands expl 'run command locally after connecting'
@@ -389,7 +351,7 @@ _ssh () {
               '1' \
               '2' && ret=0
           ;;
-        (#i)proxycommand=*)
+        (#i)(proxy|remote)command=*)
           _cmdstring && ret=0
           ;;
         (#i)rekeylimit=*)
@@ -421,7 +383,13 @@ _ssh () {
         (#i)streamlocalbindmask=*)
           _message -e 'octal mask' && ret=0
           ;;
-        (#i)(stricthostkeychecking|verifyhostkeydns|updatehostkeys)=*)
+        (#i)stricthostkeychecking=*)
+          _wanted values expl 'value' compadd yes no ask accept-new off && ret=0
+          ;;
+        (#i)syslogfacility=*)
+          _wanted facilities expl 'facility' compadd -M 'm:{a-z}={A-Z}' DAEMON USER AUTH LOCAL{0,1,2,3,4,5,6,7} && ret=0
+          ;;
+        (#i)(verifyhostkeydns|updatehostkeys)=*)
           _wanted values expl 'truthish value' compadd yes no ask && ret=0
           ;;
         (#i)transport=*)
@@ -466,11 +434,9 @@ _ssh () {
                 CertificateFile \
                 ChallengeResponseAuthentication \
                 CheckHostIP \
-                Cipher \
                 Ciphers \
                 ClearAllForwardings \
                 Compression \
-                CompressionLevel \
                 ConnectionAttempts \
                 ConnectTimeout \
                 ControlMaster \
@@ -488,10 +454,14 @@ _ssh () {
                 GatewayPorts \
                 GlobalKnownHostsFile \
                 GSSAPIAuthentication \
+                GSSAPIClientIdentity \
                 GSSAPIDelegateCredentials \
+                GSSAPIKeyExchange \
+                GSSAPIRenewalForcesRekey \
+                GSSAPIServerIdentity \
                 GSSAPITrustDns \
+                GSSAPIKexAlgorithms \
                 HashKnownHosts \
-                Host \
                 HostbasedAuthentication \
                 HostbasedKeyTypes \
                 HostKeyAlgorithms \
@@ -516,13 +486,13 @@ _ssh () {
                 PKCS11Provider \
                 Port \
                 PreferredAuthentications \
-                Protocol \
                 ProxyCommand \
                 ProxyJump \
                 ProxyUseFdpass \
                 PubkeyAcceptedKeyTypes \
                 PubkeyAuthentication \
                 RekeyLimit \
+                RemoteCommand \
                 RemoteForward \
                 RequestTTY \
                 RevokedHostKeys \
@@ -534,8 +504,8 @@ _ssh () {
                 StreamLocalBindMask \
                 StreamLocalBindUnlink \
                 StrictHostKeyChecking \
+                SyslogFacility \
                 TCPKeepAlive \
-                Transport \
                 Tunnel \
                 TunnelDevice \
                 UpdateHostKeys \
@@ -608,11 +578,18 @@ _ssh () {
       return ret
       ;;
     macs)
-      _values -s , 'MAC algorithms' hmac-md5 hmac-sha1 umac-64@openssh.com \
-          hmac-ripemd160 hmac-sha1-96 hmac-md5-96 hmac-sha2-256 \
-          hmac-sha2-256-96 hmac-sha2-512 hmac-sha2-512-96 && ret=0
+      _wanted macs expl 'MAC algorithm' _sequence compadd - $(_call_program macs ssh -Q mac)
+      return
+      ;;
+    ciphers)
+      _wanted ciphers expl 'encryption cipher' _sequence compadd - $(_call_program ciphers ssh -Q cipher)
+      return
       ;;
     command)
+      if (( $+opt_args[-s] )); then
+	_wanted subsystems expl subsystem compadd sftp
+	return
+      fi
       local -a _comp_priv_prefix
       shift 1 words
       (( CURRENT-- ))
diff --git a/Completion/Unix/Command/_sudo b/Completion/Unix/Command/_sudo
index ec293d469..10fa2e82e 100644
--- a/Completion/Unix/Command/_sudo
+++ b/Completion/Unix/Command/_sudo
@@ -47,7 +47,8 @@ else
     '(-s --shell)'{-s,--shell}'[run shell as the target user; a command may also be specified]' \
     '(-i --login)'{-i,--login}'[run login shell as the target user; a command may also be specified]' \
     '(-b --background -i --login -s --shell -e --edit)'{-b,--background}'[run command in the background]' \
-    '(-E --preserve-env -i --login -s --shell -e --edit)'{-E,--preserve-env}'[preserve user environment when running command]' \
+    '(--preserve-env -i --login -s --shell -e --edit)-E[preserve user environment when running command]' \
+    '(-E -i --login -s --shell -e --edit)--preserve-env=-[preserve user environment when running command]::environment variable:_sequence _parameters -g "*export*"' \
     '(-H --set-home -i --login -s --shell -e --edit)'{-H,--set-home}"[set HOME variable to target user's home dir]" \
     '(-P --preserve-groups -i -login -s --shell -e --edit)'{-P,--preserve-groups}"[preserve group vector instead of setting to target's]" \
     '(-)1:command: _command_names -e'
diff --git a/Completion/Unix/Command/_tidy b/Completion/Unix/Command/_tidy
index a4dccd5f5..3998ccdb3 100644
--- a/Completion/Unix/Command/_tidy
+++ b/Completion/Unix/Command/_tidy
@@ -6,8 +6,8 @@ opts=( ${${${(s.</option>.)"$(_call_program options $words[1] -xml-config)"}##*<
 opts=( ${opts/;Integer*/:number} )
 opts=( ${opts/;Boolean*/:boolean:(yes no)} )
 opts=( ${opts/;AutoBool*/:value:(auto yes no)} )
-opts=( ${opts/;(String|Tag)*/:value} )
-opts=( ${opts/(#b);(enum|Encoding|DocType)*<default>(*)<*<example>([^<]#)<*/:value [${match[2]%% *}]:(${(j. .)${(@s., .)match[3]}%% *})} )
+opts=( ${opts/;(String|Tag|Attributes)*/:value} )
+opts=( ${opts/(#bi);(enum|Encoding|DocType)*<default>(*)<*<example>([^<]#)<*/:value [${match[2]%% *}]:(${(j. .)${(@s., .)match[3]}%% *})} )
 opts=( ${(M)opts:#*:*} )
 
 _arguments -s -A "-*" --$^opts \
@@ -33,7 +33,10 @@ _arguments -s -A "-*" --$^opts \
   '(- *)'{-version,-v}'[show the version of Tidy]' \
   '(- *)'{-help,-h,-\?}'[list the command line options]' \
   '(- *)-help-config[list all configuration options]' \
+  '(- *)-help-env[show details of environment and runtime configuration]' \
   '(- *)-show-config[list the current configuration settings]' \
+  '(- *)-export-config[list the current settings in config file form]' \
+  '(- *)-export-default-config[list the default settings in config file form]' \
   "(*)-help-option[show a description of specified configuration option]:config option:(${(@j. .)opts%%:*})" \
   '(-lang -language)'{-lang,-language}'[set the two-letter language code]:language:_locales' \
   '-xml-help[list the command line options in XML format]' \
@@ -41,6 +44,7 @@ _arguments -s -A "-*" --$^opts \
   "-xml-strings[output all of Tidy's strings in XML format]" \
   '-xml-error-strings[output error constants and strings in XML format]' \
   '-xml-options-strings[output option descriptions in XML format]' \
+  '-options[specify file containing configuration settings]:config file:_files' \
   '*:file:_files' \
   + '(encoding)' \
   '-ascii[use US-ASCII for output, ISO-8859-1 for input]' \
diff --git a/Completion/Unix/Command/_tmux b/Completion/Unix/Command/_tmux
index b0aa75ba1..730959e84 100644
--- a/Completion/Unix/Command/_tmux
+++ b/Completion/Unix/Command/_tmux
@@ -180,7 +180,7 @@ _tmux-bind-key() {
     '-r[the key may repeat]' \
     '-T+[specify key table for the binding]:key table' \
     '1:key' \
-    '*:::template:_tmux'
+    '*:::template:= _tmux'
 }
 
 _tmux-break-pane() {
@@ -211,49 +211,38 @@ _tmux-capture-pane() {
 }
 
 _tmux-choose-buffer() {
-  [[ -n ${tmux_describe} ]] && print "put a window into buffer choice mode" && return
+  [[ -n ${tmux_describe} ]] && print "put a pane into buffer choice mode" && return
   _arguments -s \
-    '-F+[specify output format]:format:__tmux-format' \
+    '-N[start without the preview]' \
+    '-F+[specify format for each list item]:format:__tmux-formats' \
+    '-f+[filter items]:filter format:__tmux-formats' \
+    '-O+[initial sort order]:order:(time name size)' \
     '-t+[specify target window]:session:__tmux-windows' \
-    '*:::template:_tmux'
+    '*:::template:= _tmux'
 }
 
 _tmux-choose-client() {
   [[ -n ${tmux_describe} ]] && print "put a window into client choice mode" && return
   _arguments -s \
-    '-F+[specify output format]:format:__tmux-format' \
+    '-N[start without the preview]' \
+    '-F+[specify format for each list item]:format:__tmux-formats' \
+    '-f+[filter items]:filter format:__tmux-formats' \
+    '-O+[initial sort order]:order:(time name size)' \
     '-t+[specify target window]:session:__tmux-windows' \
-    '*:::template:_tmux'
-}
-
-_tmux-choose-session() {
-  [[ -n ${tmux_describe} ]] && print "put a window into session choice mode" && return
-  _arguments -s \
-    '-F+[specify output format]:format:__tmux-format' \
-    '-t+[specify target window]:session:__tmux-windows' \
-    '*:::template:_tmux'
+    '*:::template:= _tmux'
 }
 
 _tmux-choose-tree() {
   [[ -n ${tmux_describe} ]] && print "put a window into tree choice mode" && return
   _arguments -s \
-    '-b+[override default session command]:session-command' \
-    '-c+[override default window command]:window-command' \
-    '-S+[specify session format]:session-format:__tmux-formats' \
+    '-N[start without the preview]' \
+    '-F+[specify format for each list item]:format:__tmux-formats' \
+    '-f+[filter items]:filter format:__tmux-formats' \
+    '-O+[initial sort order]:order:(time name size)' \
     '-s[choose among sessions]' \
     '-t+[specify target window]:session:__tmux-windows' \
-    '-u[show generated tree uncollapsed at startup]' \
-    '-W+[specify window format]:window-format:__tmux-formats' \
     '-w[choose among windows]' \
-    '*:::template:_tmux'
-}
-
-_tmux-choose-window() {
-  [[ -n ${tmux_describe} ]] && print "put a window into window choice mode" && return
-  _arguments -s \
-    '-F+[specify output format]:format:__tmux-formats' \
-    '-t+[specify target window]:session:__tmux-windows' \
-    '*:::template:_tmux'
+    '*:::template:= _tmux'
 }
 
 _tmux-clear-history() {
@@ -275,7 +264,7 @@ _tmux-command-prompt() {
     '-I+[specify list of initial inputs]:initial-text (comma-separated list)' \
     '-p+[specify list of prompts]:prompts (comma-separated list)' \
     '-t+[specify target client]:client:__tmux-clients' \
-    '*:::template:_tmux'
+    '*:::template:= _tmux'
 }
 
 _tmux-confirm-before() {
@@ -283,7 +272,7 @@ _tmux-confirm-before() {
   _arguments -s \
     '-p+[specify prompt]:prompt string' \
     '-t+[specify target client]:client:__tmux-clients' \
-    '*:::command:_tmux'
+    '*:::command:= _tmux'
 }
 
 _tmux-copy-mode() {
@@ -321,14 +310,16 @@ _tmux-display-message() {
 
 _tmux-display-panes() {
   [[ -n ${tmux_describe} ]] && print "display an indicator for each visible pane" && return
-  _arguments '-t+[specify target client]:client:__tmux-clients'
+  _arguments -S \
+    '-d+[time to show indicator for]:duration (ms)' \
+    '-t+[specify target client]:client:__tmux-clients' \
+    '*:::command:= _tmux'
 }
 
 _tmux-find-window() {
   [[ -n ${tmux_describe} ]] && print "search for a pattern in windows" && return
   _arguments -s \
     '-C[match visible contents]' \
-    '-F+[specify output format]:format:__tmux-formats' \
     '-N[match window name]' \
     '-T[match window title]' \
     '-t+[specify target window]:window:__tmux-windows' \
@@ -628,6 +619,7 @@ _tmux-resize-pane() {
 _tmux-respawn-pane() {
   [[ -n ${tmux_describe} ]] && print "reuse a pane in which a command has exited" && return
   _arguments -s -A "-*" -S \
+    '-c+[specify a new working directory for the pane]:directory:_directories' \
     '-k[kill window if it is in use]' \
     '-t+[specify target pane]:pane:__tmux-pane' \
     ':command:_cmdambivalent'
@@ -636,6 +628,7 @@ _tmux-respawn-pane() {
 _tmux-respawn-window() {
   [[ -n ${tmux_describe} ]] && print "reuse a window in which a command has exited" && return
   _arguments -s -A "-*" -S \
+    '-c+[specify a new working directory for the window]:directory:_directories' \
     '-k[kill window if it is in use]' \
     '-t+[specify target window]:window:__tmux-windows' \
     ':command:_cmdambivalent'
@@ -688,6 +681,7 @@ _tmux-select-pane() {
     '-R[move to the pane right of target]' \
     '-U[move to the pane above target]' \
     '-P+[set pane style]:style:__tmux-style' \
+    '-T+[set the pane title]:title' \
     '-t+[specify target pane]:pane:__tmux-panes'
 }
 
@@ -772,6 +766,7 @@ _tmux-set-option() {
   typeset -A opt_args
   _arguments -C -s : \
     '-a[append to string options]' \
+    '-F[expand formats in the option value]' \
     '-g[set a global session option]' \
     '-o[prevent setting of an option that is already set]' \
     '-q[suppress errors about unknown or ambiguous options]' \
@@ -796,6 +791,7 @@ _tmux-set-window-option() {
   typeset -A opt_args
   _arguments -C -s : \
     '-a[append to string options]' \
+    '-F[expand formats in the option value]' \
     '-g[set a global window option]' \
     '-o[prevent setting of an option that is already set]' \
     '-q[suppress errors about unknown or ambiguous options]' \
@@ -811,8 +807,9 @@ _tmux-set-hook() {
   _arguments -s \
     '-g[add hook to global list]' \
     '-u[unset a hook]' \
-    '-t+[specify target session]:session:__tmux-sessions'
-    ':command'
+    '-t+[specify target session]:session:__tmux-sessions' \
+    ':hook name:_tmux_hooks' \
+    '*:::command:= _tmux'
 }
 
 _tmux-show-hooks() {
@@ -820,7 +817,6 @@ _tmux-show-hooks() {
   _arguments -s -S -A "-*" \
     '-g[show global list of hooks]' \
     '-t+[specify target session]:session:__tmux-sessions' \
-    ':hook name:(alert-activity alert-bell alert-silence client-attached client-detached client-resized pane-died pane-exited)' \
 }
 
 _tmux-show-buffer() {
@@ -1105,6 +1101,12 @@ function __tmux-colours() {
     fi
 }
 
+_tmux_hooks() {
+  _alternative \
+    'hooks:hook name:(alert-activity alert-bell alert-silence client-attached client-detached client-resized client-session-changed pane-died pane-exited pane-set-clipboard session-created session-closed session-renamed window-linked window-renamed window-unlinked)' \
+    'post-hooks:command post-hook:compadd - after-${_tmux_aliasmap}'
+}
+
 function __tmux-get-optarg() {
     local opt="$1"
     local -i i
diff --git a/Completion/Unix/Command/_truss b/Completion/Unix/Command/_truss
index d56839e02..b798f03cb 100644
--- a/Completion/Unix/Command/_truss
+++ b/Completion/Unix/Command/_truss
@@ -43,6 +43,9 @@ case $OSTYPE in
       "(-c)-S[don't report signals received by the process]"
       '(*)-p[trace specified existing processes]:pid:_pids'
     )
+  ;|
+  freebsd<10->.*)
+    args+=( '-H[include thread ID in the output of each event]' )
   ;;
 esac
 
diff --git a/Completion/Unix/Command/_wget b/Completion/Unix/Command/_wget
index 2a7390fba..1a7e2d0a3 100644
--- a/Completion/Unix/Command/_wget
+++ b/Completion/Unix/Command/_wget
@@ -71,6 +71,7 @@ _arguments -C -s \
   '(--adjust-extension -E)'{--adjust-extension,-E}'[save all HTML/CSS documents with proper extensions]' \
   "--ignore-length[ignore \`Content-Length' header field]" \
   '*--header=[send a custom HTTP header]:header:->header' \
+  '--compression=:compression:(auto gzip none)' \
   '--max-redirect=:number' \
   '--proxy-user=:user' \
   '--proxy-password=:password' \
@@ -90,7 +91,7 @@ _arguments -C -s \
   '--content-disposition[honor the Content-Disposition header when choosing local file names]'  \
   '--content-on-error[output received content on server errors]' \
   "--auth-no-challenge[send basic HTTP authentication without first waiting for server's challenge]" \
-  '--secure-protocol=[choose secure protocol]:protocol:(SSLv2 SSLv3 TLSv1)' \
+  '--secure-protocol=[choose secure protocol]:protocol:(SSLv2 SSLv3 TLSv1 TLSv1_1 TLSv1_2 PFS)' \
   --https-only \
   "--no-check-certificate=[don't check the server certificate]" \
   '--certificate=[specify client certificate]:client certificate file:_files' \
@@ -142,6 +143,7 @@ _arguments -C -s \
   '(-np --no-parent)'{-np,--no-parent}"[don't ascend to parent directory]" \
   '--no-verbose' \
   '--no-clobber' \
+  '--no-netrc' \
   '--no-use-server-timestamps[do not set timestamp to server provided value]' \
   '--htmlify=:htmlify:' \
   '--no:no:->noflags' \


^ permalink raw reply	[relevance 2%]

* [patch] Update _ln for [DFNO]BSD and Darwin
@ 2018-01-27 19:06  3% Matthew Martin
  0 siblings, 0 replies; 200+ results
From: Matthew Martin @ 2018-01-27 19:06 UTC (permalink / raw)
  To: zsh-workers

-L and -P are POSIX, but not supported by Darwin or NetBSD (or so the
online man pages claim).

diff --git a/Completion/Unix/Command/_ln b/Completion/Unix/Command/_ln
index efb982aba..764dc8f99 100644
--- a/Completion/Unix/Command/_ln
+++ b/Completion/Unix/Command/_ln
@@ -3,19 +3,11 @@
 local curcontext="$curcontext" state line ret=1
 local -A opt_args
 
-local -a args bsd
+local -a args opts
 args=(
-  '-f[remove existing destination files]'
+  '(-i)-f[remove existing destination files]'
   '-s[create symbolic links instead of hard links]'
 )
-bsd=(
-  '-F[remove existing destination directories]'
-  {-h,-n}'[do not dereference destination]'
-  '-i[prompt before removing destination files]'
-  '-v[print name of each linked file]'
-)
-
-local -a opts
 
 local variant
 _pick_variant -r variant gnu=gnu unix --help
@@ -50,18 +42,40 @@ elif (( ${+builtins[ln]} )); then
   args+=(
     '-d[attempt to hard link directories]'
     {-h,-n}'[do not dereference destination]'
-    '-i[prompt before removing destination files]')
-elif [[ $OSTYPE == darwin* ]]; then
-  args+=( $bsd )
-elif [[ $OSTYPE == freebsd* ]]; then
-  args+=( $bsd
-    '(-L)-P[create hard links directly to symbolic links]'
-    '(-P)-L[create hard links to symbolic link references]'
-    "-w[warn if source of a symbolic link doesn't currently exist]"
-  )
+    '(-f)-i[prompt before removing destination files]')
+else
+  case $OSTYPE in
+    darwin*|dragonfly*|freebsd*|netbsd*|openbsd*)
+      args+=(
+        {-h,-n}'[do not dereference destination]'
+      )
+    ;|
+    darwin*|dragonfly*|freebsd*|netbsd*)
+      args+=(
+      '(-f)-i[prompt before removing destination files]'
+        '-v[print name of each linked file]'
+      )
+    ;|
+    darwin*|dragonfly*|freebsd*)
+      args+=(
+        '-F[remove existing destination directories]'
+      )
+    ;|
+    dragonfly*|freebsd*|openbsd*)
+      args+=(
+        '(-L)-P[create hard links directly to symbolic links]'
+        '(-P)-L[create hard links to symbolic link references]'
+      )
+    ;|
+    dragonfly*|freebsd*)
+      args+=(
+        "-w[warn if source of a symbolic link doesn't currently exist]"
+      )
+    ;;
+  esac
 fi
 
-_arguments -C -s $opts \
+_arguments -C -s $opts : \
   $args \
   ':link target:_files' \
   '*:: :->files' && ret=0


^ permalink raw reply	[relevance 3%]

* '<<-' here-documents oddity with line continuation
@ 2018-02-03 17:39  5% Martijn Dekker
    0 siblings, 1 reply; 200+ results
From: Martijn Dekker @ 2018-02-03 17:39 UTC (permalink / raw)
  To: Zsh hackers list

zsh has an oddity with here-documents using the '<<-' operator.

(Note: below, <tab> represents a tab character, not the literal string
'<tab>'.)

POSIX says:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_07_04
| If the redirection operator is "<<-", all leading <tab> characters
| shall be stripped from input lines and the line containing the
| trailing delimiter.

In a construct like

	cat <<-EOF
<tab>	one \
<tab>	two
<tab>	EOF

where the newline after "one \" is backslash-escaped (line
continuation), zsh outputs

one two

whereas all other shells (bash, dash, *ksh, yash, etc.) output
one <tab>two

Superficially, it looks like zsh is the only shell that actually
complies with POSIX, as it strips the leading <tab> characters from all
lines in the here-document, including lines followed by a line ending in
slash.

However, line continuation in POSIXy shells is parsed at a very early
stage, even before token recognition:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02_01
| A <backslash> that is not quoted shall preserve the literal value of
| the following character, with the exception of a <newline>. If a
| <newline> follows the <backslash>, the shell shall interpret this as
| line continuation. The <backslash> and <newline> shall be removed
| before splitting the input into tokens. Since the escaped <newline>
| is removed entirely from the input and is not replaced by any white
| space, it cannot serve as a token separator.

(One funny effect of this: reserved words such as 'while' or 'select'
are not recognised if any part of them is quoted, but they can still be
split over multiple lines using line continuation!)

So it would seem logical that the definition of "input line" used by
POSIX for here-documents is based on lines resulting *after* parsing
line continuation. That would then keep the <tab>s from being stripped
from "continued" lines.

Here's a quick test script (compatible with all POSIX shells). It
outputs "zsh" on zsh and "ok" on all other shells.

tab=$(printf '\t')
lf=$(printf '\nX'); lf=${lf%X}
eval "foo=\$(cat <<-EOF${lf}${tab}1\\${lf}${tab}2${lf}${tab}EOF${lf})"
case $foo in
( 1${tab}2 ) echo ok ;;
( 12 )       echo zsh ;;
( * )        echo NEWBUG ;;
esac

Since zsh's behaviour looks sensible on the face of it, I'm reluctant to
call it a bug, but it is certainly an incompatibility and seems to be
non-compliant with POSIX. Maybe something to fix in emulation?

Thanks,

- M.


^ permalink raw reply	[relevance 5%]

* inf and nan in arithmetic expansions
@ 2018-02-07 22:30  2% Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2018-02-07 22:30 UTC (permalink / raw)
  To: Zsh hackers list

Hi,

While looking into:
https://unix.stackexchange.com/questions/422122/why-does-0-1-expand-to-0-10000000000000001-in-zsh

I noted that zsh is very good at making sure that the result of
floating point arithmetic expansions are always suitable for
reinput inside other arithmetic expansions as floats.

For instance $((0.5 * 2)) expands to 1. and not 1 so that
when used in arithmetic expressions, it is still a float.

Or that is (I think) why numbers are expressed with 17 digits,
the full precision of IEEE 754 doubles, so that when reinput, we
get the same double.

Now, there's one case where it doesn't work is when the
expansion results into nan or inf

$ echo $((1e500))
inf.
$ echo $(($((1e500))))
zsh: bad floating point constant
$ a=$((1e200**2))
$ echo $((a))
zsh: bad floating point constant
$ echo $((1e500/1e500))
-nan.
$ echo $(($((1e500/1e500))))
zsh: bad floating point constant

See also:

$ typeset -F a; a=$((1e500))
zsh: bad floating point constant


neither "inf." nor "inf" are understood in arithmetic
expressions (and for "inf.", nor by other tools like awk, or
even the builtin printf):

$ printf '%f\n' inf nan infinity NAN
inf
nan
inf
nan
$ printf '%f\n' inf. nan.
zsh: bad floating point constant
zsh: bad floating point constant
0.000000
0.000000

Not sure what's the best way to address that. 

At the moment, $((inf)) is meant to expand to the result of
the arithmetic expression in $inf

$ inf=1+1 zsh -c 'echo $((inf))'
2

It should be safe to change zsh so that inf. (and Inf. INF. NAN.
nan., maybe also Infinity.) are recognised in arithmetic
expression, as it's currently invalid, but that leaves the
problem of "inf." not being recognised by other tools
(awk/printf).

yash and ksh93 are the two other POSIX-like shells (that I know)
that support floating point arithmetics. yash is not much
better:

$ echo $((1e400))
yash: arithmetic: `1e500' is not a valid number
$ echo $((1e200*1e200))
inf
$ inf=42; echo $(($((1e200*1e200))))
42

ksh93 recognises inf (not infinity, same for its printf) and nan
on input:

$ echo $((1e6000))
inf
$ echo $((INF))
inf
$ echo $((nan))
nan
$ echo $((infinity))
0

It is documented (though not the fact that all case variants are
supported).

Slightly related:

The printf builtin understands the C99 hex with binary
exponent on input (where strtod supports them), but not on
output (%a, %A) and not in arithmetic expressions

$ printf "%g\n" 0x1p4
16
$ printf "%a\n" 16
printf: %a: invalid directive
$ echo $((0x1p4))
zsh: bad math expression: operator expected at `p4'

yash and ksh93 do support them.

(not that I would need them, that would be more for
consistency).

-- 
Stephane


^ permalink raw reply	[relevance 2%]

* Re: '<<-' here-documents oddity with line continuation
  @ 2018-02-09  7:58  3%   ` Martijn Dekker
  0 siblings, 0 replies; 200+ results
From: Martijn Dekker @ 2018-02-09  7:58 UTC (permalink / raw)
  To: Zsh hackers list

Op 09-02-18 om 08:01 schreef Martijn Dekker:
> In the next few days I hope to submit a patch that fixes both issues.
> 
> Meanwhile I would welcome opinions whether either or both of these
> issues should be fixed unconditionally, or in emulation only -- and, if
> the latter, what shell option to attach it to. POSIX_STRINGS maybe?

Here's a fairly trivial concept patch. I believe this makes zsh
here-documents act like other POSIX shells. If either or both fixes need
to be conditional upon emulation, an extra call to isset() should suffice.

- M.

diff --git a/Src/exec.c b/Src/exec.c
index c39680d..ca04b05 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -4351,7 +4351,7 @@ char *
 gethere(char **strp, int typ)
 {
     char *buf;
-    int bsiz, qt = 0, strip = 0;
+    int bsiz, qt = 0, strip = 0, linecont = 0;
     char *s, *t, *bptr, c;
     char *str = *strp;

@@ -4372,7 +4372,7 @@ gethere(char **strp, int typ)
     for (;;) {
 	t = bptr;

-	while ((c = hgetc()) == '\t' && strip)
+	while ((c = hgetc()) == '\t' && strip && !linecont)
 	    ;
 	for (;;) {
 	    if (bptr == buf + bsiz) {
@@ -4393,12 +4393,14 @@ gethere(char **strp, int typ)
 	    c = hgetc();
 	}
 	*bptr = '\0';
-	if (!strcmp(t, str))
+	if (!strcmp(t, str) && !linecont)
 	    break;
 	if (lexstop) {
 	    t = bptr;
 	    break;
 	}
+	if (!qt)
+	    linecont = (bptr > t && *(bptr - 1) == '\\');
 	*bptr++ = '\n';
     }
     *t = '\0';


^ permalink raw reply	[relevance 3%]

* Re: [BUG]builtin echo error doing arguments parsing
  @ 2018-02-22 10:32  7%     ` Peter Stephenson
    0 siblings, 1 reply; 200+ results
From: Peter Stephenson @ 2018-02-22 10:32 UTC (permalink / raw)
  To: zsh-workers

On Thu, 22 Feb 2018 09:37:11 +0000
Peter Stephenson <p.stephenson@samsung.com> wrote:
> On Wed, 21 Feb 2018 23:23:09 -0800
> wumingxwk@gmail.com wrote:
> > if there is only a '-' as argument,builtin echo won't print it
>
> You might have thought it should at least be turned off in some or most
> of the emulation modes...

Would look something like this.  Probably a good idea if there aren't
subtleties?

pws

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 6c7ec4b..098b989 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -61,7 +61,9 @@ arguments, but is otherwise ignored.  This is useful in cases where
 arguments to the command may begin with `tt(-)'.  For historical
 reasons, most builtin commands also recognize a single `tt(-)' in a
 separate word for this purpose; note that this is less standard and
-use of `tt(-)tt(-)' is recommended.
+use of `tt(-)tt(-)' is recommended.  Use of a single `tt(-)' to
+terminate option processing is turned off if the option
+tt(POSIX_BUILTINS) is set.
 
 startitem()
 prefix(-)
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 25b3d57..5217e62 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -2183,6 +2183,9 @@ Furthermore, the tt(getopts) builtin behaves in a POSIX-compatible
 fashion in that the associated variable tt(OPTIND) is not made
 local to functions.
 
+In addition, a single dash (`tt(-)') does not cause the termination of option
+processing: a double dash (`tt(-)tt(-)') is required.
+
 Moreover, the warning and special exit code from
 tt([[ -o )var(non_existent_option)tt( ]]) are suppressed.
 )
diff --git a/Src/builtin.c b/Src/builtin.c
index fb59738..d2c6ec4 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -304,7 +304,8 @@ execbuiltin(LinkList args, LinkList assigns, Builtin bn)
 		if (!(flags & BINF_KEEPNUM) && idigit(arg[1]))
 		    break;
 		/* For cd and friends, a single dash is not an option. */
-		if ((flags & BINF_SKIPDASH) && !arg[1])
+		if (((flags & BINF_SKIPDASH) || isset(POSIXBUILTINS)) &&
+		    !arg[1])
 		    break;
 		if ((flags & BINF_DASHDASHVALID) && !strcmp(arg, "--")) {
 		    /*


^ permalink raw reply	[relevance 7%]

* Re: [BUG]builtin echo error doing arguments parsing
  @ 2018-02-24  8:20  5%               ` Stephane Chazelas
  2019-04-27  6:46  5%                 ` Stephane Chazelas
  0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2018-02-24  8:20 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: zsh workers

2018-02-22 19:34:23 +0000, Peter Stephenson:
> On Thu, 22 Feb 2018 20:00:58 +0100
> Mikael Magnusson <mikachu@gmail.com> wrote:
> > I didn't try the patch but currently echo
> > -- just outputs --, only - terminates options for echo.
> 
> Yes, the -- behaviour appears to be general behaviour, in fact,
> so not something that should be changed.  So indeed it's hard to
> do this at the moment in a shell script without some kind of kludge
> for zsh.  "disable echo" and use /bin/echo might be the best bet.
> 
> However, I'm not really sure if that makes it less or actually more
> useful to align with other shells (with POSIXBUILTINS) from now on...
> it's not obvious perpetuating the need for a kludge for ever more
> is the best bet.
[...]

IMO, the best thing to do here is to do nothing. Leave it as it
is.

The fact that - marks the end of options in zsh is documented
and relatively well known.

See
https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo

That makes it one of the very few echo implementations that can
actually output arbitrary strings reliably (the only one  if you
consider that zsh is the only shell that can store NULs in its
variables)

echo -E - $var

The only other modern implementation I'm aware of that can do
that (in a Bourne-like shell) is yash's with its:

ECHO_STYLE=raw echo "$var"

To be POSIX compliant, echo -- *must* output --<nl> (-- as an
end-of-option marker must *not* be supported), and support for
-, -e and -E should be disabled. Also echo -nn should output
-nn<nl>.

However doing that would certainly break many scripts as the sh
emulation is often used to interpret code written for bash and
bash is also not POSIX compliant in that regard even in POSIX
mode (unless the xpg_echo option is also enabled) as "echo -e"
doesn't output "-e<nl>" there.

To be UNIX compliant, no option should be recognised and -e
should be the default.

People already know or should already know that echo cannot be
used for portability/reliability. It's too late to fix it and
zsh's implementation is actually the least broken of them (for
the very reason that it supports a way to mark the end of
options)..

zsh does support the POSIX printf and the ksh print which have a
more reliable and portable API (at least when limited to the
basic usage of echo, with the caveat that print '\01234' behaves
differently in zsh than in other ksh implementations).

See also https://github.com/att/ast/issues/370 for ksh93, where
they considered changing the behaviour of echo and eventually
backed down when considering the backward compatibility risk.

Note that pdksh was another shell that skipped "-" arguments.
But the "-" didn't mark the end of options, it was a bug in the
option parsing.

-- 
Stephane


^ permalink raw reply	[relevance 5%]

* Re: [doc] "sh_word_split nothing to do with word splitting"?
  @ 2018-03-11 20:53  5%   ` Stephane Chazelas
  2018-03-11 23:41  5%     ` Martijn Dekker
  0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2018-03-11 20:53 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

2018-03-11 11:24:07 -0700, Bart Schaefer:
> On Mar 11,  9:44am, Stephane Chazelas wrote:
> } Subject: [doc] "sh_word_split nothing to do with word splitting"?
> }
> } SH_WORD_SPLIT (-y) <K> <S>
> }      Causes field splitting to be performed on unquoted parameter
> }      expansions.  Note that this option has nothing to do with word
> }      splitting.  (See *note Parameter Expansion::.)
> 
> What this means to say:
> 
> This option applies to field splitting within an expanded shell word,
> not to splitting a command line into shell words during parsing.

Thanks Bart for confirming that.

What about changing it to something like:


diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 25b3d57..7677f73 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -2314,9 +2314,17 @@ pindex(NOSHWORDSPLIT)
 cindex(field splitting, sh style)
 cindex(sh, field splitting style)
 item(tt(SH_WORD_SPLIT) (tt(-y)) <K> <S>)(
-Causes field splitting to be performed on unquoted parameter expansions.
-Note that this option has nothing to do with word splitting.
-(See noderef(Parameter Expansion).)
+Causes tt($IFS) field splitting to be performed on unquoted parameter
+expansions in addition to command substitutions. Note that contrary to
+POSIX shells, field splitting is still not performed on unquoted
+arithmetic expansions and contrary to the Bourne shell, not on words
+that are not the result of expansions. Like in other Bourne-like shells,
+field splitting is only performed in contexts where several words may be
+expected, such as in arguments to simple commands or anonymous functions,
+array assignments and for loop word lists. If the tt(MULTIOS) option is
+enabled, it is also performed in the targets of tt(<), tt(>) and tt(>>)
+redirection operators. (See noderef(Parameter Expansion) and
+noderef(Redirection)).
 )
 pindex(TRAPS_ASYNC)
 pindex(NO_TRAPS_ASYNC)

A reference to $IFS may also be useful. Is it possible to add
references to index entries in yodl?

(I only include the >, >>, < redirections, that's missing the
clobber variants and the >& >>&... Not sure how to express it
without making it too wordy).

Not sure it's worth mentioning:

$var() function-definition

as a context where shwordsplit happens.

See also https://unix.stackexchange.com/a/382914

-- 
Stephane


^ permalink raw reply	[relevance 5%]

* Re: [doc] "sh_word_split nothing to do with word splitting"?
  2018-03-11 20:53  5%   ` Stephane Chazelas
@ 2018-03-11 23:41  5%     ` Martijn Dekker
  2018-03-12  7:43  4%       ` Stephane Chazelas
  0 siblings, 1 reply; 200+ results
From: Martijn Dekker @ 2018-03-11 23:41 UTC (permalink / raw)
  To: zsh-workers

Op 11-03-18 om 21:53 schreef Stephane Chazelas:
> What about changing it to something like:
[...]
> +Causes tt($IFS) field splitting to be performed on unquoted parameter
> +expansions in addition to command substitutions. Note that contrary to
> +POSIX shells, field splitting is still not performed on unquoted
> +arithmetic expansions

In terms of sh emulation, that's actually a bug. It may be terrible (who
would ever want to split an arithmetic expansion?), but emulation is
emulation and SH_WORD_SPLIT should cause splitting as in POSIX sh.

>                        and contrary to the Bourne shell, not on words
> +that are not the result of expansions.

Now that even Solaris finally got rid of it, I think the ancient Bourne
shell is definitively obsolete and not worth mentioning in current
documentation. POSIX is the norm now.

>                                         Like in other Bourne-like shells,

I'd say "Like in other POSIX shells,".

[...]
> Not sure it's worth mentioning:
> 
> $var() function-definition
> 
> as a context where shwordsplit happens.

It might be better if the possibility to define functions by names
resulting from expansions were disabled completely if SH_WORD_SPLIT is
active, so the program fails properly instead of producing weird/broken
behaviour.

Alternatively, SH_WORD_SPLIT could be ignored in that context.

- M.


^ permalink raw reply	[relevance 5%]

* Re: [doc] "sh_word_split nothing to do with word splitting"?
  2018-03-11 23:41  5%     ` Martijn Dekker
@ 2018-03-12  7:43  4%       ` Stephane Chazelas
  2018-03-12  8:07  5%         ` Stephane Chazelas
  2018-03-24 20:17  5%         ` Martijn Dekker
  0 siblings, 2 replies; 200+ results
From: Stephane Chazelas @ 2018-03-12  7:43 UTC (permalink / raw)
  To: Martijn Dekker; +Cc: zsh-workers

2018-03-12 00:41:03 +0100, Martijn Dekker:
> Op 11-03-18 om 21:53 schreef Stephane Chazelas:
> > What about changing it to something like:
> [...]
> > +Causes tt($IFS) field splitting to be performed on unquoted parameter
> > +expansions in addition to command substitutions. Note that contrary to
> > +POSIX shells, field splitting is still not performed on unquoted
> > +arithmetic expansions
> 
> In terms of sh emulation, that's actually a bug. It may be terrible (who
> would ever want to split an arithmetic expansion?), but emulation is
> emulation and SH_WORD_SPLIT should cause splitting as in POSIX sh.

I'd rather zsh keep it that way as a statement of resistance
against silliness, at least until someone complains that his
POSIX script fails when run on zsh because its arithmetic
expansions are not split as expected.

Not doing so would keep fixing scripts where authors omitted to
quote arithmetic expansions in contexts where $IFS contained
dashes (or digits) on the ground that no shell author in their
right mind would ever want to subject arithmetic expansion to
split+glob (note that no shell does split+glob upon tilde
expansion nor process substitution nor $'...' expansions (though
bash used to do globbing upon tilde expansion) where that
wouldn't make sense *either*).

pdksh was not doing word/field splitting there and posh and
OpenBSD sh still don't.

> 
> >                        and contrary to the Bourne shell, not on words
> > +that are not the result of expansions.
> 
> Now that even Solaris finally got rid of it, I think the ancient Bourne
> shell is definitively obsolete and not worth mentioning in current
> documentation. POSIX is the norm now.

Agreed. I suspect the original text was making that point about
"shwordsplit having nothing to do with word splitting" in
reference to the Bourne shell.

> >                                         Like in other Bourne-like shells,
> 
> I'd say "Like in other POSIX shells,".

Except that zsh (like many other Bourne-like shells) is not
strictly speaking fully compliant (though on several aspects is more
compliant in sh emulation than some certified ones like the
/usr/xpg4/bin/sh of Solaris)

> [...]
> > Not sure it's worth mentioning:
> > 
> > $var() function-definition
> > 
> > as a context where shwordsplit happens.
> 
> It might be better if the possibility to define functions by names
> resulting from expansions were disabled completely if SH_WORD_SPLIT is
> active, so the program fails properly instead of producing weird/broken
> behaviour.
> 
> Alternatively, SH_WORD_SPLIT could be ignored in that context.
[...]

AFAIK, that's a feature which is actually used in some of the
functions shipped with zsh to declare several functions at once
with the same body.

It's not a POSIX conformance issue as the behaviour is
unspecified there for:

$var() { ...; }

As $var is not a valid function name.

In any case, I wouldn't expect anyone to use SH_WORD_SPLIT for
anything but to interpret POSIX/ksh scripts (via "emulate sh/ksh")
which wouldn't have such constructs, so I don't expect it would
cause any problem.

On the other hand, one might still want to do:

$=myfunctions() { something; zle .$WIDGET; }

for instance, that is using the explicit word-split operator to
define several functions at once.

-- 
Stephane


^ permalink raw reply	[relevance 4%]

* Re: [doc] "sh_word_split nothing to do with word splitting"?
  2018-03-12  7:43  4%       ` Stephane Chazelas
@ 2018-03-12  8:07  5%         ` Stephane Chazelas
  2018-03-16 17:26  8%           ` Stephane Chazelas
  2018-03-24 20:17  5%         ` Martijn Dekker
  1 sibling, 1 reply; 200+ results
From: Stephane Chazelas @ 2018-03-12  8:07 UTC (permalink / raw)
  To: Martijn Dekker, zsh-workers

2018-03-12 07:43:29 +0000, Stephane Chazelas:
[...]
> Not doing so would keep fixing scripts where authors omitted to
> quote arithmetic expansions in contexts where $IFS contained
> dashes (or digits) on the ground that no shell author in their
> right mind would ever want to subject arithmetic expansion to
> split+glob (note that no shell does split+glob upon tilde
> expansion nor process substitution nor $'...' expansions (though
> bash used to do globbing upon tilde expansion) where that
> wouldn't make sense *either*).
[...]

Also note that zsh arithmetic expansions may output globs
(though the extendedglob + emulate sh + [#base] is an unlikely
combination):

/dev/fd$ ARGV0=sh zsh -o extendedglob -c 'a=$(([#8]8)); echo $a'
10

(8#10 expanded to 10 as # is an extendedglob operator and
there's a "10" file in /dev/fd).

POSIX also requires the result of arithmetic expansions to be
subject to globbing though of course it doesn't really apply in
POSIX scripts except maybe in things like [0$((-3))]

So if zsh was made POSIX compliance in that regard,
echo $(([#8]8)) would expand to 10 in that context above.

-- 
Stephane


^ permalink raw reply	[relevance 5%]

* Re: zsh/bash behavior variance: regex ERE matching
  @ 2018-03-14 14:37  5% ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2018-03-14 14:37 UTC (permalink / raw)
  To: Phil Pennock; +Cc: zsh-workers

2018-03-13 22:40:33 -0400, Phil Pennock:
[...]
> So: we ask for ERE, we get ERE+nonstandard.
> 
> On the same platform, Bash 4.4.19(1)-release from Homebrew does _NOT_
> match with `REG_ENHANCED` features.
[...]

An important note about how bash's =~ works since 3.2 (in 3.1
or with the compat31 option it works more like zsh):

In bash (and to some extent in ksh93 as well though it's very
buggy there), the shell quoting operators have an influence on
the regex matching like it does for shell wildcards.

[[ a =~ "." ]] or [[ a =~ \. ]]

actually call regcomp() with a "\." regexp.

To do that, bash needs to parse the regexp and does it using the
POSIX ERE syntax. In 

[[ a =~ \d ]] there is the same as [[ a =~ "d" ]] and calls
regcomp() with "d" while for [[ a =~ '\d' ]], it calls it with
"\\d" (the "\" being shell-quoted results in it being
regexp-escaped).

That means that if you want to use extensions, you need to use
variables or other expansions there (which you  leave unquoted).

Like:

re='\d'
[[ a =~ $re ]]

for regcomp() to be called with "\d".

Note that  (?:...) and \d are fine. We're not breaking EREs by
supporting it as the behaviour for (?:...) and \d is unspecified
in the POSIX ERE specification.

Other regexp implementations have other backward-compatible
extensions. For instance, GNU EREs support \b, \<, \>...

Some incompatibilities I'm aware of between ERE and PCRE (I
don't know if that also applies to those macOS REs):

- In POSIX ERE, [\d] matches on \ and d while it matches on a
  digit in PCRE (see also [\]] and co)
- in POSIX ERE, alternation looks for the longest match, while
  PCRE the  leftmost one that matches.

  $ echo abc | grep -oE 'a|ab'
  ab
  $ echo abc | grep -oP 'a|ab'
  a

  $ [[ abc =~ '(a|ab)' ]]; echo $match
  ab
  $ setopt rematchpcre
  $ [[ abc =~ '(a|ab)' ]]; echo $match
  a

As long as the regex library does what is required for POSIX
compliant regular expressions, since we document that =~ does
POSIX ERE, I'd say it doesn't matter what extension are
implemented over the standard.

-- 
Stephane


^ permalink raw reply	[relevance 5%]

* Re: [doc] "sh_word_split nothing to do with word splitting"?
  2018-03-12  8:07  5%         ` Stephane Chazelas
@ 2018-03-16 17:26  8%           ` Stephane Chazelas
    0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2018-03-16 17:26 UTC (permalink / raw)
  To: Martijn Dekker, zsh-workers

So, how about this new take at it? Renders (in info) as:

SH_WORD_SPLIT (-y) <K> <S>
     Causes $IFS field splitting to be performed on unquoted parameter
     expansions in addition to command substitutions.  Note that
     contrary to POSIX shells, field splitting is still not performed on
     unquoted arithmetic expansions.  Like in other Bourne-like shells,
     field splitting is only performed in contexts where several words
     may be expected, such as in arguments to simple commands or inline
     functions, array assignments and for loop word lists.  If the
     MULTIOS option is enabled, it is also performed in the targets of
     file redirections (except for the read+write ones).  (See *note
     Parameter Expansion:: and *note Redirection::).

diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 25b3d57..97782de 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -2314,9 +2314,16 @@ pindex(NOSHWORDSPLIT)
 cindex(field splitting, sh style)
 cindex(sh, field splitting style)
 item(tt(SH_WORD_SPLIT) (tt(-y)) <K> <S>)(
-Causes field splitting to be performed on unquoted parameter expansions.
-Note that this option has nothing to do with word splitting.
-(See noderef(Parameter Expansion).)
+Causes tt($IFS) field splitting to be performed on unquoted parameter
+expansions in addition to command substitutions. Note that contrary to
+POSIX shells, field splitting is still not performed on unquoted
+arithmetic expansions. Like in other Bourne-like shells, field splitting
+is only performed in contexts where several words may be expected, such
+as in arguments to simple commands or inline functions, array
+assignments and for loop word lists. If the tt(MULTIOS) option is
+enabled, it is also performed in the targets of file redirections
+(except for the read+write ones). (See noderef(Parameter Expansion) and
+noderef(Redirection)).
 )
 pindex(TRAPS_ASYNC)
 pindex(NO_TRAPS_ASYNC)


^ permalink raw reply	[relevance 8%]

* Re: [doc] "sh_word_split nothing to do with word splitting"?
  @ 2018-03-16 19:33  5%               ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2018-03-16 19:33 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Martijn Dekker, zsh-workers

2018-03-16 11:28:23 -0700, Bart Schaefer:
> On Fri, Mar 16, 2018 at 10:26 AM, Stephane Chazelas
> <stephane.chazelas@gmail.com> wrote:
> > So, how about this new take at it? Renders (in info) as:
> >
> > SH_WORD_SPLIT (-y) <K> <S>
> >      Causes $IFS field splitting to be performed on unquoted parameter
> >      expansions in addition to command substitutions.  Note that
> 
> This is OK, I just keep thinking there should be a reference back to
> the shell grammar; field splitting applies to anything identified in
> the grammar by the token WORD provided that the WORD is not quoted.
> Arithmetic expressions are treated as if double-quoted.  That's really
> all there is to it.

Note that for $((text)), there's possible confusion. In the
POSIX spec, the reference to "as if double quoted" is about the
interpretation of text, not $((text)).

In POSIX, in $(($1 + 2)), $1 is not subject to split + glob
because $1 + 2 is treated as if quoted, but $(($1 + 2)) is
*required* (!) to be split+globbed.

As in:

IFS=2; echo $((11 * 11))

is *required* to output

1 1

but zsh, even in sh emulation outputs "121" as everyone would
expect.

That's why I mention it in the text to clarify that it's an
intentionaly deviation from the POSIX standard.

(I will try and have POSIX lift that silly requirement but I
have little hope they'll accept; note that a few shells have
already gone out of their way to change their sensible
behaviour to meet POSIX compliance on that front)

-- 
Stephane


^ permalink raw reply	[relevance 5%]

* Re: [doc] "sh_word_split nothing to do with word splitting"?
  2018-03-12  7:43  4%       ` Stephane Chazelas
  2018-03-12  8:07  5%         ` Stephane Chazelas
@ 2018-03-24 20:17  5%         ` Martijn Dekker
  2018-03-25  6:42  5%           ` Stephane Chazelas
  1 sibling, 1 reply; 200+ results
From: Martijn Dekker @ 2018-03-24 20:17 UTC (permalink / raw)
  To: zsh-workers

Op 12-03-18 om 08:43 schreef Stephane Chazelas:
> I'd rather zsh keep it that way as a statement of resistance
> against silliness, at least until someone complains that his
> POSIX script fails when run on zsh because its arithmetic
> expansions are not split as expected.

The thing is, there are very good reasons for that silliness, and for
not resisting it in sh emulation.

In a way, POSIX is primarily descriptive, not prescriptive. It intends
to incorporate the historical practice that real-world shell
implementations have copied into the standard, so that everyone can be
compatible with it.

For the most part, that historical practice was established by AT&T
ksh88. It's mostly obsolete now but very much lives on in all the shells
that copy its behaviour.

ksh88 implemented the original shell arithmetic expansion. It subjects
those expansions to IFS field splitting.

Not emulating this in zsh's sh emulation mode will probably not cause
many scripts to fail on zsh-as-sh. However, someone could use zsh to
develop a POSIX script intended to be compatible with other shells.

And that someone might end up under the mistaken impression that it's
pointless to quote arithmetic expansions. Cue potential mysterious
breakage on non-zsh shells.

That's why I believe it's important that this is fixed in sh emulation.

> pdksh was not doing word/field splitting there and posh and
> OpenBSD sh still don't.

Then pdksh, supposedly a ksh88 clone, failed to clone ksh88 in that
aspect -- among many others.

pdksh, posh, NetBSD ksh, OpenBSD sh/ksh, and nearly all other pdksh
derivatives are all full of bugs and fatally broken when it comes to
implementing POSIX compatibility.

The only properly working pdksh derivative is mksh (and its "legacy"
even-more-POSIX companion lksh).

- M.


^ permalink raw reply	[relevance 5%]

* Re: [doc] "sh_word_split nothing to do with word splitting"?
  2018-03-24 20:17  5%         ` Martijn Dekker
@ 2018-03-25  6:42  5%           ` Stephane Chazelas
  2018-03-26 18:11  5%             ` Martijn Dekker
  0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2018-03-25  6:42 UTC (permalink / raw)
  To: Martijn Dekker; +Cc: zsh-workers

2018-03-24 21:17:48 +0100, Martijn Dekker:
> Op 12-03-18 om 08:43 schreef Stephane Chazelas:
> > I'd rather zsh keep it that way as a statement of resistance
> > against silliness, at least until someone complains that his
> > POSIX script fails when run on zsh because its arithmetic
> > expansions are not split as expected.
> 
> The thing is, there are very good reasons for that silliness, and for
> not resisting it in sh emulation.
> 
> In a way, POSIX is primarily descriptive, not prescriptive. It intends
> to incorporate the historical practice that real-world shell
> implementations have copied into the standard, so that everyone can be
> compatible with it.
> 
> For the most part, that historical practice was established by AT&T
> ksh88. It's mostly obsolete now but very much lives on in all the shells
> that copy its behaviour.
[...]

There have been many cases where POSIX have specified accidents
of implementation or design bugs of original implementations and
the POSIX specification has been fixed later on when spotted
even in some cases forcing the original implementation to be
fixed.

IMO, here, it's clearly one of those. The solution here is not to
implement the bug but fix the specification.

> ksh88 implemented the original shell arithmetic expansion. It subjects
> those expansions to IFS field splitting.
> 
> Not emulating this in zsh's sh emulation mode will probably not cause
> many scripts to fail on zsh-as-sh. However, someone could use zsh to
> develop a POSIX script intended to be compatible with other shells.
> 
> And that someone might end up under the mistaken impression that it's
> pointless to quote arithmetic expansions. Cue potential mysterious
> breakage on non-zsh shells.

There are many "unspecified areas" in POSIX where it's the case,
if you rely on unspecified behaviour you can't expect anything
portably.

Here I would expect POSIX, when fixed would at first leave the
behaviour unspecified to allow both the bogus historical
behaviour and the zsh behaviour (which until not so long ago was
the behaviour of most other shells, it's quite sad to see that
many have broken their shell to meet POSIX compliance), maybe
with a "future direction" requiring the zsh behaviour.

In the mean time, we should document the difference so it's no
longer "mysterious" like in my  suggested patch.

> That's why I believe it's important that this is fixed in sh emulation.
> 
> > pdksh was not doing word/field splitting there and posh and
> > OpenBSD sh still don't.
> 
> Then pdksh, supposedly a ksh88 clone, failed to clone ksh88 in that
> aspect -- among many others.
[...]

pdksh has fixed a number of issues in ksh88 (itself broken on
many aspects as you've found out) as well. Another one shared
with zsh: that the ".*" glob does not include "." nor ".." (in
the case of pdksh actually inherited from the Forsyth shell).
That also breaks POSIX compliance, though last time I mentioned
it on the Austin Group mailing list, they would likely be
willing to change the spec to allow it (POSIX already leaves it
undefined if readdir() returns those IIRC).

Another example is brace expansion (also supported by zsh and bash)
which also breaks POSIX compliance which was not in ksh88, added
in pdksh (1992), and then later in ksh93.

-- 
Stephane


^ permalink raw reply	[relevance 5%]

* [PATCH] move zsh reserved words out of the way when invoked in sh/ksh emulation
@ 2018-03-25 22:23  3% ` Martijn Dekker
  2018-04-16  8:32  3%   ` Peter Stephenson
  0 siblings, 1 reply; 200+ results
From: Martijn Dekker @ 2018-03-25 22:23 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 2308 bytes --]

In zsh-workers 27648 and 27650 (commit 8ac97f33, back in 2010), the
"repeat" reserved word was disabled when zsh starts in any emulation
mode, to avoid interfering with sh scripts when zsh is invoked as sh.

(Note that this change disabled the (t)csh-style 'repeat' loop even if
zsh is invoked as csh. It seems unlikely that was intended.)

Meanwhile, I've identified five other reserved words that are unique to
zsh *and* are perfectly plausible function names, so could kill an sh or
ksh/bash script:

	end
	float
	foreach
	integer
	nocorrect

So I think they (and 'repeat') should be disabled if zsh is invoked in
sh or ksh emulation mode.

'float' and 'integer' are part of the typeset family, so the underlying
builtins would be exposed. It would then be inconsistent not to do the
same with the rest of the typeset family:

	declare
	export
	local
	readonly
	typeset

Thankfully, the KSH_TYPESET option still works. It should revert to
being automatically enabled for ksh emulation and not for sh emulation,
as in zsh up to 5.0.8.

Treating the whole typeset family as builtins subject to KSH_TYPESET
actually increases the accuracy of sh and ksh emulation modes.

One issue is that POSIX explicitly specifies 'export' and 'readonly' as
special builtins and not as reserved words. So POSIX scripts should be
able to override them at least with an alias.[*] This breaks if they are
reserved words.

For the incomplete csh emulation mode (does anyone actually use this?)
it's probably not worth bothering to disable anything, so instead of
testing for !EMULATION(EMULATE_ZSH) we should test for
EMULATION(EMULATE_SH) || EMULATION(EMULATE_KSH). This restores 'repeat'
when zsh is invoked as csh.

By the way, contrary to claims in the documentation, the KSH_TYPESET
option was never completely obsolete. Even with the post-5.0.8 reserved
word interface activated, the behaviour of MAGIC_EQUAL_SUBST still takes
KSH_TYPESET into account.

Thanks,

- Martijn

[*] This is useful if a sh script needs to parse the output of 'export
-p' (or 'readonly -p'). It is not possible to grep their output reliably
as entries may contain newlines. The only reliable POSIX way is to have
the shell parse it, which is done by temporarily aliasing 'export' to a
handler shell function and doing an 'eval "$(export -p)"'.

[-- Attachment #2: init_builtins.patch --]
[-- Type: text/plain, Size: 5775 bytes --]

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index f460e48..9763a6c 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -1866,8 +1866,9 @@ tt(integer), tt(local), tt(readonly) or tt(typeset) is matched when the
 line is parsed (N.B. not when it is executed).  In this case the arguments
 are parsed as assignments, except that the `tt(+=)' syntax and the
 tt(GLOB_ASSIGN) option are not supported, and scalar values after tt(=)
-are em(not) split further into words, even if expanded (regardless of the
-setting of the tt(KSH_TYPESET) option; this option is obsolete).
+are em(not) split further into words, even if expanded (unless tt(zsh)
+was invoked as tt(sh) or tt(ksh), in which case it depends on the
+setting of the tt(KSH_TYPESET) option).
 
 Examples of the differences between command and reserved word parsing:
 
diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo
index d2c7cd2..bb720ce 100644
--- a/Doc/Zsh/grammar.yo
+++ b/Doc/Zsh/grammar.yo
@@ -155,6 +155,10 @@ item(tt(nocorrect))(
 Spelling correction is not done on any of the words.  This must appear
 before any other precommand modifier, as it is interpreted immediately,
 before any parsing is done.  It has no effect in non-interactive shells.
+
+The tt(nocorrect) reserved word is disabled by default when the
+shell is invoked as tt(sh) or tt(ksh).  It can be enabled
+with the command `tt(enable -r nocorrect)'.
 )
 findex(noglob)
 item(tt(noglob))(
@@ -229,8 +233,8 @@ which must evaluate to a number var(n).
 var(list) is then executed var(n) times.
 
 The tt(repeat) syntax is disabled by default when the
-shell starts in a mode emulating another shell.  It can be enabled
-with the command `tt(enable -r repeat)'
+shell is invoked as tt(sh) or tt(ksh).  It can be enabled
+with the command `tt(enable -r repeat)'.
 )
 findex(case)
 cindex(case selection)
@@ -442,6 +446,10 @@ A short form of the arithmetic tt(for) command.
 findex(foreach)
 item(tt(foreach) var(name) ... tt(LPAR()) var(word) ... tt(RPAR()) var(list) tt(end))(
 Another form of tt(for).
+
+The tt(foreach) syntax is disabled by default when the
+shell is invoked as tt(sh) or tt(ksh).  It can be enabled
+with the command `tt(enable -r foreach end)'.
 )
 item(tt(while) var(list) tt({) var(list) tt(}))(
 An alternative form of tt(while).  Note the limitations on the form of
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 25b3d57..782ad34 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -2061,17 +2061,27 @@ pindex(NOKSHTYPESET)
 cindex(argument splitting, in typeset etc.)
 cindex(ksh, argument splitting in typeset)
 item(tt(KSH_TYPESET))(
-This option is now obsolete: a better appropximation to the behaviour of
-other shells is obtained with the reserved word interface to
-tt(declare), tt(export), tt(float), tt(integer), tt(local), tt(readonly)
-and tt(typeset).  Note that the option is only applied when the reserved
-word interface is em(not) in use.
-
-Alters the way arguments to the tt(typeset) family of commands, including
+If tt(zsh) is invoked as tt(sh) or tt(ksh), this option alters
+the way arguments to the tt(typeset) family of commands, including
 tt(declare), tt(export), tt(float), tt(integer), tt(local) and
 tt(readonly), are processed.  Without this option, zsh will perform normal
 word splitting after command and parameter expansion in arguments of an
 assignment; with it, word splitting does not take place in those cases.
+
+By default, this option is superseded by a more natural way of
+parsing assignment-arguments: the reserved word interface to
+tt(declare), tt(export), tt(float), tt(integer), tt(local), tt(readonly)
+and tt(typeset).  The tt(KSH_TYPESET) option is only applied to these when
+the reserved word interface is em(not) in use -- that is, either when the
+shell was invoked as tt(sh) or tt(ksh), or when the reserved words are
+explicitly disabled using tt(disable -r), exposing the underlying builtin
+commands directly.
+
+Regardless of the above, this option always modifies the behaviour of the
+tt(MAGIC_EQUAL_SUBST) option.
+
+For tt(ksh) emulation mode, this option is enabled by default; otherwise,
+it is disabled by default.
 )
 pindex(KSH_ZERO_SUBSCRIPT)
 pindex(NO_KSH_ZERO_SUBSCRIPT)
diff --git a/Src/builtin.c b/Src/builtin.c
index fb59738..f557d0c 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -210,10 +210,29 @@ freebuiltinnode(HashNode hn)
 void
 init_builtins(void)
 {
-    if (!EMULATION(EMULATE_ZSH)) {
-	HashNode hn = reswdtab->getnode2(reswdtab, "repeat");
-	if (hn)
-	    reswdtab->disablenode(hn, 0);
+    if (EMULATION(EMULATE_SH) || EMULATION(EMULATE_KSH)) {
+	static const char *disablereswd[] = {
+	/* reserved words that may kill (k)sh scripts */
+		"end",
+		"foreach",
+		"nocorrect",
+		"repeat",
+	/* the following revert to builtins subject to KSH_TYPESET */
+		"declare",
+		"export",
+		"float",
+		"integer",
+		"local",
+		"readonly",
+		"typeset",
+		0 };
+	const char **n;
+	HashNode hn;
+	for (n = disablereswd; *n; n++) {
+	    hn = reswdtab->getnode2(reswdtab, *n);
+	    if (hn)
+		reswdtab->disablenode(hn, 0);
+	}
     }
 }
 
diff --git a/Src/options.c b/Src/options.c
index 590652e..6145b77 100644
--- a/Src/options.c
+++ b/Src/options.c
@@ -176,7 +176,7 @@ static struct optname optns[] = {
 {{NULL, "kshautoload",	      OPT_EMULATE|OPT_BOURNE},	 KSHAUTOLOAD},
 {{NULL, "kshglob",	      OPT_EMULATE|OPT_KSH},	 KSHGLOB},
 {{NULL, "kshoptionprint",     OPT_EMULATE|OPT_KSH},	 KSHOPTIONPRINT},
-{{NULL, "kshtypeset",	      0},			 KSHTYPESET},
+{{NULL, "kshtypeset",	      OPT_EMULATE|OPT_KSH},	 KSHTYPESET},
 {{NULL, "kshzerosubscript",   0},			 KSHZEROSUBSCRIPT},
 {{NULL, "listambiguous",      OPT_ALL},			 LISTAMBIGUOUS},
 {{NULL, "listbeep",	      OPT_ALL},			 LISTBEEP},

^ permalink raw reply	[relevance 3%]

* Re: [doc] "sh_word_split nothing to do with word splitting"?
  2018-03-25  6:42  5%           ` Stephane Chazelas
@ 2018-03-26 18:11  5%             ` Martijn Dekker
  0 siblings, 0 replies; 200+ results
From: Martijn Dekker @ 2018-03-26 18:11 UTC (permalink / raw)
  To: zsh-workers

Op 25-03-18 om 08:42 schreef Stephane Chazelas:
> There have been many cases where POSIX have specified accidents
> of implementation or design bugs of original implementations and
> the POSIX specification has been fixed later on when spotted
> even in some cases forcing the original implementation to be
> fixed.
> 
> IMO, here, it's clearly one of those. The solution here is not to
> implement the bug but fix the specification.

By that token, global field splitting and globbing themselves are
massive design flaws (which they are -- as you know I've been working on
a cross-shell way to make it practical to get rid of them).

Yet, POSIX shells conform, at least in their respective POSIX modes.

At least the current spec is consistent. All POSIX expansions are
subject to global split & glob (unless those are disabled).

Why would $( ... ) be subject to split+glob but not $(( ... ))? That
would be a bit confusing, and would detract from quoting expansions
consistently.

I think consistent and broken is better than inconsistent and broken.

> There are many "unspecified areas" in POSIX where it's the case,
> if you rely on unspecified behaviour you can't expect anything
> portably.

I don't see that as a reason to add yet another unspecified behaviour.
In practice we'd be stuck with it for a decade anyway, maybe two.

To rephrase myself, I think specified and broken is better than
unspecified and broken.

> In the mean time, we should document the difference so it's no
> longer "mysterious" like in my  suggested patch.

Yes, but what wonderful planet do you live on, where people actually
read documentation? :)

>> Then pdksh, supposedly a ksh88 clone, failed to clone ksh88 in that
>> aspect -- among many others.
> [...]
> 
> pdksh has fixed a number of issues in ksh88 (itself broken on
> many aspects as you've found out) as well. Another one shared
> with zsh: that the ".*" glob does not include "." nor ".." (in
> the case of pdksh actually inherited from the Forsyth shell).

I don't really see that as a fix though, just as another mostly
pointless difference causing incompatibility surprises. ksh93 matches
'.' and '..' to this day.

On zsh, it looks like another emulation bug. The SH_GLOB option should
cause ".*" to match "." and "..".

(Actually, modernish ought to detect this as a shell quirk with its own
ID. Thanks for reminding me of it.)

- M.


^ permalink raw reply	[relevance 5%]

* Re: _sh doing _normal completion
  @ 2018-03-28 13:46  3%   ` Leah Neukirchen
  2018-03-28 13:59  3%     ` Stephane Chazelas
  0 siblings, 1 reply; 200+ results
From: Leah Neukirchen @ 2018-03-28 13:46 UTC (permalink / raw)
  To: zsh-workers

Peter Stephenson <p.w.stephenson@ntlworld.com> writes:

> On Tue, 27 Mar 2018 15:34:12 +0200
> Leah Neukirchen <leah@vuxu.org> wrote:
>> the default _sh completion tries _files, then falls back to _normal.
>> However, sh, [t]csh and rc don't support this, and ksh/bash only
>> support it for actual shell scripts.
>> 
>> So I wonder if this is a reasonable completion?
>
> Do you mean falling back to _normal isn't sensible, i.e. it's not worth
> assuming you can specify an executable command or similar after the file
> name, since it's unlikely the shell can handle it? It's not a particularly
> sophisticated fallback, certainly.  It's possible you could think of a
> better way of searching for finding a script in the path, which I guess
> is the real intention.  Until someone does, this is probably going to
> stay the best we've got.

The main problem is that it floods the reasonable expansions (from .)
with the full contents of $PATH.

As discussed in #zsh I now use:
zstyle ':completion:*:(sh|rc):*' tag-order '! commands builtins'

(POSIX /bin/sh may do a PATH lookup, but dash and OpenBSD sh don't.)

thx,
-- 
Leah Neukirchen  <leah@vuxu.org>  http://leah.zone


^ permalink raw reply	[relevance 3%]

* Re: _sh doing _normal completion
  2018-03-28 13:46  3%   ` Leah Neukirchen
@ 2018-03-28 13:59  3%     ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2018-03-28 13:59 UTC (permalink / raw)
  To: Leah Neukirchen; +Cc: zsh-workers

2018-03-28 15:46:32 +0200, Leah Neukirchen:
[...]
> The main problem is that it floods the reasonable expansions (from .)
> with the full contents of $PATH.
[...]

Some sh implementations (bash, zsh, AT&T ksh) look-up slash-less
commands in $PATH if not found in the currently directory, some
(mksh, dash, yash) don't. Both behaviours are allowed by POSIX.

ksh88 actually looked it up in $PATH *before* the current
directory, causing security vulnerabilities on systems with
support for setuid scripts for instance.

If you want to run a script in the current directory, I would
suggest you get  used to typing:

sh ./myscr<Tab>

to avoid surprises on those systems that lookup scripts in
$PATH. The fact that zsh completes commands in $PATH when you do
sh myscr<Tab> is actually useful as a reminder that it's not a
wise thing to do.

-- 
Stephane


^ permalink raw reply	[relevance 3%]

* Re: [PATCH] move zsh reserved words out of the way when invoked in sh/ksh emulation
  2018-03-25 22:23  3% ` [PATCH] move zsh reserved words out of the way when invoked in sh/ksh emulation Martijn Dekker
@ 2018-04-16  8:32  3%   ` Peter Stephenson
  2018-04-16 21:26  3%     ` Bart Schaefer
  0 siblings, 1 reply; 200+ results
From: Peter Stephenson @ 2018-04-16  8:32 UTC (permalink / raw)
  To: Zsh hackers list

On Mon, 26 Mar 2018 00:23:07 +0200
Martijn Dekker <martijn@inlv.org> wrote:
> Meanwhile, I've identified five other reserved words that are unique
> to zsh *and* are perfectly plausible function names, so could kill an
> sh or ksh/bash script:
> 
> 	end
> 	float
> 	foreach
> 	integer
> 	nocorrect
> 
> So I think they (and 'repeat') should be disabled if zsh is invoked in
> sh or ksh emulation mode.

As far as I can see this isn't a particularly big deal as long a
properly documented seeing as "enable -r" works OK if people want
to mix code in emulation mode (in which case they can expect
to have the odd extra hoop to jump through).

> 'float' and 'integer' are part of the typeset family, so the
> underlying builtins would be exposed. It would then be inconsistent
> not to do the same with the rest of the typeset family:
> 
> 	declare
> 	export
> 	local
> 	readonly
> 	typeset
> 
> Thankfully, the KSH_TYPESET option still works. It should revert to
> being automatically enabled for ksh emulation and not for sh
> emulation, as in zsh up to 5.0.8.

This is when I got really confused when I first saw this patch.  This
breaks the syntax supporting inline parentheses present in POSIX mode
like any other and replaces it with what was always a kludge (though
does remain useful in certain cases), and furthermore doesn't have any
clear functional motivation.  So my eyes just glazed over.

pws


^ permalink raw reply	[relevance 3%]

* PATCH: completion option updates
@ 2018-04-16 14:28  1% Oliver Kiddle
  0 siblings, 0 replies; 200+ results
From: Oliver Kiddle @ 2018-04-16 14:28 UTC (permalink / raw)
  To: Zsh workers

There's a few more options updated affecting completion functions.
Versions are:

ethtool 4.16
htop 2.2.0
pidof (procps-ng) 3.3.13
git 2.17
GNU sed 4.5
sqlite 3.23.1
ssh 7.7 - I said before that there was no changes but on closer
inspection, there are a couple.

Oliver

diff --git a/Completion/Linux/Command/_ethtool b/Completion/Linux/Command/_ethtool
index 52b8f0451..84f2837a8 100644
--- a/Completion/Linux/Command/_ethtool
+++ b/Completion/Linux/Command/_ethtool
@@ -69,7 +69,7 @@ if [[ -n $state ]]; then
   rx-frames-low|tx-usecs-low|tx-frames-low|pkt-rate-high|rx-usecs-high) ;&
   rx-frames-high|tx-usecs-high|tx-frames-high|sample-interval|dmac|rx-mini) ;&
   rx-jumbo|offset|length|magic|value|phyad|proto|tos|tclass|l4proto|src-port) ;&
-  dst-port|spi|l4data|vlan-etype|vlan|user-def|action|vf|queue|loc|delete) ;&
+  dst-port|spi|l4data|vlan-etype|vlan|user-def|action|vf|queue|loc) ;&
   other|combined|tx-timer|count)
     _message -e numbers 'number'
   ;;
@@ -130,17 +130,20 @@ if [[ -n $state ]]; then
     _message -e masks mask
   ;;
   hkey)
-    _message -e keys expl 'hash key'
+    _message -e keys 'hash key'
   ;;
   hfunc)
-    _message -e functions expl 'hash function'
+    _message -e functions 'hash function'
   ;;
   flags)
-    _message -e masks expl mask
+    _message -e masks mask
   ;;
   encoding)
     _wanted encodings expl encoding compadd auto off rs baser
   ;;
+  context)
+    _message -e contexts 'RSS context'
+  ;;
   *)
     case $words[2] in
     -A|--pause)
@@ -218,7 +221,7 @@ if [[ -n $state ]]; then
       fi
           ;;
     -n|-u|--show-nfc|--show-ntuple)
-      _wanted options expl option compadd -F line - rx-flow-hash rule
+      _wanted options expl option compadd -F line - rx-flow-hash context rule
     ;;
     -N|-U|--config-nfc|--config-ntuple)
       if [[ $words[CURRENT-2] = rx-flow-hash ]]; then
@@ -231,19 +234,25 @@ if [[ -n $state ]]; then
           'f[bytes 0 and 1 of the Layer 4 header]' \
           'n[bytes 2 and 3 of the Layer 4 header]' \
           'r[discard all packets of this flow type]'
+      elif [[ $words[CURRENT-1] = delete ]]; then
+        _message -e numbers 'number'
       else
-        _wanted options expl option compadd -F line - rx-flow-hash flow-type \
+        _wanted options expl option compadd -F line - rx-flow-hash context flow-type \
             delete src dst proto src-ip dst-ip tos m tclass l4proto src-port \
             dst-port spi l4data vlan-etype vlan user-def dst-mac action vf \
-            queue loc
+            queue context loc
       fi
     ;;
+    -x|--show-rxfh-indir|--show-rxfh)
+      _wanted options expl option compadd -F line - context
+    ;;
     -X|--set-rxfh-indir|--rxfh)
       _values -S ' ' -w 'option' \
+        context \
         '(weight default)equal' \
         '(equal default)weight' \
         '(equal weight)default' \
-        hkey hfunc
+        hkey hfunc delete
     ;;
     -f|--flash)
       if (( CURRENT = 4 )); then
diff --git a/Completion/Linux/Command/_htop b/Completion/Linux/Command/_htop
index 9a6133a86..28c7512bf 100644
--- a/Completion/Linux/Command/_htop
+++ b/Completion/Linux/Command/_htop
@@ -1,10 +1,11 @@
 #compdef htop
 
 _arguments -S : \
-  '(-d --delay)'{-d+,--delay=}'[update frequency]:duration' \
+  '(-d --delay)'{-d+,--delay=}'[update frequency]:duration (tenths of seconds)' \
   '(-C --no-color --no-colour)'{-C,--no-colo{,u}r}'[monochrome mode]' \
-  '(-h --help)'{-h,--help}'[display help]' \
-  '(-p --pid)'{-p+,--pid=}'[show given pids]: : _sequence -n ${$(</proc/sys/kernel/pid_max)\:-32768} _pids' \
-  '(-s --sort-key)'{-s+,--sort-key=}'[sort by key]:key:( ${(f)"$($service --sort-key help)"} )' \
+  '(-)'{-h,--help}'[display usage information]' \
+  \*{-p+,--pid=}'[show given pids]: : _sequence -n ${$(</proc/sys/kernel/pid_max)\:-32768} _pids' \
+  '(-s --sort-key)'{-s+,--sort-key=}'[sort by key]:key:( ${(f)"$(_call_program sort-keys $words[1] --sort-key help)"} )' \
+  '(-t --tree)'{-t,--tree}'[show tree view of processes]' \
   '(-u --user)'{-u+,--user=}'[show processes of user]: : _users' \
-  '(-v --version)'{-v,--version}'[print version information]'
+  '(-)'{-v,--version}'[display version information]'
diff --git a/Completion/Linux/Command/_pidof b/Completion/Linux/Command/_pidof
index 6605e7e67..05fb23d45 100644
--- a/Completion/Linux/Command/_pidof
+++ b/Completion/Linux/Command/_pidof
@@ -10,7 +10,8 @@ _arguments -C -s -w \
   "(-s --single-shot $exargs)"{-s,--single-shot}'[return one PID only]' \
   "(-c --check-root $exargs)"{-c,--check-root}'[omit processes with different root]' \
   "(-x $exargs)"-x'[include shells running named scripts]' \
-  "($exargs)"\*{-o+,--omit-pid}'[omit processes with PIDs]:pids:_sequence -s , _pids' \
+  "($exargs)"\*{-o+,--omit-pid=}'[omit processes with PIDs]:pids:_sequence -s , _pids' \
+  '(-S --separator)'{-S+,--separator=}'[specify separator put between PIDs]:separator' \
   '*:process:->procnames' \
   && return 0
 
diff --git a/Completion/Unix/Command/_git b/Completion/Unix/Command/_git
index 0eb16987d..f6cec8b60 100644
--- a/Completion/Unix/Command/_git
+++ b/Completion/Unix/Command/_git
@@ -116,6 +116,8 @@ _git-am () {
     '(-u --utf8 --no-utf8)--no-utf8[pass -n to git mailinfo]' \
     '(-3 --3way)'{-3,--3way}'[use 3-way merge if patch does not apply cleanly]' \
     $apply_options \
+    '--quit[abort the patching operation but keep HEAD where it is]' \
+    '--show-current-patch[show the patch being applied]' \
     '(-i --interactive)'{-i,--interactive}'[apply patches interactively]' \
     '--committer-date-is-author-date[use author date as committer date]' \
     '--ignore-date[use committer date as author date]' \
@@ -637,6 +639,7 @@ _git-clone () {
     '--separate-git-dir[place .git dir outside worktree]:path to .git dir:_path_files -/' \
     '(-4 --ipv4 -6 --ipv6)'{-4,--ipv4}'[use IPv4 addresses only]' \
     '(-4 --ipv4 -6 --ipv6)'{-6,--ipv6}'[use IPv6 addresses only]' \
+    '--filter=[object filtering]:filter:_git_rev-list_filters' \
     ': :->repository' \
     ': :_directories' && ret=0
 
@@ -685,6 +688,8 @@ _git-commit () {
     $reset_author_opt \
     '(        --porcelain --dry-run)--short[dry run with short output format]' \
     '--branch[show branch information]' \
+    '!(--no-ahead-behind)--ahead-behind' \
+    "--no-ahead-behind[don't display detailed ahead/behind counts relative to upstream branch]" \
     '(--short             --dry-run)--porcelain[dry run with machine-readable output format]' \
     '(--short --porcelain --dry-run -z --null)'{-z,--null}'[dry run with NULL-separated output format]' \
     {-p,--patch}'[use the interactive patch selection interface to chose which changes to commit]' \
@@ -865,6 +870,8 @@ _git-fetch () {
     '--deepen[deepen history of shallow clone]:number of commits' \
     '(-n --no-tags -t --tags)'{-n,--no-tags}'[disable automatic tag following]' \
     '(--all -m --multiple)'{-m,--multiple}'[fetch from multiple remotes]' \
+    '(-P --prune-tags)'{-P,--prune-tags}'[prune local tags no longer on remote and clobber changed tags]' \
+    '--filter=[object filtering]:filter:_git_rev-list_filters' \
     '*:: :->repository-or-group-or-refspec' && ret=0
 
   case $state in
@@ -1431,6 +1438,7 @@ _git-rebase () {
     '(-)--edit-todo[edit interactive instruction sheet in an editor]' \
     '(-)--skip[skip the current patch]' \
     '(-)--quit[abort but keep HEAD where it is]' \
+    '(-)--show-current-patch[show the patch file being applied or merged]' \
     - options \
     '(-m --merge)'{-m,--merge}'[use merging strategies to rebase]' \
     '(-S --gpg-sign --no-gpg-sign)'{-S-,--gpg-sign=-}'[GPG-sign the commit]::key id' \
@@ -1450,6 +1458,7 @@ _git-rebase () {
     '(-p --preserve-merges --interactive)'{-p,--preserve-merges}'[try to recreate merges instead of ignoring them]' \
     {-x+,--exec=}'[with -i\: append "exec <cmd>" after each line]:command:_command_names -e' \
     '(-k --keep-empty)'{-k,--keep-empty}'[keep empty commits in the result]' \
+    '--allow-empty-message[allow rebasing commits with empty messages]' \
     '(1)--root[rebase all reachable commits]' \
     $autosquash_opts \
     '(--autostash --no-autostash)--autostash[stash uncommitted changes before rebasing and apply them afterwards]' \
@@ -1747,6 +1756,8 @@ _git-status () {
     $branch_opts \
     '(-s --short)--porcelain=-[produce machine-readable output]:version:(v1)' \
     '(-s --short)--show-stash[show stash information]' \
+    '!(--no-ahead-behind)--ahead-behind' \
+    "--no-ahead-behind[don't display detailed ahead/behind counts relative to upstream branch]" \
     '(-u --untracked-files)'{-u-,--untracked-files=-}'[show untracked files]::mode:((no\:"show no untracked files" \
                                                                                      normal\:"show untracked files and directories" \
                                                                                      all\:"also show untracked files in untracked directories (default)"))' \
@@ -1988,6 +1999,7 @@ _git-tag () {
   _arguments \
     - creation \
       '(-a --annotate -s --sign -u --local-user)'{-a,--annotate}'[create an unsigned, annotated tag]' \
+      '(-e --edit)'{-e,--edit}'[force edit of tag message]' \
       '(-a --annotate -s --sign -u --local-user)'{-s,--sign}'[create a signed and annotated tag]' \
       '(-a --annotate -s --sign)'{-u+,--local-user=}'[create a tag, annotated and signed with the given key]: :__git_gpg_secret_keys' \
       '(-f --force)'{-f,--force}'[replace existing tag]' \
@@ -2037,6 +2049,8 @@ _git-worktree() {
         prune:'prune working tree information'
         list:'list details of each worktree'
 	lock:'prevent a working tree from being pruned'
+	move:'move a working tree to a new location'
+	remove:'remove a working tree'
 	unlock:'allow working tree to be pruned, moved or deleted'
       )
 
@@ -2051,32 +2065,44 @@ _git-worktree() {
 	  else
 	    args=( ':commit:__git_commits' )
 	  fi
-          _arguments \
+          _arguments -S \
 	    '(-f --force)'{-f,--force}'[checkout branch even if already checked out in another worktree]' \
 	    '(-B --detach)-b+[create a new branch]: :__git_branch_names' \
 	    '(-b --detach)-B+[create or reset a branch]: :__git_branch_names' \
 	    '(-b -B)--detach[detach HEAD at named commit]' \
 	    '--no-checkout[suppress file checkout in new worktree]' \
-	    ':path:_files' $args && ret=0
+	    '--lock[keep working tree locked after creation]' \
+	    ':path:_directories' $args && ret=0
 	;;
         (prune)
-          _arguments \
+          _arguments -S \
 	    '(-n --dry-run)'{-n,--dry-run}"[don't remove, show only]" \
 	    '(-v --verbose)'{-v,--verbose}'[report pruned objects]' \
 	    '--expire[expire objects older than specified time]:time' && ret=0
 	;;
         (list)
-	  _arguments '--porcelain[machine-readable output]' && ret=0
+	  _arguments -S '--porcelain[machine-readable output]' && ret=0
 	;;
 	(lock)
-	  _arguments -C '--reason=[specify reason for locking]:reason' ': :->worktrees' && ret=0
-	  [[ -z $state ]] && return ret
-	;&
+	  _arguments -C -S '--reason=[specify reason for locking]:reason' ': :->worktrees' && ret=0
+	;;
+	(move)
+	  _arguments -C \
+            ': :->worktrees' \
+            ':location:_directories' && ret=0
+	;;
+	(remove)
+	  _arguments -C -S '--force[remove working trees that are not clean or that have submodules]' \
+            ': :->worktrees' && ret=0
+	;;
 	(unlock)
-	  _wanted directories expl 'working tree' compadd -S ' ' -f -M 'r:|/=* r:|=*' \
-	      ${${(M)${(f)"$(_call_program directories git worktree list --porcelain)"}:#worktree*}#* }
+	  state=worktrees
 	;;
       esac
+      if [[ $state = worktrees ]]; then
+        _wanted directories expl 'working tree' compadd -S ' ' -f -M 'r:|/=* r:|=*' \
+            ${${(M)${(f)"$(_call_program directories git worktree list --porcelain)"}:#worktree*}#* } && ret=0
+      fi
     ;;
   esac
   return ret
@@ -3442,6 +3468,7 @@ _git-prune () {
     '(-v --verbose)'{-v,--verbose}'[report all removed objects]' \
     '--progress[show progress]' \
     '--expire=[only expire loose objects older than specified date]: :__git_datetimes' \
+    '--exclude-promisor-objects[limit traversal to objects outside promisor packfiles]' \
     '*:: :__git_heads'
 }
 
@@ -4133,11 +4160,14 @@ _git-send-email () {
     '--cc-cover[copy the Cc: list from the first file to the rest]' \
     '--compose[edit introductory message for patch series]' \
     '--from=[specify sender]:email address:_email_addresses' \
+    '--reply-to=[specify Reply-To address]:email address:_email_addresses' \
     '--in-reply-to=[specify contents of first In-Reply-To header]:message-id' \
     '--subject=[specify the initial subject of the email thread]:subject' \
     '--to=[specify the primary recipient of the emails]: :_email_addresses' \
+    "--no-xmailer[don't add X-Mailer header]" \
     '--8bit-encoding=[encoding to use for non-ASCII messages]: :__git_encodings' \
     '--compose-encoding=[encoding to use for compose messages]: :__git_encodings' \
+    '--transfer-encoding=[specify transfer encoding to use]:transfer encoding:(quoted-printable 8bit base64)' \
     '--envelope-sender[specify the envelope sender used to send the emails]: :_email_addresses' \
     '--smtp-encryption=[specify encryption method to use]: :__git_sendemail_smtpencryption_values' \
     '--smtp-domain=[specify FQDN used in HELO/EHLO]: :_domains' \
@@ -4147,7 +4177,10 @@ _git-send-email () {
     '--smtp-server-option=[specify the outgoing SMTP server option to use]:SMPT server option' \
     '--smtp-ssl-cert-path=[path to ca-certificates (directory or file)]:ca certificates path:_files' \
     '--smtp-user=[specify user to use for SMTP-AUTH]:smtp user:_users' \
+    '--smtp-auth=[specify allowed AUTH mechanisms]:space-separated list of mechanisms' \
     '--smtp-debug=[enable or disable debug output]:smtp debug:((0\:"disable" 1\:"enable"))' \
+    '--batch-size=[specify maximum number of messages per connection]:number' \
+    '--relogin-delay=[specify delay between successive logins]:delay (seconds)' \
     '--cc-cmd=[specify command to generate Cc\: header with]:Cc\: command:_cmdstring' \
     '--to-cmd=[specify command to generate To\: header with]:To\: command:_cmdstring' \
     '(                 --no-chain-reply-to)--chain-reply-to[send each email as a reply to previous one]' \
@@ -4168,6 +4201,7 @@ _git-send-email () {
     '(           --no-validate)--validate[perform sanity checks on patches]' \
     '(--validate              )--no-validate[do not perform sanity checks on patches]' \
     '--force[send emails even if safety checks would prevent it]' \
+    '(- *)--dump-aliases[dump configured aliases and exit]' \
     '*: : _alternative -O expl
       "files:file:_files"
       "commits:recent commit object name:__git_commit_objects_prefer_recent"'
@@ -4671,7 +4705,8 @@ _git-pack-objects () {
     '--use-bitmap-index[use a bitmap index if available to speed up counting objects]' \
     '--write-bitmap-index[write a bitmap index together with the pack index]' \
     '--filter=[omit certain objects from pack file]:filter:_git_rev-list_filters' \
-    '--missing=[specify how missing objects are handled]:action:(error allow-any print)' \
+    '--missing=[specify how missing objects are handled]:action:(error allow-any allow-promisor print)' \
+    "--exclude-promisor-objects[don't pack objects in promisor packfiles]" \
     ':base-name:_files'
 }
 
@@ -5080,7 +5115,7 @@ _git-rev-list () {
     '--no-filter[turn off any previous --filter argument]' \
     '--filter-print-omitted[print a list of objects omitted by --filter]' \
     '--filter=[omit certain objects from pack file]:filter:_git_rev-list_filters' \
-    '--missing=[specify how missing objects are handled]:action:(error allow-any print)' \
+    '--missing=[specify how missing objects are handled]:action:(error allow-any allow-promisor print)' \
     '(--count --pretty --header --left-right --abbrev-commit --abbrev --parent --children)--quiet[print nothing; exit status indicates if objects are fully connected]' \
     '--use-bitmap-index[try to speed traversal using pack bitmap index if available]' \
     '--progress=-[show progress reports as objects are considered]:header' \
@@ -5197,6 +5232,7 @@ _git-daemon () {
     '--forbid-override[forbid overriding site-wide service]: :__git_daemon_service' \
     '(--no-informative-errors)--informative-errors[report more verbose errors to the client]' \
     '(--informative-errors)--no-informative-errors[report all errors as "access denied" to the client]' \
+    '--log-destination=[send log messages to the specified destination]:destination:(stderr syslog none)' \
     '*:repository:_directories'
 }
 
@@ -7071,6 +7107,7 @@ __git_setup_diff_options () {
     '--diff-filter=-[select certain kinds of files for diff]: :_guard "[AaCcDdMmRrTtUuXxBb*]#" kinds'
     '-S-[look for differences that add or remove the given string]:string'
     '-G-[look for differences whose added or removed line matches the given regex]:pattern'
+    '--find-object=[look for differences that change the number of occurrences of the specified object]:object:__git_blobs'
     '--pickaxe-all[when -S finds a change, show all changes in that changeset]'
     '--pickaxe-regex[treat argument of -S as regular expression]'
     '-O-[output patch in the order of glob-pattern lines in given file]: :_files'
diff --git a/Completion/Unix/Command/_sed b/Completion/Unix/Command/_sed
index 222798b8a..15b92a634 100644
--- a/Completion/Unix/Command/_sed
+++ b/Completion/Unix/Command/_sed
@@ -21,6 +21,7 @@ elif _pick_variant gnu=GNU unix --version; then
   args+=(
     '--follow-symlinks[follow symlinks when processing in place]'
     '(-i --in-place)'{-i-,--in-place=-}$inplace
+    '(-c --copy)'{-c,--copy}'[copy instead of rename when shuffling files in in-place mode]'
     '(-l --line-length)'{-l,--line-length=-}'[specify line-wrap length for the l command]'
     '(-r)--posix[disable GNU extensions]'
     '(-E -r --regexp-extended)'{-E,-r,--regexp-extended}$extended
diff --git a/Completion/Unix/Command/_sqlite b/Completion/Unix/Command/_sqlite
index f63f4c085..58f7a9116 100644
--- a/Completion/Unix/Command/_sqlite
+++ b/Completion/Unix/Command/_sqlite
@@ -41,6 +41,7 @@ options+=(
 )
 
 (( $+sqlite3 )) && options+=(
+  $^dashes'-append[append the database to the end of the file]'
   $^dashes'-bail[stop after hitting an error]'
   $^dashes'-cmd[run specified command before reading stdin]:sqlite meta-command'
   '(-*batch -*interactive)'$^dashes'-batch[force batch I/O]'
@@ -49,6 +50,7 @@ options+=(
   $^dashes'-mmap[set default mmap size]:size'
   $^dashes'-newline[set output row separator]:separator [\n]'
   $^dashes'-pagecache[specify size and number of slots for page cache memory]:size (bytes): :slots'
+  $^dashes'-readonly[open the database read-only]'
   $^dashes'-stats[print memory stats before each finalize]'
   $^dashes'-vfs[use specified default VFS]:vfs:(unix-dotfile unix-excl unix-none unix-namedsem)'
 )
diff --git a/Completion/Unix/Command/_ssh b/Completion/Unix/Command/_ssh
index 2aae7027e..9c827d655 100644
--- a/Completion/Unix/Command/_ssh
+++ b/Completion/Unix/Command/_ssh
@@ -32,6 +32,7 @@ _ssh () {
     _arguments -C -s \
       '(-a)-A[enable forwarding of the authentication agent connection]' \
       '(-A)-a[disable forwarding of authentication agent connection]' \
+      '-B+[bind to specified interface before attempting to connect]:interface:_net_interfaces' \
       '(-P)-b+[specify interface to transmit on]:bind address:_bind_addresses' \
       '-D+[specify a dynamic port forwarding]:dynamic port forwarding:->dynforward' \
       '-e+[set escape character]:escape character (or `none'\''):' \
@@ -90,6 +91,8 @@ _ssh () {
       '-k[load plain private keys only and skip certificates]' \
       '-L[list public key parameters of all identities in the agent]'\
       '-l[list all identities]' \
+      '-m+[specify minimum remaining signatures before maximum is changed]:number' \
+      '-M+[specify maximum number of signatures]:number' \
       '-s+[add keys provided by the PKCS#11 shared library]:library:_files -g "*.(so|dylib)(|.<->)(-.)"' \
       '-t+[set maximum lifetime for identity]:maximum lifetime (in seconds or time format):' \
       '-q[be quiet after a successful operation]' \


^ permalink raw reply	[relevance 1%]

* Re: [PATCH] move zsh reserved words out of the way when invoked in sh/ksh emulation
  2018-04-16  8:32  3%   ` Peter Stephenson
@ 2018-04-16 21:26  3%     ` Bart Schaefer
  0 siblings, 0 replies; 200+ results
From: Bart Schaefer @ 2018-04-16 21:26 UTC (permalink / raw)
  To: Zsh hackers list

On Apr 16,  9:32am, Peter Stephenson wrote:
} Subject: Re: [PATCH] move zsh reserved words out of the way when invoked i
}
} On Mon, 26 Mar 2018 00:23:07 +0200
} Martijn Dekker <martijn@inlv.org> wrote:
} > 'float' and 'integer' are part of the typeset family, so the
} > underlying builtins would be exposed. It would then be inconsistent
} > not to do the same with the rest of the typeset family:
} > 
} > 	declare
} > 	export
} > 	local
} > 	readonly
} > 	typeset
} 
} This is when I got really confused when I first saw this patch.  This
} breaks the syntax supporting inline parentheses present in POSIX mode

This is fairly bad.  We made those into reserved words particularly for
this reason.

I would not support breaking this.


^ permalink raw reply	[relevance 3%]

* PATCH: handle system specific arguments in _dd
@ 2018-04-17 22:08  4% Oliver Kiddle
  0 siblings, 0 replies; 200+ results
From: Oliver Kiddle @ 2018-04-17 22:08 UTC (permalink / raw)
  To: Zsh workers

This expands dd completion to deal with system specifics.

Oliver

diff --git a/Completion/Unix/Command/_dd b/Completion/Unix/Command/_dd
index e51337f21..e5c5e63ce 100644
--- a/Completion/Unix/Command/_dd
+++ b/Completion/Unix/Command/_dd
@@ -1,17 +1,99 @@
 #compdef dd gdd
 
-_values -w 'option' \
-  'if[specify input file]:input file:_tilde_files' \
-  'of[specify output file]:output file:_tilde_files' \
-  '(bs)ibs[input block size]:block size (bytes)' \
-  '(bs)obs[output block size]:block size (bytes)' \
-  '(ibs obs)bs[block size]:block size (bytes)' \
-  'cbs[conversion buffer size]:buffer size (bytes)' \
-  'skip[input blocks initially skipped]:blocks' \
-  'seek[output blocks initially skipped]:blocks' \
-  'files[specify number of input files to copy and concatenate]:number of files' \
-  'count[number of input blocks to copy]:blocks' \
-  'conv[specify conversions to apply]:conversion:_values -s , conversion
-    "(ebcdic ibm)ascii" "(ascii ibm)ebcdic" "(ascii ebcdic)ibm"
-    "(unblock)block" "(block)unblock"
-    "(ucase)lcase" "(lcase)ucase" swab noerror sync'
+local -a vals conv flags
+local variant
+
+_pick_variant -r variant gnu=GNU $OSTYPE --version
+
+vals=(
+  '(ibs obs)bs[block size]:block size (bytes)'
+  'cbs[conversion buffer size]:buffer size (bytes)'
+  'conv[specify conversions to apply]: :_values -s , conversion $conv'
+  'count[number of input blocks to copy]:blocks'
+  '(bs)ibs[input block size]:block size (bytes)'
+  'if[specify input file]:input file:_tilde_files'
+  '(bs)obs[output block size]:block size (bytes)'
+  'of[specify output file]:output file:_tilde_files'
+  'seek[output blocks initially skipped]:blocks'
+  'skip[input blocks initially skipped]:blocks'
+)
+conv=(
+  '(ascii asciib oldascii ebcdic ebcdicb oldebcdic ibm ibmb oldibm)'{ascii,ebcdic,ibm}
+  '(unblock)block' '(block)unblock'
+  '(ucase)lcase' '(lcase)ucase'
+  swab sync noerror notrunc
+)
+
+case $variant in
+  ^gnu)
+    vals+=(
+      'files[specify number of input files to copy and concatenate]:number of files'
+    )
+  ;|
+  (gnu|darwin|dragonfly|(free|net)bsd*)
+    conv+=( sparse )
+  ;|
+  gnu|netbsd*)
+    vals+=(
+      '*iflag[specify read flags]:flag:_sequence compadd - $flags'
+      '*oflag[specify write flags]:flag:_sequence compadd - $flags'
+    )
+    flags=( append direct directory dsync sync nonblock noctty nofollow )
+  ;|
+  darwin*|dragonfly*|(free|net)bsd*)
+    vals+=(
+      'oseek[output blocks initially skipped]:blocks'
+    )
+    conv+=(
+      '(ascii oldascii ebcdic oldebcdic oldibm)'old{ascii,ebcdic,ibm}
+    )
+  ;|
+  (darwin|dragonfly|freebsd|netbsd|solaris)*)
+    vals+=(
+      'iseek[input blocks initially skipped]:blocks'
+    )
+  ;|
+  (open|free)bsd*)
+    vals+=(
+      'status[specify level of information to print to stderr]:level:(none noxfer)'
+    )
+    conv+=( osync )
+  ;|
+
+  freebsd*)
+    vals+=(
+      'fillchar[specify padding character]:character'
+      'speed[limit copying speed]:speed (bytes/second)'
+    )
+    conv+=(
+      '(pareven parnone parodd parset)'{pareven,parnone,parodd,parset}
+    )
+  ;;
+  gnu)
+    vals+=(
+      'status[specify level of information to print to stderr]:level:(none noxfer progress)'
+    )
+    flags+=( fullblock noatime nocache count_bytes skip_bytes seek_bytes )
+    conv+=( excl nocreat fdatasync fsync )
+  ;;
+  netbsd*)
+    vals+=(
+      'msgfmt[specify format for information summary]:format:(quiet posix human)'
+      'progress[enable progress display]:enable:(1)'
+    )
+    flags+=(
+      wronly rdwr creat trunc excl shlock exlock cloexec nosigpipe rsync alt_io async
+    )
+  ;;
+  solaris*)
+    vals+=(
+      'files[specify number of input files to copy and concatenate]:number of files'
+      'oseek[output blocks initially skipped (via seek, not NUL-padding)]:blocks'
+    )
+    conv+=(
+      '(ascii asciib ebcdic ebcdicb ibmb)'{ascii,ebcdic,ibm}b
+    )
+  ;;
+esac
+
+_values -w 'option' $vals


^ permalink raw reply	[relevance 4%]

* [PATCH] posix_builtins: allow exporting a reaonly
@ 2018-04-18 19:58  4% Martijn Dekker
  2018-04-18 20:07  0% ` Bart Schaefer
  2019-06-20 18:47  0% ` [PATCH] posix_builtins: allow exporting a readonly Martijn Dekker
  0 siblings, 2 replies; 200+ results
From: Martijn Dekker @ 2018-04-18 19:58 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 309 bytes --]

POSIX_BUILTINS incorrectly prohibits exporting a readonly variable. All 
other POSIX shells allow this and there is nothing in the POSIX text[*] 
that says it's not allowed. The attached patch fixes this.

Thanks,

- M.

[*] 
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_22

[-- Attachment #2: export-readonly.patch --]
[-- Type: text/plain, Size: 456 bytes --]

diff --git a/Src/builtin.c b/Src/builtin.c
index 73cfe7a..a75c4b2 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2169,7 +2169,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 	    !ASG_VALUEP(asg))
 	    on |= PM_UNSET;
 	else if (usepm && (pm->node.flags & PM_READONLY) &&
-		 !(on & PM_READONLY)) {
+		 !(on & PM_READONLY) && !(on & PM_EXPORTED)) {
 	    zerr("read-only variable: %s", pm->node.nam);
 	    return NULL;
 	}

^ permalink raw reply	[relevance 4%]

* Re: [PATCH] posix_builtins: allow exporting a reaonly
  2018-04-18 19:58  4% [PATCH] posix_builtins: allow exporting a reaonly Martijn Dekker
@ 2018-04-18 20:07  0% ` Bart Schaefer
  2018-04-21 12:57  0%   ` Martijn Dekker
  2019-06-20 18:47  0% ` [PATCH] posix_builtins: allow exporting a readonly Martijn Dekker
  1 sibling, 1 reply; 200+ results
From: Bart Schaefer @ 2018-04-18 20:07 UTC (permalink / raw)
  To: Martijn Dekker; +Cc: Zsh hackers list

On Wed, Apr 18, 2018 at 12:58 PM, Martijn Dekker <martijn@inlv.org> wrote:
> POSIX_BUILTINS incorrectly prohibits exporting a readonly variable. All
> other POSIX shells allow this and there is nothing in the POSIX text[*] that
> says it's not allowed. The attached patch fixes this.

I hope it's really that simple -- see thread about typeset -T and
read-only and resulting confusion, e.g. zsh-workers/42061 and
references.


^ permalink raw reply	[relevance 0%]

* Re: [PATCH] posix_builtins: allow exporting a reaonly
  2018-04-18 20:07  0% ` Bart Schaefer
@ 2018-04-21 12:57  0%   ` Martijn Dekker
  0 siblings, 0 replies; 200+ results
From: Martijn Dekker @ 2018-04-21 12:57 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: zsh-workers

Op 18-04-18 om 22:07 schreef Bart Schaefer:
> On Wed, Apr 18, 2018 at 12:58 PM, Martijn Dekker <martijn@inlv.org> wrote:
>> POSIX_BUILTINS incorrectly prohibits exporting a readonly variable. All
>> other POSIX shells allow this and there is nothing in the POSIX text[*] that
>> says it's not allowed. The attached patch fixes this.
> 
> I hope it's really that simple -- see thread about typeset -T and
> read-only and resulting confusion, e.g. zsh-workers/42061 and
> references.

I can't really find anything in that thread or its references that seems 
relevant.

Exporting readonly variables was already possible with POSIXBUILTINS 
off, so I don't see how it could harm anything to allow it with 
POSIXBUILTINS on.

Here is some more context than was provided by the patch. Note that all 
of this is only executed if POSIXBUILTINS is on.

     if (isset(POSIXBUILTINS)) {
         /*
          * Stricter rules about retaining readonly attribute in this case.
          */
         if ((on & (PM_READONLY|PM_EXPORTED)) &&
             (!usepm || (pm->node.flags & PM_UNSET)) &&
             !ASG_VALUEP(asg))
             on |= PM_UNSET;
         else if (usepm && (pm->node.flags & PM_READONLY) &&
                  !(on & PM_READONLY) && !(on & PM_EXPORTED)) {
             zerr("read-only variable: %s", pm->node.nam);
             return NULL;
         }
     }

As the comment says, that block applies stricter rules about retaining 
the readonly attribute for POSIXBUILTINS. I merely removed the 
restriction that readonly variables cannot be exported by adding:

	 && !(on & PM_EXPORTED)

Note that the restriction was already inconsistent: this code did not 
block unset readonly variables from being given the export flag.

FWIW, the test suite passes cleanly with this patch.

Thanks,

- M.


^ permalink raw reply	[relevance 0%]

* Re: "echo | ps -j $(:) | cat | cat | cat" runs components in different process groups
  @ 2018-04-23 14:03  3%                                   ` Peter Stephenson
       [not found]                                         ` <CGME20180423140859eucas1p2591bf1422614209979d4890383268c37@eucas1p2.samsung.com>
  0 siblings, 1 reply; 200+ results
From: Peter Stephenson @ 2018-04-23 14:03 UTC (permalink / raw)
  To: Zsh hackers' list

On Mon, 23 Apr 2018 14:52:38 +0100
Peter Stephenson <p.stephenson@samsung.com> wrote:
> > zsh% ls | less
> > zsh: done                    ls | 
> > zsh: suspended (tty output)  less
> 
> Hmmm --- I wonder if less was already forked and that's the problem?
> So maybe we should check if there are other processes already forked
> and avoid resetting the pgrp leader in that case?  Does anyone
> actually know?

Actually, I think this is the *other* thing I was wondering about but
didn't get around to taking account of...

See if this makes it reliable.  I don't think it breaks the original
fix.

The main reason I didn't do this is the effect of killpg with signal 0
isn't actually defined in the Linu man pages or as far as I can see in
POSIX. But we do this elsewhere so presumably it's a well-known fact
this works...

pws

diff --git a/Src/signals.c b/Src/signals.c
index 6e12158..f2165c0 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -538,7 +538,8 @@ wait_for_processes(void)
 		update_process(pn, status);
 #endif
 		if (WIFEXITED(status) &&
-		    pn->pid == jn->gleader) {
+		    pn->pid == jn->gleader &&
+		    killpg(pn->pid, 0) == -1) {
 		    jn->gleader = 0;
 		    if (!(jn->stat & STAT_NOSTTY)) {
 			/*


^ permalink raw reply	[relevance 3%]

* Re: "echo | ps -j $(:) | cat | cat | cat" runs components in different process groups
       [not found]                                         ` <CGME20180423140859eucas1p2591bf1422614209979d4890383268c37@eucas1p2.samsung.com>
@ 2018-04-23 14:08  0%                                       ` Peter Stephenson
  0 siblings, 0 replies; 200+ results
From: Peter Stephenson @ 2018-04-23 14:08 UTC (permalink / raw)
  To: Zsh hackers' list

On Mon, 23 Apr 2018 15:03:12 +0100
Peter Stephenson <p.stephenson@samsung.com> wrote:
> The main reason I didn't do this is the effect of killpg with signal 0
> isn't actually defined in the Linu man pages or as far as I can see in
> POSIX. But we do this elsewhere so presumably it's a well-known fact
> this works...

After enough poking in manuals, this certainly looks pukka on Linux...

"man killpg" sez

       On  Linux, killpg() is implemented as a library function that makes the
       call kill(-pgrp, sig).

and "man 2 kill" sez

       If  sig  is 0, then no signal is sent, but error checking is still per‐
       formed; this can be used to check for the existence of a process ID  or
       process group ID.

Given it's already all over the shell it's obviously the right thing to
do here, too.

pws


^ permalink raw reply	[relevance 0%]

* [patch] update _df
@ 2018-04-28  4:30 10% Matthew Martin
  0 siblings, 0 replies; 200+ results
From: Matthew Martin @ 2018-04-28  4:30 UTC (permalink / raw)
  To: zsh-workers

Update _df for [DFNO]BSD.

- Matthew Martin


diff --git a/Completion/Unix/Command/_df b/Completion/Unix/Command/_df
index 2586f4358..a31145cd4 100644
--- a/Completion/Unix/Command/_df
+++ b/Completion/Unix/Command/_df
@@ -28,32 +28,63 @@ if _pick_variant gnu=GNU unix --version; then
     {-H,--si}'[human readable format, but use powers of 1000 not 1024]'
     {-i,--inodes}'[list inode information instead of block usage]'
   )
-elif [[ "$OSTYPE" == (darwin|freebsd|dragonfly)* ]]; then
+elif [[ "$OSTYPE" == (darwin|dragonfly|freebsd|netbsd*|openbsd)* ]]; then
   args=(
-    '(-b -g -H -h -k -m)-b[use 512-byte blocks (default)]'
-    '(-b -g -H -h -k -m)-g[use 1024^3-byte blocks]'
-    '(-b -g -H -h -k -m)-H[human-readable output (base 10)]'
-    '(-b -g -H -h -k -m)-h[human-readable output (base 2)]'
-    '(-b -g -H -h -k -m)-k[use 1024-byte blocks]'
-    '(-b -g -H -h -k -m)-m[use 1024*1024-byte blocks]'
-    '-P[POSIX compliant output]'
-    '-a[show all mount points]'
-    '-c[display a grand total]'
-    '-i[include inode usage statistics (default)]'
+    '(-b -g -H -h -k -m --si)-h[human-readable output (base 2)]'
+    '(-b -g -H -h -k -m --si)-k[use 1024-byte blocks]'
+    '(-G -i -P)-P[POSIX compliant output]'
+    '(-G -i -P)-i[include inode usage statistics (default)]'
     '-l[only display locally-mounted file systems]'
     '-n[use previously obtained statistics]'
     '*:files:_umountable'
   )
   spec='[only display file systems of specified types]:file system type:->fslist'
   case "$OSTYPE" in
+    (darwin*|dragonfly*|freebsd*|netbsd*)
+      args+=(
+        '-a[show all mount points]'
+        '(-b -g -H -h -k -m --si)-g[use 1024^3-byte blocks]'
+        '(-b -g -H -h -k -m --si)-m[use 1024*1024-byte blocks]'
+      )
+      ;|
+    (darwin*|dragonfly*|freebsd*)
+      args+=(
+        '(-b -g -H -h -k -m --si)-b[use 512-byte blocks (default)]'
+        '(-b -g -H -h -k -m --si)-H[human-readable output (base 10)]'
+      )
+      ;|
+    (darwin*|freebsd*)
+      args+=(
+        '-c[display a grand total]'
+      )
+      ;|
     (darwin*)
       args+=(
         "-T+$spec"
         "!-t+$spec" # obsolete
       )
       ;;
-    (freebsd*|dragonfly*)
-      args+=( "-t+$spec" '-T[include file system type]' )
+    (dragonfly*|freebsd*|netbsd*|openbsd*)
+      args+=(
+        "-t+$spec"
+      )
+      ;|
+    (dragonfly*|freebsd*)
+      args+=(
+        '-T[include file system type]'
+      )
+      ;|
+    (freebsd*)
+      args+=(
+        '--libxo[generate output via libxo]'
+        '(-b -g -H -h -k -m --si)--si[human-readable output (base 10)]'
+        '-,[separate thousands]'
+      )
+      ;;
+    (netbsd*)
+      args+=(
+        '(-G -i -P)-G[display all fields in statvfs]'
+      )
       ;;
   esac
 else


^ permalink raw reply	[relevance 10%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  @ 2018-05-14  6:36  3%   ` Stephane Chazelas
  2018-05-14  6:44  5%     ` Stephane Chazelas
  2018-05-14  8:11  0%     ` [PATCH] " Sebastian Gniazdowski
  0 siblings, 2 replies; 200+ results
From: Stephane Chazelas @ 2018-05-14  6:36 UTC (permalink / raw)
  To: Sebastian Gniazdowski; +Cc: Zsh hackers list

2018-05-14 04:27:46 +0200, Sebastian Gniazdowski:
> On 13 May 2018 at 23:25, Stephane Chazelas <stephane.chazelas@gmail.com>
> wrote:
> 
> > I noticed that [[:blank:]] was not matching on non-ASCII blank
> > characters. In a typical UTF-8 GNU locale, [[:blank:]] normally
> > includes
> >
> 
> Let's be conservative. [[:blank:]] matches 2 characters, [[:space:]]
> matches Unicode ones that you want to add.
[...]

That's not true.

[[:blank:]] is horizontal spacing characters (like \h in perl),
[[:space:]] is all spacing characters (like \s in perl),
including vertical ones like \v, \f, \n...

On some systems (like the ones that follow ISO/IEC 30112 such as
GNU), that's excluding the ones that should not be considered as
delimiters (like U+00A0 the non-breaking space).

[[:blank:]], [[:space:]]... are POSIX character classes,
supported by most utilities that do wildcard or regexp matching.

I know of no other utility than zsh whose [[:space:]] includes
all the characters classified as "space" in the locale and where
[[:blank:]] doesn't include all the "blank" ones.

That struck me as very odd when I found that out yesterday and
is inconsistent with all other shells. But because that meant
extra code was added for that, I wondered if maybe that was
intentional.

It seems to me that if you wanted to match on only SPC and TAB
and not the other horizontal spacing characters classified as
such in the locale, you should use [ $'\t']. See also [[:IFS:]]
and [[:IFSSPACE:]] though they depend on the value of $IFS and
include \n by default (and \0 for [[:IFS:]]).

Now it's true that most people only care about SPC and TAB, and
since there's so much variation between systems as to what is
classified as "blank" (same for "alpha"... for that matters), it
probably doesn't matter that much. U+00A0 is probably the only
other horizontal spacing character that people are likely to
find in text that zsh is going to match [[:blank:]] against and
every other system doesn't consider it as "blank" (or "space"
for that matters).

-- 
Stephane


^ permalink raw reply	[relevance 3%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-14  6:36  3%   ` Stephane Chazelas
@ 2018-05-14  6:44  5%     ` Stephane Chazelas
  2018-05-14  8:47  0%       ` Peter Stephenson
  2018-05-14  8:11  0%     ` [PATCH] " Sebastian Gniazdowski
  1 sibling, 1 reply; 200+ results
From: Stephane Chazelas @ 2018-05-14  6:44 UTC (permalink / raw)
  To: Sebastian Gniazdowski, Zsh hackers list

2018-05-14 07:36:11 +0100, Stephane Chazelas:
[...]
> That struck me as very odd when I found that out yesterday and
> is inconsistent with all other shells. But because that meant
> extra code was added for that, I wondered if maybe that was
> intentional.
[...]

Looking at the Changelog, I see:

Tue Oct 13 21:42:47 1998  Andrew Main  <zefram@zsh.org>

        * Doc/Zsh/expn.yo, Src/glob.c: Add the [:blank:] character class
          required by POSIX, which has no corresponding ctype macro.

        * Doc/Zsh/expn.yo, Misc/globtests, Src/glob.c, Src/lex.c:
          Add POSIX globbing character classes ([:alnum:] etc.).
          (pws, 4209+4212)


Which explains why it's not using isblank() and strongly
suggests that it was not intentional.

Looking at POSIX:
http://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/isblank.html

> First released in Issue 6. Derived from the ISO/IEC 9899:1999 standard.

So it's /relatively/ recent (late 90s). Do we also need an
autoconf check for isblank() or can we assume that all systems
zsh is supported on have it?

-- 
Stephane


^ permalink raw reply	[relevance 5%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-14  6:36  3%   ` Stephane Chazelas
  2018-05-14  6:44  5%     ` Stephane Chazelas
@ 2018-05-14  8:11  0%     ` Sebastian Gniazdowski
  1 sibling, 0 replies; 200+ results
From: Sebastian Gniazdowski @ 2018-05-14  8:11 UTC (permalink / raw)
  To: Sebastian Gniazdowski, Zsh hackers list

On 14 May 2018 at 08:36, Stephane Chazelas <stephane.chazelas@gmail.com> wrote:
> [[:blank:]], [[:space:]]... are POSIX character classes,
> supported by most utilities that do wildcard or regexp matching.
>
> I know of no other utility than zsh whose [[:space:]] includes
> all the characters classified as "space" in the locale and where
> [[:blank:]] doesn't include all the "blank" ones.

Do you think some middle-way is possible? I mean, enabling :blank: to
that much new characters and observing ML for user reports (who knows,
maybe there wouldn't be many or any, but yeah, "who knows") is like
compiling 32 bit product on 64 bit compiler and continue selling it
without break. Well, to be honest, I was hired in one work when this
happened and it worked. By middle-way I mean: to look at the possible
characters, recognize ones that are crucial for lowering the oddity of
Zshell's :blank:, and include just a few in :blank:, doing some
thinking if some of chosen characters doesn't have a potential to
break something. It's a difficult situation because from one point of
view, nothing should break, exotic spaces don't occur often and even
if they did, they shouldn't break anything, the code should behave as
more robust. But from other point of view, any character added to
:blank: has its twin-code assigned that will break.

> It seems to me that if you wanted to match on only SPC and TAB
> and not the other horizontal spacing characters classified as
> such in the locale, you should use [ $'\t']. See also [[:IFS:]]
> and [[:IFSSPACE:]] though they depend on the value of $IFS and
> include \n by default (and \0 for [[:IFS:]]).

I've greped some projects to check if they used :blank::

- https://github.com/zsh-users/zsh-syntax-highlighting/blob/5b539663c0d740a0c00169d5ecbd58e47ff16252/highlighters/main/main-highlighter.zsh#L959

- https://github.com/zsh-users/zaw/blob/91c5e1a179ba543458e341a4d8e95c75f762f5c6/sources/ssh-hosts.zsh#L13

- Zshells distributed functions: _complete, _expand_alias,
_description, _main_complete, _bsd_pkg, compinstall, zcalc, etc. quite
many.

- my 3 past projects, most notably Zshelldoc, which parses scripts to
extract functions.

These uses aren't drastic, and I think all those projects would work
after replacing :blank: with :space:. But there's no way to be sure.
Who knows maybe some DevOp wanted to uplift some system at work and
proposed use of Zsh, and has :blank: in his scripts.

-- 
Best regards,
Sebastian Gniazdowski


^ permalink raw reply	[relevance 0%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-14  6:44  5%     ` Stephane Chazelas
@ 2018-05-14  8:47  0%       ` Peter Stephenson
  2018-05-14 12:34  4%         ` Stephane Chazelas
  0 siblings, 1 reply; 200+ results
From: Peter Stephenson @ 2018-05-14  8:47 UTC (permalink / raw)
  To: Zsh hackers list

On Mon, 14 May 2018 07:44:31 +0100
Stephane Chazelas <stephane.chazelas@gmail.com> wrote:
> Tue Oct 13 21:42:47 1998  Andrew Main  <zefram@zsh.org>
> 
>         * Doc/Zsh/expn.yo, Src/glob.c: Add the [:blank:] character
> class required by POSIX, which has no corresponding ctype macro.
> 
> Which explains why it's not using isblank() and strongly
> suggests that it was not intentional.

I think that's correct, but I tend to agree with Sebastian that some
caution is required here since it's not necessarily clear what action
with non-ASCII spaces is actually wanted when this is used.  I'd be
surprised if it actually broke anything, though.

pws


^ permalink raw reply	[relevance 0%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-14  8:47  0%       ` Peter Stephenson
@ 2018-05-14 12:34  4%         ` Stephane Chazelas
    0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2018-05-14 12:34 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: Zsh hackers list

2018-05-14 09:47:33 +0100, Peter Stephenson:
> On Mon, 14 May 2018 07:44:31 +0100
> Stephane Chazelas <stephane.chazelas@gmail.com> wrote:
> > Tue Oct 13 21:42:47 1998  Andrew Main  <zefram@zsh.org>
> > 
> >         * Doc/Zsh/expn.yo, Src/glob.c: Add the [:blank:] character
> > class required by POSIX, which has no corresponding ctype macro.
> > 
> > Which explains why it's not using isblank() and strongly
> > suggests that it was not intentional.
> 
> I think that's correct, but I tend to agree with Sebastian that some
> caution is required here since it's not necessarily clear what action
> with non-ASCII spaces is actually wanted when this is used.  I'd be
> surprised if it actually broke anything, though.
[...]

I was going to say that surely, when someone uses [:blank:] that
means they want to trust the locale on the definition of
"blank", and I can't see why that should be different from other
character classes, but I just noticed that the documentation
actually says:

     [:blank:]
               The character is either space or tab

Instead of "horizontal whitespace". And on GNU systems,
"isblank(3)" also says its SPC and TAB:

     Returns true if C is a blank character; that is, a space or a tab.
     This function was originally a GNU extension, but was added in
     ISO C99.

While iswblank(3) is careful to refer to locale classification.

In practice, the only system where I could find a locale with a
single-byte charset with "blank" characters other than SPC and
TAB was NetBSD. And there, isblank(0xa0) under setlocale() in a
locale that uses ISO8859-1 for instance does return true (as
POSIX requires if that's how 0xa0 is classified in the locale.
However in the same locale, its sh (which is not multibyte
aware) outputs no in:

case $nbsb in
  [[:blank:][:space:]]) echo yes;;
  *) echo no
esac

(bash outputs yes for both blank and space as POSIX requires).

I don't think many people complained when multi-byte support was
added and English people were starting to have their [[:alpha:]]
match on Greek or Korean letters in addition to English ones
(fair enough as "alpha" means the first letter of the Greek
alphabet).

The main problem if we want to align with other shells and make
the shell POSIX compliant is that the documentation currently
states explicitely that  it matches on space and tab only.

The question is would any script be broken if we changed it?

People still keep using [a-z] when they mean to match English
lower case letters while in effect nowadays, except in zsh and a
very few other utilities that match ranges based on code points,
that matches on hundreds more (like à, œ, ć, if not ch, fi...), I
wouldn't be surprised if people use [[:alnum:]] thinking it only
matches on Latin letters without diacritics and Arabic decimal
degits.

But then again, that still works more or less for them, as they
use it anyway against text that only contains English data.

To me the correct way to do a strict match against ASCII blanks
(or English letters, or ASCII punctuations) would be to use the
C locale.

-- 
Stephane


^ permalink raw reply	[relevance 4%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  @ 2018-05-14 15:51  4%             ` Stephane Chazelas
  2018-05-14 16:31  0%               ` Sebastian Gniazdowski
  2018-05-15 19:06  4%               ` Oliver Kiddle
  0 siblings, 2 replies; 200+ results
From: Stephane Chazelas @ 2018-05-14 15:51 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: Zsh hackers list

2018-05-14 14:50:56 +0100, Peter Stephenson:
[...]
> It wouldn't be ridiculous to change the documentation for this case and
> require "unsetopt multibyte" for strict byte-by-byte comparisions, which
> is already how it works in the vast majority of other cases.
[...]

But note that here it's not about multibyte vs singlebyte but
whether [:blank:] honours the locale like the other POSIX
character classes (alpha, punct...) do.

There are locales on some systems (like NetBSD already
mentioned) that use a single-byte charset where more than SPC
and TAB are classified as "blank" (like 0xA0 (nbsp) in locales
using iso8859-x charsets or 0x9A in KOI8-R on NetBSD).

IMO, without the "multibyte" option, we should still call
isblank() which on most systems and most locales will match only
on SPC and TAB but is not guaranteed to (and does not in
practice like on NetBSD).

I just noticed that on NetBSD, in locales using UTF-8 or
GB18030, isblank() returns true on \v (vertical TAB), not in any
other locale! So does iswblank(). So out goes my claim that
"blank" should be for horizontal spaces. On OpenBSD (where only
UTF-8 charsets are supported in locales other than C/POSIX),
iswblank() matches on \v and \f. 

What a mess!

-- 
Stephane


^ permalink raw reply	[relevance 4%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-14 15:51  4%             ` Stephane Chazelas
@ 2018-05-14 16:31  0%               ` Sebastian Gniazdowski
  2018-05-14 16:50  3%                 ` Bart Schaefer
  2018-05-15 19:06  4%               ` Oliver Kiddle
  1 sibling, 1 reply; 200+ results
From: Sebastian Gniazdowski @ 2018-05-14 16:31 UTC (permalink / raw)
  To: Zsh hackers list

On 14 May 2018 at 17:51, Stephane Chazelas <stephane.chazelas@gmail.com> wrote:
> I just noticed that on NetBSD, in locales using UTF-8 or
> GB18030, isblank() returns true on \v (vertical TAB), not in any
> other locale! So does iswblank(). So out goes my claim that
> "blank" should be for horizontal spaces. On OpenBSD (where only
> UTF-8 charsets are supported in locales other than C/POSIX),
> iswblank() matches on \v and \f.
>
> What a mess!

Maybe seeing Zsh as a platform is a way. I suspect that the person
which decided to match non-horizontal space on NetBSD or OpenBSD via
:blank: was himself thinking in platform terms. So basically "in Zsh
world [[:blank:]] is ...". I feel that BSD coders like such
power-of-creation moments, "in NetBSD world it will be that way...".
Too bad character classes shine in control code, "if :space:, then,
else" and such play in a creator is actually very influential and long
term. So one option is to leave [[:blank:]] as it is, I would be happy
to use it in code and comfortably include tabs in various initially
0x20-space comparisons (BTW., [ $'\t'] doesn't work in [[ ... ]], the
space needs to be backslashed). The other way is to make it very
normal, without bumps. The other is to join systems that prevail in
numbers or market share.

-- 
Best regards,
Sebastian Gniazdowski


^ permalink raw reply	[relevance 0%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-14 16:31  0%               ` Sebastian Gniazdowski
@ 2018-05-14 16:50  3%                 ` Bart Schaefer
  2018-05-14 19:52  3%                   ` Daniel Tameling
  0 siblings, 1 reply; 200+ results
From: Bart Schaefer @ 2018-05-14 16:50 UTC (permalink / raw)
  To: Sebastian Gniazdowski; +Cc: Zsh hackers list

On Mon, May 14, 2018 at 9:31 AM, Sebastian Gniazdowski
<sgniazdowski@gmail.com> wrote:
>
> Maybe seeing Zsh as a platform is a way.

That only works for native zsh mode.  In other emulations, zsh ought
to behave either the way other shells on the same OS do, or as the
POSIX spec requires, to the extent that is both possible and sane.  In
this case we have to decide whether it is reasonable to make a
distinction.

> (BTW., [ $'\t'] doesn't work in [[ ... ]], the
> space needs to be backslashed).

Just put the space inside the quotes:  [$' \t']


^ permalink raw reply	[relevance 3%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-14 16:50  3%                 ` Bart Schaefer
@ 2018-05-14 19:52  3%                   ` Daniel Tameling
  2018-05-14 20:42  5%                     ` Stephane Chazelas
  0 siblings, 1 reply; 200+ results
From: Daniel Tameling @ 2018-05-14 19:52 UTC (permalink / raw)
  To: zsh-workers

Stephane already quoted some man pages, but here is what the C99/C11
standards say:

"The isblank function tests for any character that is a standard blank
character or is one of a locale-specific set of characters for which
isspace is true and that is used to separate words within a line of
text. The standard blank characters are the following: space (' '),
and horizontal tab ('\t'). In the "C" locale, isblank returns true
only for the standard blank characters."

And Posix seems to say the same: it defines blank for the C locale
and states that in other locales it should at least encompass space
and tab.

So in other locales it seems to be totally undefined what a blank is,
and everybody does what they think is good choice. Thus the mess
Stephane observed. In fact, I looked at the musl library and found
this code:
int isblank(int c)
{
	return (c == ' ' || c == '\t');
}
int __isblank_l(int c, locale_t l)
{
	return isblank(c);
}
So they completely ignore the locale and just use the bare minimum
required by the standard. So after the patch, zsh would not only
behave differently on different platforms but would also change it's
behavior if you link with a different libc. 

Nevertheless, I'm slightly in favour of the patch. While defining our
own :blank: for other locales might give us consistency across
platforms, I think it will end up to be different than what everybody
else does and will thus lead to unexpected results for users -- in
particular if the libc's start to agree on isblank for different
locales. And at that point, it might be difficult to change the
behavior if it breaks backward compatibility.

In fact, it's the hope that the situation will improve in the future
that sways me towards the patch compared to the status-quo. But seeing
the mess Stephane uncovered made it a very tight race.

Finally, whether the patch gets applied or not, the documentation
should definitely be updated to reflect the issues around :blank:.

-- 
Daniel


^ permalink raw reply	[relevance 3%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-14 19:52  3%                   ` Daniel Tameling
@ 2018-05-14 20:42  5%                     ` Stephane Chazelas
  2018-05-15 18:12  5%                       ` Stephane Chazelas
  0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2018-05-14 20:42 UTC (permalink / raw)
  To: zsh-workers

I agree with Daniel here.

Although I think I would prefer a [[:blank:]] that consistently
matches SPC and TAB rather than something completely random
ranging from SPC and TAB (the minimum required by POSIX) and
whatever [[:space:]] matches (POSIX requires [:blank:] to be a
subset of [:space:]), I don't think it's zsh's role to fix the
POSIX character classes.

There's also the question of the consistency between

[[ $x = [[:blank:]] ]] (using zsh's own pattern matching
implementation)

[[ $x =~ [[:blank:]] ]] (using the system's EREs, so generally
influenced by the locale)

and the same with rematchpcre, that one only matching SPC and TAB
regardless of the locale AFAICT with all the character classes
only matching the minimum ASCII characters required by POSIX (as
if using the C locale with wildcards or ERE).

-- 
Stephane


^ permalink raw reply	[relevance 5%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-14 20:42  5%                     ` Stephane Chazelas
@ 2018-05-15 18:12  5%                       ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2018-05-15 18:12 UTC (permalink / raw)
  To: zsh-workers

Note a "theoretical" problem with not doing iswblank() is that
[[:blank:]] is often used to parse the output of some commands.

When POSIX specifies the output of a command (generally only in
the POSIX locale) and that output has whitespace separated fields
(like in the output of id, ls -l, wc...) the separators are one
or more "blanks".

So we need to be able to match *those* "blanks" which are the
POSIX blanks.

Now in practice, I don't know of any current implementation of
any utility that would use anything but SPC or TAB in any
locale, so it's only a "theoretical" point.

Note that by some reading of the spec, and bash and yash have
made such readings, when the spec says tokens are delimited by
blanks, that's any blank in the locale.

$ yash -c $'echo\u2006test'
test

In the case of bash, that only works "properly" with single-byte
characters.

-- 
Stephane


^ permalink raw reply	[relevance 5%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-14 15:51  4%             ` Stephane Chazelas
  2018-05-14 16:31  0%               ` Sebastian Gniazdowski
@ 2018-05-15 19:06  4%               ` Oliver Kiddle
  2018-05-16 13:15  4%                 ` Stephane Chazelas
  1 sibling, 1 reply; 200+ results
From: Oliver Kiddle @ 2018-05-15 19:06 UTC (permalink / raw)
  To: Zsh hackers list

Stephane Chazelas wrote:
> [...]
> > It wouldn't be ridiculous to change the documentation for this case and
> > require "unsetopt multibyte" for strict byte-by-byte comparisions, which
> > is already how it works in the vast majority of other cases.
> [...]
>
> But note that here it's not about multibyte vs singlebyte but
> whether [:blank:] honours the locale like the other POSIX
> character classes (alpha, punct...) do.

For consistency with the other character classes, I think the best is to
follow POSIX and the other shells and have [:blank:] call iswblank().
That is apply the patch plus whatever change the documentation needs to
reflect it.

I can't see it actually breaking scripts in practice. We do at least
have the option of using [$' \t'] if we want and could add [[:BLANK:]]
or similar if needed. It does seem wrong for non-breaking spaces to be
matched but that's an issue for NetBSD or whatever.

This isn't as bad as the idiocy of [a-z] matching B-Z.

> What a mess!

Indeed.

I also wish POSIX would standardise an alternative for the C locale
that's UTF-8 aware and with ISO rather than US format dates.

Oliver


^ permalink raw reply	[relevance 4%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-15 19:06  4%               ` Oliver Kiddle
@ 2018-05-16 13:15  4%                 ` Stephane Chazelas
  2018-05-16 13:40  0%                   ` Peter Stephenson
  0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2018-05-16 13:15 UTC (permalink / raw)
  To: Oliver Kiddle; +Cc: Zsh hackers list

2018-05-15 21:06:01 +0200, Oliver Kiddle:
[...]
> For consistency with the other character classes, I think the best is to
> follow POSIX and the other shells and have [:blank:] call iswblank().
> That is apply the patch plus whatever change the documentation needs to
> reflect it.
[...]

3rd version of the patch with doc update and check for
isblank().

diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 8b447e2..c791097 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -2004,7 +2004,7 @@ The character is 7-bit, i.e. is a single-byte character without
 the top bit set.
 )
 item(tt([:blank:]))(
-The character is either space or tab
+The character is a blank character
 )
 item(tt([:cntrl:]))(
 The character is a control character
diff --git a/NEWS b/NEWS
index 1db9da6..1786897 100644
--- a/NEWS
+++ b/NEWS
@@ -4,7 +4,14 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH
 
 Note also the list of incompatibilities in the README file.
 
-Changes from %.5 to 5.5.1
+Changes from 5.5.1 to FIXME
+---------------------------
+
+In shell patterns, [[:blank:]] now honours the locale instead of
+matching exclusively on space and tab, like for the other POSIX
+character classes or in extended regular expressions.
+
+Changes from 5.5 to 5.5.1
 -------------------------
 
 Apart from a fix for a configuration problem finding singal names from
diff --git a/Src/pattern.c b/Src/pattern.c
index fc7c737..97a6d9c 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -3605,7 +3605,7 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp)
 		    return 1;
 		break;
 	    case PP_BLANK:
-		if (ch == L' ' || ch == L'\t')
+		if (iswblank(ch))
 		    return 1;
 		break;
 	    case PP_CNTRL:
@@ -3840,7 +3840,14 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp)
 		    return 1;
 		break;
 	    case PP_BLANK:
-		if (ch == ' ' || ch == '\t')
+#if !defined(HAVE_ISBLANK) && !defined(isblank)
+/*
+ * isblank() is GNU and C99. There's a remote chance that some
+ * systems still don't support it.
+ */
+#define isblank(c) (c == ' ' || c == '\t')
+#endif
+		if (isblank(ch))
 		    return 1;
 		break;
 	    case PP_CNTRL:
diff --git a/configure.ac b/configure.ac
index 4329afb..c2efda5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1304,6 +1304,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \
 	       memcpy memmove strstr strerror strtoul \
 	       getrlimit getrusage \
 	       setlocale \
+	       isblank \
 	       uname \
 	       signgam tgamma \
 	       scalbn \
@@ -2564,10 +2565,10 @@ AC_HELP_STRING([--enable-multibyte], [support multibyte characters]),
 [AC_CACHE_VAL(zsh_cv_c_unicode_support,
   AC_MSG_NOTICE([checking for functions supporting multibyte characters])
   [zfuncs_absent=
-   for zfunc in iswalnum iswcntrl iswdigit iswgraph iswlower iswprint \
-iswpunct iswspace iswupper iswxdigit mbrlen mbrtowc towupper towlower \
-wcschr wcscpy wcslen wcsncmp wcsncpy wcrtomb wcwidth wmemchr wmemcmp \
-wmemcpy wmemmove wmemset; do
+   for zfunc in iswalnum iswblank iswcntrl iswdigit iswgraph iswlower \
+     iswprint iswpunct iswspace iswupper iswxdigit mbrlen mbrtowc \
+     towupper towlower wcschr wcscpy wcslen wcsncmp wcsncpy wcrtomb \
+     wcwidth wmemchr wmemcmp wmemcpy wmemmove wmemset; do
      AC_CHECK_FUNC($zfunc,
      [:], [zfuncs_absent="$zfuncs_absent $zfunc"])
     done

-- 
Stephane


^ permalink raw reply	[relevance 4%]

* Re: [PATCH] [[:blank:]] only matches on SPC and TAB
  2018-05-16 13:15  4%                 ` Stephane Chazelas
@ 2018-05-16 13:40  0%                   ` Peter Stephenson
    0 siblings, 1 reply; 200+ results
From: Peter Stephenson @ 2018-05-16 13:40 UTC (permalink / raw)
  To: Stephane Chazelas, Zsh hackers list

On Wed, 16 May 2018 14:15:47 +0100
Stephane Chazelas <stephane.chazelas@gmail.com> wrote:
> 2018-05-15 21:06:01 +0200, Oliver Kiddle:
> [...]
> > For consistency with the other character classes, I think the best
> > is to follow POSIX and the other shells and have [:blank:] call
> > iswblank(). That is apply the patch plus whatever change the
> > documentation needs to reflect it.  
> [...]
> 
> 3rd version of the patch with doc update and check for
> isblank().

Probably slightly better with the patch than without, in an imperfect world.

Is iswblank() guaranteed to be available?  It's covered by an extra set
of #ifdef's compared with the isblank() case but none of them is forcing
it to use C99 standard headers.

pws


^ permalink raw reply	[relevance 0%]

* Re: [PATCH v4] [[:blank:]] only matches on SPC and TAB
  @ 2018-05-16 21:02  4%                       ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2018-05-16 21:02 UTC (permalink / raw)
  To: Peter Stephenson, Zsh hackers list

2018-05-16 17:31:19 +0100, Stephane Chazelas:
[...]
> > Is iswblank() guaranteed to be available?  It's covered by an extra set
> > of #ifdef's compared with the isblank() case but none of them is forcing
> > it to use C99 standard headers.
[...] 

I have to admit I'm not sure what you mean by that. And those
are the kind of thing I'm not very familiar with. AFAICT, the
AC_CHECK_FUNCS() checks that the iswblank symbol is available in
the libc. And Src/zsh_system.h looks like it should enable
enough of the feature test macros for the system headers to
expose it, but I may very well misunderstand things.

> In that v3 patch, I've added iswblank() in the list of functions
> to check before enabling "unicode support". Maybe we should do
> like for isblank() so that we can still have unicode support if
> iswalpha()... are present but not iswblank() (and have
> iswblank() check for spc and tab only then).
> 
> OK, I'll send a v4 patch tonight.


diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index 8b447e2..c791097 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -2004,7 +2004,7 @@ The character is 7-bit, i.e. is a single-byte character without
 the top bit set.
 )
 item(tt([:blank:]))(
-The character is either space or tab
+The character is a blank character
 )
 item(tt([:cntrl:]))(
 The character is a control character
diff --git a/NEWS b/NEWS
index 1db9da6..1786897 100644
--- a/NEWS
+++ b/NEWS
@@ -4,7 +4,14 @@ CHANGES FROM PREVIOUS VERSIONS OF ZSH
 
 Note also the list of incompatibilities in the README file.
 
-Changes from %.5 to 5.5.1
+Changes from 5.5.1 to FIXME
+---------------------------
+
+In shell patterns, [[:blank:]] now honours the locale instead of
+matching exclusively on space and tab, like for the other POSIX
+character classes or for extended regular expressions.
+
+Changes from 5.5 to 5.5.1
 -------------------------
 
 Apart from a fix for a configuration problem finding singal names from
diff --git a/Src/pattern.c b/Src/pattern.c
index fc7c737..737f5cd 100644
--- a/Src/pattern.c
+++ b/Src/pattern.c
@@ -3605,7 +3605,15 @@ mb_patmatchrange(char *range, wchar_t ch, int zmb_ind, wint_t *indptr, int *mtp)
 		    return 1;
 		break;
 	    case PP_BLANK:
-		if (ch == L' ' || ch == L'\t')
+#if !defined(HAVE_ISWBLANK) && !defined(iswblank)
+/*
+ * iswblank() is GNU and C99. There's a remote chance that some
+ * systems still don't support it (but would support the other ones
+ * if MULTIBYTE_SUPPORT is enabled).
+ */
+#define iswblank(c) (c == L' ' || c == L'\t')
+#endif
+		if (iswblank(ch))
 		    return 1;
 		break;
 	    case PP_CNTRL:
@@ -3840,7 +3848,14 @@ patmatchrange(char *range, int ch, int *indptr, int *mtp)
 		    return 1;
 		break;
 	    case PP_BLANK:
-		if (ch == ' ' || ch == '\t')
+#if !defined(HAVE_ISBLANK) && !defined(isblank)
+/*
+ * isblank() is GNU and C99. There's a remote chance that some
+ * systems still don't support it.
+ */
+#define isblank(c) (c == ' ' || c == '\t')
+#endif
+		if (isblank(ch))
 		    return 1;
 		break;
 	    case PP_CNTRL:
diff --git a/configure.ac b/configure.ac
index 4329afb..00c7318 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1304,6 +1304,7 @@ AC_CHECK_FUNCS(strftime strptime mktime timelocal \
 	       memcpy memmove strstr strerror strtoul \
 	       getrlimit getrusage \
 	       setlocale \
+	       isblank iswblank \
 	       uname \
 	       signgam tgamma \
 	       scalbn \
@@ -2564,6 +2565,12 @@ AC_HELP_STRING([--enable-multibyte], [support multibyte characters]),
 [AC_CACHE_VAL(zsh_cv_c_unicode_support,
   AC_MSG_NOTICE([checking for functions supporting multibyte characters])
   [zfuncs_absent=
+dnl
+dnl Note that iswblank is not included and checked separately.
+dnl As iswblank() was added to C long after the others, we still
+dnl want to enabled unicode support even if iswblank is not available
+dnl (we then just do the SPC+TAB approximation)
+dnl
    for zfunc in iswalnum iswcntrl iswdigit iswgraph iswlower iswprint \
 iswpunct iswspace iswupper iswxdigit mbrlen mbrtowc towupper towlower \
 wcschr wcscpy wcslen wcsncmp wcsncpy wcrtomb wcwidth wmemchr wmemcmp \

-- 
Stephane


^ permalink raw reply	[relevance 4%]

* [PATCH 1/2] autoconf: run autoupdate
@ 2018-05-27  2:51  3% Eitan Adler
  0 siblings, 0 replies; 200+ results
From: Eitan Adler @ 2018-05-27  2:51 UTC (permalink / raw)
  To: zsh-workers; +Cc: Eitan Adler

This brings us in line with a slightly more modern build system

Signed-off-by: Eitan Adler <lists@eitanadler.com>
---
 configure.ac | 412 +++++++++++++++++++--------------------------------
 1 file changed, 149 insertions(+), 263 deletions(-)

diff --git a/configure.ac b/configure.ac
index 00c731864..c4d3889a3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,8 +25,9 @@ dnl  Zsh Development Group have no obligation to provide maintenance,
 dnl  support, updates, enhancements, or modifications.
 dnl
 
-AC_INIT(Src/zsh.h)
-AC_PREREQ(2.59c)
+AC_INIT
+AC_CONFIG_SRCDIR([Src/zsh.h])
+AC_PREREQ([2.69])
 AC_CONFIG_HEADER(config.h)
 
 dnl What version of zsh are we building ?
@@ -60,7 +61,7 @@ ifdef([zsh-debug],[undefine([zsh-debug])])dnl
 AH_TEMPLATE([DEBUG],
 [Define to 1 if you want to debug zsh.])
 AC_ARG_ENABLE(zsh-debug,
-AC_HELP_STRING([--enable-zsh-debug], [compile with debug code and debugger symbols]),
+AS_HELP_STRING([--enable-zsh-debug],[compile with debug code and debugger symbols]),
 [if test x$enableval = xyes; then
   AC_DEFINE(DEBUG)
 fi])
@@ -70,7 +71,7 @@ ifdef([zsh-mem],[undefine([zsh-mem])])dnl
 AH_TEMPLATE([ZSH_MEM],
 [Define to 1 if you want to use zsh's own memory allocation routines])
 AC_ARG_ENABLE(zsh-mem,
-AC_HELP_STRING([--enable-zsh-mem], [compile with zsh memory allocation routines]),
+AS_HELP_STRING([--enable-zsh-mem],[compile with zsh memory allocation routines]),
 [if test x$enableval = xyes; then
   AC_DEFINE(ZSH_MEM)
 fi])
@@ -80,7 +81,7 @@ ifdef([zsh-mem-debug],[undefine([zsh-mem-debug])])dnl
 AH_TEMPLATE([ZSH_MEM_DEBUG],
 [Define to 1 if you want to debug zsh memory allocation routines.])
 AC_ARG_ENABLE(zsh-mem-debug,
-AC_HELP_STRING([--enable-zsh-mem-debug], [debug zsh memory allocation routines]),
+AS_HELP_STRING([--enable-zsh-mem-debug],[debug zsh memory allocation routines]),
 [if test x$enableval = xyes; then
   AC_DEFINE(ZSH_MEM_DEBUG)
 fi])
@@ -90,7 +91,7 @@ AH_TEMPLATE([ZSH_MEM_WARNING],
 [Define to 1 if you want to turn on warnings of memory allocation errors])
 ifdef([zsh-mem-warning],[undefine([zsh-mem-warning])])dnl
 AC_ARG_ENABLE(zsh-mem-warning,
-AC_HELP_STRING([--enable-zsh-mem-warning], [print warnings for errors in memory allocation]),
+AS_HELP_STRING([--enable-zsh-mem-warning],[print warnings for errors in memory allocation]),
 [if test x$enableval = xyes; then
   AC_DEFINE(ZSH_MEM_WARNING)
 fi])
@@ -100,7 +101,7 @@ ifdef([zsh-secure-free],[undefine([zsh-secure-free])])dnl
 AH_TEMPLATE([ZSH_SECURE_FREE],
 [Define to 1 if you want to turn on memory checking for free().])
 AC_ARG_ENABLE(zsh-secure-free,
-AC_HELP_STRING([--enable-zsh-secure-free], [turn on error checking for free()]),
+AS_HELP_STRING([--enable-zsh-secure-free],[turn on error checking for free()]),
 [if test x$enableval = xyes; then
   AC_DEFINE(ZSH_SECURE_FREE)
 fi])
@@ -111,8 +112,7 @@ ifdef([zsh-heap-debug],[undefine([zsh-heap-debug])])dnl
 AH_TEMPLATE([ZSH_HEAP_DEBUG],
 [Define to 1 if you want to turn on error checking for heap allocation.])
 AC_ARG_ENABLE(zsh-heap-debug,
-AC_HELP_STRING([--enable-zsh-heap-debug],
-[turn on error checking for heap allocation]),
+AS_HELP_STRING([--enable-zsh-heap-debug],[turn on error checking for heap allocation]),
 [if test x$enableval = xyes; then
   AC_DEFINE(ZSH_HEAP_DEBUG)
 fi])
@@ -122,8 +122,7 @@ ifdef([zsh-valgrind],[undefine([zsh-valgrind])])dnl
 AH_TEMPLATE([ZSH_VALGRIND],
 [Define to 1 if you want to add code for valgrind to debug heap memory.])
 AC_ARG_ENABLE(zsh-valgrind,
-AC_HELP_STRING([--enable-zsh-valgrind],
-[turn on support for valgrind debugging of heap memory]),
+AS_HELP_STRING([--enable-zsh-valgrind],[turn on support for valgrind debugging of heap memory]),
 [if test x$enableval = xyes;  then
   AC_DEFINE(ZSH_VALGRIND)
 fi])
@@ -135,7 +134,7 @@ AH_TEMPLATE([ZSH_HASH_DEBUG],
 [Define to 1 if you want to get debugging information on internal
  hash tables.  This turns on the `hashinfo' builtin.])
 AC_ARG_ENABLE(zsh-hash-debug,
-AC_HELP_STRING([--enable-zsh-hash-debug], [turn on debugging of internal hash tables]),
+AS_HELP_STRING([--enable-zsh-hash-debug],[turn on debugging of internal hash tables]),
 [if test x$enableval = xyes; then
   AC_DEFINE(ZSH_HASH_DEBUG)
 fi])
@@ -145,7 +144,7 @@ ifdef([stack-allocation],[undefine([stack-allocation])])dnl
 AH_TEMPLATE([USE_STACK_ALLOCATION],
 [Define to 1 if you want to allocate stack memory e.g. with `alloca'.])
 AC_ARG_ENABLE(stack-allocation,
-AC_HELP_STRING([--enable-stack-allocation], [allocate stack memory e.g. with `alloca']),
+AS_HELP_STRING([--enable-stack-allocation],[allocate stack memory e.g. with `alloca']),
 [if test x$enableval = xyes; then
   AC_DEFINE(USE_STACK_ALLOCATION)
 fi])
@@ -153,12 +152,12 @@ fi])
 dnl Pathnames for global zsh scripts
 ifdef([etcdir],[undefine([etcdir])])dnl
 AC_ARG_ENABLE(etcdir,
-AC_HELP_STRING([--enable-etcdir=DIR], [the default directory for global zsh scripts]),
+AS_HELP_STRING([--enable-etcdir=DIR],[the default directory for global zsh scripts]),
 [etcdir="$enableval"], [etcdir=/etc])
 
 ifdef([zshenv],[undefine([zshenv])])dnl
 AC_ARG_ENABLE(zshenv,
-AC_HELP_STRING([--enable-zshenv=FILE], [the full pathname of the global zshenv script]),
+AS_HELP_STRING([--enable-zshenv=FILE],[the full pathname of the global zshenv script]),
 [zshenv="$enableval"],
 [if test "x$etcdir" = xno; then
   zshenv=no
@@ -174,7 +173,7 @@ fi
 
 ifdef([zshrc],[undefine([zshrc])])dnl
 AC_ARG_ENABLE(zshrc,
-AC_HELP_STRING([--enable-zshrc=FILE], [the full pathname of the global zshrc script]),
+AS_HELP_STRING([--enable-zshrc=FILE],[the full pathname of the global zshrc script]),
 [zshrc="$enableval"],
 [if test "x$etcdir" = xno; then
   zshrc=no
@@ -190,7 +189,7 @@ fi
 
 ifdef([zprofile],[undefine([zprofile])])dnl
 AC_ARG_ENABLE(zprofile,
-AC_HELP_STRING([--enable-zprofile=FILE], [the full pathname of the global zprofile script]),
+AS_HELP_STRING([--enable-zprofile=FILE],[the full pathname of the global zprofile script]),
 [zprofile="$enableval"],
 [if test "x$etcdir" = xno; then
   zprofile=no
@@ -206,7 +205,7 @@ fi
 
 ifdef([zlogin],[undefine([zlogin])])dnl
 AC_ARG_ENABLE(zlogin,
-AC_HELP_STRING([--enable-zlogin=FILE], [the full pathname of the global zlogin script]),
+AS_HELP_STRING([--enable-zlogin=FILE],[the full pathname of the global zlogin script]),
 [zlogin="$enableval"],
 [if test "x$etcdir" = xno; then
   zlogin=no
@@ -222,7 +221,7 @@ fi
 
 ifdef([zlogout],[undefine([zlogout])])dnl
 AC_ARG_ENABLE(zlogout,
-AC_HELP_STRING([--enable-zlogout=FILE], [the full pathname of the global zlogout script]),
+AS_HELP_STRING([--enable-zlogout=FILE],[the full pathname of the global zlogout script]),
 [zlogout="$enableval"],
 [if test "x$etcdir" = xno; then
   zlogout=no
@@ -246,7 +245,7 @@ AC_SUBST(zlogout)dnl
 dnl Do you want dynamically loaded binary modules.
 ifdef([dynamic],[undefine([dynamic])])dnl
 AC_ARG_ENABLE(dynamic,
-AC_HELP_STRING([--disable-dynamic], [turn off dynamically loaded binary modules]),
+AS_HELP_STRING([--disable-dynamic],[turn off dynamically loaded binary modules]),
 [dynamic="$enableval"], [dynamic=yes])
 
 dnl Do you want to disable restricted on r* commands
@@ -256,7 +255,7 @@ AH_TEMPLATE([RESTRICTED_R],
  when zsh is exec'd with basename that starts with r.
  By default this is defined.])
 AC_ARG_ENABLE(restricted-r,
-AC_HELP_STRING([--disable-restricted-r], [turn off r* invocation for restricted shell]),
+AS_HELP_STRING([--disable-restricted-r],[turn off r* invocation for restricted shell]),
 [if test x$enableval = xyes; then
   AC_DEFINE(RESTRICTED_R)
 fi],
@@ -267,7 +266,7 @@ dnl Do you want to disable use of locale functions
 AH_TEMPLATE([CONFIG_LOCALE],
 [Undefine if you don't want local features.  By default this is defined.])
 AC_ARG_ENABLE([locale],
-AC_HELP_STRING([--disable-locale], [turn off locale features]),
+AS_HELP_STRING([--disable-locale],[turn off locale features]),
 [if test x$enableval = xyes; then
   AC_DEFINE(CONFIG_LOCALE)
 fi],
@@ -276,12 +275,12 @@ AC_DEFINE(CONFIG_LOCALE)
 
 dnl Do you want to compile as K&R C.
 AC_ARG_ENABLE(ansi2knr,
-AC_HELP_STRING([--enable-ansi2knr], [translate source to K&R C before compiling]),
+AS_HELP_STRING([--enable-ansi2knr],[translate source to K&R C before compiling]),
 [ansi2knr="$enableval"], [ansi2knr=default])
 
 ifdef([runhelpdir],[undefine([runhelpdir])])dnl
 AC_ARG_ENABLE(runhelpdir,
-AC_HELP_STRING([--enable-runhelpdir=DIR], [the directory in which to install run-help files]),
+AS_HELP_STRING([--enable-runhelpdir=DIR],[the directory in which to install run-help files]),
 [if test x"$enableval" = xno; then
   runhelpdir=
 else
@@ -298,7 +297,7 @@ fi
 
 ifdef([fndir],[undefine([fndir])])dnl
 AC_ARG_ENABLE(fndir,
-AC_HELP_STRING([--enable-fndir=DIR], [the directory in which to install functions]),
+AS_HELP_STRING([--enable-fndir=DIR],[the directory in which to install functions]),
 dnl ${VERSION} to be determined at compile time.
 [if test x$enableval = xyes; then
   fndir=${datadir}/${tzsh_name}/'${VERSION}'/functions
@@ -308,7 +307,7 @@ fi], [fndir=${datadir}/${tzsh_name}/'${VERSION}'/functions])
 
 ifdef([sitefndir],[undefine([sitefndir])])dnl
 AC_ARG_ENABLE(site-fndir,
-AC_HELP_STRING([--enable-site-fndir=DIR], [same for site functions (not version specific)]),
+AS_HELP_STRING([--enable-site-fndir=DIR],[same for site functions (not version specific)]),
 [if test x$enableval = xyes; then
   sitefndir=${datadir}/${tzsh_name}/site-functions
 else
@@ -336,7 +335,7 @@ fi
 
 ifdef([function_subdirs],[undefine([function_subdirs])])
 AC_ARG_ENABLE(function-subdirs,
-AC_HELP_STRING([--enable-function-subdirs], [install functions in subdirectories]))
+AS_HELP_STRING([--enable-function-subdirs],[install functions in subdirectories]))
 
 if test "x${enable_function_subdirs}" != x &&
   test "x${enable_function_subdirs}" != xno; then
@@ -347,7 +346,7 @@ fi
 
 ifdef([additionalfpath],[undefine([additionalfpath])])dnl
 AC_ARG_ENABLE(additional-fpath,
-AC_HELP_STRING([--enable-additional-fpath=DIR], [add directories to default function path]),
+AS_HELP_STRING([--enable-additional-fpath=DIR],[add directories to default function path]),
 [if test x$enableval = xyes; then
   additionalfpath=""
 else
@@ -366,7 +365,7 @@ dnl Directories for scripts such as newuser.
 
 ifdef([scriptdir],[undefine([scriptdir])])dnl
 AC_ARG_ENABLE(scriptdir,
-AC_HELP_STRING([--enable-scriptdir=DIR], [the directory in which to install scripts]),
+AS_HELP_STRING([--enable-scriptdir=DIR],[the directory in which to install scripts]),
 dnl ${VERSION} to be determined at compile time.
 [if test x$enableval = xyes; then
   scriptdir=${datadir}/${tzsh_name}/'${VERSION}'/scripts
@@ -376,7 +375,7 @@ fi], [scriptdir=${datadir}/${tzsh_name}/'${VERSION}'/scripts])
 
 ifdef([sitescriptdir],[undefine([sitescriptdir])])dnl
 AC_ARG_ENABLE(site-scriptdir,
-AC_HELP_STRING([--enable-site-scriptdir=DIR], [same for site scripts (not version specific)]),
+AS_HELP_STRING([--enable-site-scriptdir=DIR],[same for site scripts (not version specific)]),
 [if test x$enableval = xyes; then
   sitescriptdir=${datadir}/${tzsh_name}/scripts
 else
@@ -395,7 +394,7 @@ fi
 AH_TEMPLATE([CUSTOM_PATCHLEVEL],
 [Define to a custom value for the ZSH_PATCHLEVEL parameter])
 AC_ARG_ENABLE(custom-patchlevel,
-AC_HELP_STRING([--enable-custom-patchlevel], [set a custom ZSH_PATCHLEVEL value]),
+AS_HELP_STRING([--enable-custom-patchlevel],[set a custom ZSH_PATCHLEVEL value]),
 [if test x$enableval != x && test x$enableval != xno; then
   AC_DEFINE_UNQUOTED([CUSTOM_PATCHLEVEL], ["$enableval"])
 fi])
@@ -405,7 +404,7 @@ ifdef([maildir_support],[undefine([maildir_support])])dnl
 AH_TEMPLATE([MAILDIR_SUPPORT],
 [Define for Maildir support])
 AC_ARG_ENABLE(maildir-support,
-AC_HELP_STRING([--enable-maildir-support], [enable maildir support in MAIL and MAILPATH]),
+AS_HELP_STRING([--enable-maildir-support],[enable maildir support in MAIL and MAILPATH]),
 [if test x$enableval = xyes; then
   AC_DEFINE(MAILDIR_SUPPORT)
 fi])
@@ -415,7 +414,7 @@ ifdef([max_function_depth],[undefine([max_function_depth])])dnl
 AH_TEMPLATE([MAX_FUNCTION_DEPTH],
 [Define for function depth limits])
 AC_ARG_ENABLE(max-function-depth,
-AC_HELP_STRING([--enable-max-function-depth=MAX], [limit function depth to MAX, default 500]),
+AS_HELP_STRING([--enable-max-function-depth=MAX],[limit function depth to MAX, default 500]),
 [if test x$enableval = xyes; then
   AC_DEFINE(MAX_FUNCTION_DEPTH, 500)
 elif test x$enableval != xno; then
@@ -428,7 +427,7 @@ ifdef([default_readnullcmd],[undefine([default_readnullcmd])])dnl
 AH_TEMPLATE([DEFAULT_READNULLCMD],
 [Define default pager used by readnullcmd])
 AC_ARG_ENABLE(readnullcmd,
-AC_HELP_STRING([--enable-readnullcmd=PAGER], [pager used when READNULLCMD is not set]),
+AS_HELP_STRING([--enable-readnullcmd=PAGER],[pager used when READNULLCMD is not set]),
 [if test x$enableval = xyes; then
   AC_DEFINE(DEFAULT_READNULLCMD,"more")
 elif test x$enableval != xno; then
@@ -439,16 +438,14 @@ fi],
 
 dnl Do you want to look for pcre support?
 AC_ARG_ENABLE(pcre,
-AC_HELP_STRING([--enable-pcre],
-[enable the search for the pcre library (may create run-time library dependencies)]))
+AS_HELP_STRING([--enable-pcre],[enable the search for the pcre library (may create run-time library dependencies)]))
 
 dnl Do you want to look for capability support?
 AC_ARG_ENABLE(cap,
-AC_HELP_STRING([--enable-cap],
-[enable the search for POSIX capabilities (may require additional headers to be added by hand)]))
+AS_HELP_STRING([--enable-cap],[enable the search for POSIX capabilities (may require additional headers to be added by hand)]))
 
 AC_ARG_ENABLE(gdbm,
-AC_HELP_STRING([--disable-gdbm], [turn off search for gdbm library]),
+AS_HELP_STRING([--disable-gdbm],[turn off search for gdbm library]),
 [gdbm="$enableval"], [gdbm=yes])
 
 dnl ------------------
@@ -578,9 +575,7 @@ AC_FUNC_ALLOCA              dnl Check how to get `alloca'.
 dnl If the compiler supports union initialisation
 AC_CACHE_CHECK(if the compiler supports union initialisation,
 zsh_cv_c_have_union_init,
-[AC_TRY_COMPILE([union{void *p;long l;}u={0};], [u.l=1;],
-  zsh_cv_c_have_union_init=yes,
-  zsh_cv_c_have_union_init=no)])
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[union{void *p;long l;}u={0};]], [[u.l=1;]])],[zsh_cv_c_have_union_init=yes],[zsh_cv_c_have_union_init=no])])
 AH_TEMPLATE([HAVE_UNION_INIT],
 [Define to 1 if the compiler can initialise a union.])
 if test x$zsh_cv_c_have_union_init = xyes; then
@@ -590,10 +585,7 @@ fi
 dnl  Checking if compiler correctly cast signed to unsigned.
 AC_CACHE_CHECK(if signed to unsigned casting is broken,
 zsh_cv_c_broken_signed_to_unsigned_casting,
-[AC_TRY_RUN([main(){return((int)(unsigned char)((char) -1) == 255);}],
-  zsh_cv_c_broken_signed_to_unsigned_casting=yes,
-  zsh_cv_c_broken_signed_to_unsigned_casting=no,
-  zsh_cv_c_broken_signed_to_unsigned_casting=no)])
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[main(){return((int)(unsigned char)((char) -1) == 255);}]])],[zsh_cv_c_broken_signed_to_unsigned_casting=yes],[zsh_cv_c_broken_signed_to_unsigned_casting=no],[zsh_cv_c_broken_signed_to_unsigned_casting=no])])
 AH_TEMPLATE([BROKEN_SIGNED_TO_UNSIGNED_CASTING],
 [Define to 1 if compiler incorrectly cast signed to unsigned.])
 if test x$zsh_cv_c_broken_signed_to_unsigned_casting = xyes; then
@@ -603,9 +595,7 @@ fi
 dnl Checking if the compiler supports variable-length arrays
 AC_CACHE_CHECK(if the compiler supports variable-length arrays,
 zsh_cv_c_variable_length_arrays,
-[AC_TRY_COMPILE([int foo(), n;], [int i[foo()], a[n+1];],
-  zsh_cv_c_variable_length_arrays=yes,
-  zsh_cv_c_variable_length_arrays=no)])
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[int foo(), n;]], [[int i[foo()], a[n+1];]])],[zsh_cv_c_variable_length_arrays=yes],[zsh_cv_c_variable_length_arrays=no])])
 AH_TEMPLATE([HAVE_VARIABLE_LENGTH_ARRAYS],
 [Define to 1 if compiler supports variable-length arrays])
 if test x$zsh_cv_c_variable_length_arrays = xyes; then
@@ -704,10 +694,8 @@ AH_TEMPLATE([TIME_H_SELECT_H_CONFLICTS],
 if test x$ac_cv_header_sys_time_h = xyes && test x$ac_cv_header_sys_select_h = xyes; then
   AC_CACHE_CHECK(for conflicts in sys/time.h and sys/select.h,
   zsh_cv_header_time_h_select_h_conflicts,
-  [AC_TRY_COMPILE([#include <sys/time.h>
-#include <sys/select.h>], [int i;],
-  zsh_cv_header_time_h_select_h_conflicts=no,
-  zsh_cv_header_time_h_select_h_conflicts=yes)])
+  [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/time.h>
+#include <sys/select.h>]], [[int i;]])],[zsh_cv_header_time_h_select_h_conflicts=no],[zsh_cv_header_time_h_select_h_conflicts=yes])])
   if test x$zsh_cv_header_time_h_select_h_conflicts = xyes; then
     AC_DEFINE(TIME_H_SELECT_H_CONFLICTS)
   fi
@@ -718,28 +706,22 @@ AH_TEMPLATE([GWINSZ_IN_SYS_IOCTL],
 if test x$ac_cv_header_termios_h = xyes; then
   AC_CACHE_CHECK(TIOCGWINSZ in termios.h,
   zsh_cv_header_termios_h_tiocgwinsz,
-  [AC_TRY_LINK([
+  [AC_LINK_IFELSE([AC_LANG_PROGRAM([[
 #ifdef HAVE_SYS_TYPES_H
 # include <sys/types.h>
 #endif
-#include <termios.h>],
-  [int x = TIOCGWINSZ;],
-  zsh_cv_header_termios_h_tiocgwinsz=yes,
-  zsh_cv_header_termios_h_tiocgwinsz=no)])
+#include <termios.h>]], [[int x = TIOCGWINSZ;]])],[zsh_cv_header_termios_h_tiocgwinsz=yes],[zsh_cv_header_termios_h_tiocgwinsz=no])])
 else
   zsh_cv_header_termios_h_tiocgwinsz=no
 fi
 if test x$zsh_cv_header_termios_h_tiocgwinsz = xno; then
   AC_CACHE_CHECK(TIOCGWINSZ in sys/ioctl.h,
   zsh_cv_header_sys_ioctl_h_tiocgwinsz,
-  [AC_TRY_LINK([
+  [AC_LINK_IFELSE([AC_LANG_PROGRAM([[
 #ifdef HAVE_SYS_TYPES_H
 # include <sys/types.h>
 #endif
-#include <sys/ioctl.h>],
-  [int x = TIOCGWINSZ;],
-  zsh_cv_header_sys_ioctl_h_tiocgwinsz=yes,
-  zsh_cv_header_sys_ioctl_h_tiocgwinsz=no)])
+#include <sys/ioctl.h>]], [[int x = TIOCGWINSZ;]])],[zsh_cv_header_sys_ioctl_h_tiocgwinsz=yes],[zsh_cv_header_sys_ioctl_h_tiocgwinsz=no])])
   if test x$zsh_cv_header_sys_ioctl_h_tiocgwinsz = xyes; then
     AC_DEFINE(GWINSZ_IN_SYS_IOCTL)
   fi
@@ -749,11 +731,8 @@ AH_TEMPLATE([WINSIZE_IN_PTEM],
 [Define if your should include sys/stream.h and sys/ptem.h.])
 AC_CACHE_CHECK(for streams headers including struct winsize,
 ac_cv_winsize_in_ptem,
-[AC_TRY_COMPILE([#include <sys/stream.h>
-#include <sys/ptem.h>],
-[struct winsize wsz],
-ac_cv_winsize_in_ptem=yes,
-ac_cv_winsize_in_ptem=no)])
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/stream.h>
+#include <sys/ptem.h>]], [[struct winsize wsz]])],[ac_cv_winsize_in_ptem=yes],[ac_cv_winsize_in_ptem=no])])
 if test x$ac_cv_winsize_in_ptem = xyes; then
   AC_DEFINE(WINSIZE_IN_PTEM)
 fi
@@ -791,7 +770,7 @@ dnl is ncurses or curses.
 dnl On pre-11.11 HPUX, Hcurses is reported to work better than curses.
 dnl Prefer ncurses to curses on all systems.  tinfo isn't very common now.
 AC_ARG_WITH(term-lib,
-AC_HELP_STRING([--with-term-lib=LIBS], [search space-separated LIBS for terminal handling]),
+AS_HELP_STRING([--with-term-lib=LIBS],[search space-separated LIBS for terminal handling]),
 [if test "x$withval" != xno && test "x$withval" != x ; then
   termcap_curses_order="$withval"
   AC_SEARCH_LIBS(tigetstr, [$termcap_curses_order])
@@ -838,11 +817,9 @@ need to install a package called 'curses-devel' or 'ncurses-devel' on your
 system."], 255))
 AC_CHECK_HEADERS(curses.h, [],
 [AC_CACHE_CHECK(for Solaris 8 curses.h mistake, ac_cv_header_curses_solaris,
-AC_TRY_COMPILE([#include <curses.h>], [],
-[ac_cv_header_curses_h=yes
-ac_cv_header_curses_solaris=yes],
-ac_cv_header_curses_h=no
-ac_cv_header_curses_solaris=no))
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <curses.h>]], [[]])],[ac_cv_header_curses_h=yes
+ac_cv_header_curses_solaris=yes],[ac_cv_header_curses_h=no
+ac_cv_header_curses_solaris=no]))
 if test x$ac_cv_header_curses_solaris = xyes; then
 AC_DEFINE(HAVE_CURSES_H)
 fi])
@@ -909,9 +886,7 @@ AH_TEMPLATE([ICONV_FROM_LIBICONV],
 [Define to 1 if iconv() is linked from libiconv])
 if test "x$ac_found_iconv" = xyes; then
   AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.])
-  AC_TRY_LINK([#include <iconv.h>],
-    [int myversion = _libiconv_version],
-    AC_DEFINE(ICONV_FROM_LIBICONV), )
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <iconv.h>]], [[int myversion = _libiconv_version]])],[AC_DEFINE(ICONV_FROM_LIBICONV)],[])
 fi
 
 dnl Check if iconv uses const in prototype declaration
@@ -945,23 +920,18 @@ dnl ---------------------
 dnl Checks for external variable ospeed in the termcap library.
 AC_CACHE_CHECK(if an include file defines ospeed,
 zsh_cv_decl_ospeed_include_defines,
-[AC_TRY_LINK(
-[#include <sys/types.h>
+[AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <sys/types.h>
 #if HAVE_TERMIOS_H
 #include <termios.h>
 #endif
 #if HAVE_TERMCAP_H
 #include <termcap.h>
-#endif], [ospeed = 0;],
-zsh_cv_decl_ospeed_include_defines=yes,
-zsh_cv_decl_ospeed_include_defines=no)])
+#endif]], [[ospeed = 0;]])],[zsh_cv_decl_ospeed_include_defines=yes],[zsh_cv_decl_ospeed_include_defines=no])])
 
 if test x$zsh_cv_decl_ospeed_include_defines = xno; then
   AC_CACHE_CHECK(if you must define ospeed,
   zsh_cv_decl_ospeed_must_define,
-  [AC_TRY_LINK( ,[extern short ospeed; ospeed = 0;],
-  zsh_cv_decl_ospeed_must_define=yes,
-  zsh_cv_decl_ospeed_must_define=no)])
+  [AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[extern short ospeed; ospeed = 0;]])],[zsh_cv_decl_ospeed_must_define=yes],[zsh_cv_decl_ospeed_must_define=no])])
 fi
 
 AH_TEMPLATE([HAVE_OSPEED],
@@ -986,7 +956,18 @@ dnl --------------
 dnl CHECK TYPEDEFS
 dnl --------------
 
-AC_TYPE_SIGNAL
+AC_DIAGNOSE([obsolete],[your code may safely assume C89 semantics that RETSIGTYPE is void.
+Remove this warning and the `AC_CACHE_CHECK' when you adjust the code.])dnl
+AC_CACHE_CHECK([return type of signal handlers],[ac_cv_type_signal],[AC_COMPILE_IFELSE(
+[AC_LANG_PROGRAM([#include <sys/types.h>
+#include <signal.h>
+],
+		 [return *(signal (0, 0)) (0) == 1;])],
+		   [ac_cv_type_signal=int],
+		   [ac_cv_type_signal=void])])
+AC_DEFINE_UNQUOTED([RETSIGTYPE],[$ac_cv_type_signal],[Define as the return type of signal handlers
+		    (`int' or `void').])
+
 AC_TYPE_PID_T
 AC_TYPE_OFF_T
 AC_CHECK_TYPE(ino_t, unsigned long)
@@ -1000,10 +981,7 @@ dnl ------------------------------------------------
 dnl AC_CHECK_SIZEOF is no good, because we need the result here,
 dnl and that doesn't seem to define a shell parameter.
 AC_CACHE_CHECK(if long is 64 bits, zsh_cv_long_is_64_bit,
-[AC_TRY_RUN([int main() { return sizeof(long) < 8; }],
-zsh_cv_long_is_64_bit=yes,
-zsh_cv_long_is_64_bit=no,
-zsh_cv_long_is_64_bit=no)])
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[int main() { return sizeof(long) < 8; }]])],[zsh_cv_long_is_64_bit=yes],[zsh_cv_long_is_64_bit=no],[zsh_cv_long_is_64_bit=no])])
 
 AH_TEMPLATE([ino_t],
 [Define to `unsigned long' if <sys/types.h> doesn't define.])
@@ -1028,27 +1006,21 @@ if test x$zsh_cv_long_is_64_bit = xyes; then
   AC_DEFINE(LONG_IS_64_BIT)
 else
   AC_CACHE_CHECK(if off_t is 64 bit, zsh_cv_off_t_is_64_bit,
-  [AC_TRY_RUN([
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <sys/types.h>
 
 main() { return sizeof(off_t) < 8; }
-],
-  zsh_cv_off_t_is_64_bit=yes,
-  zsh_cv_off_t_is_64_bit=no,
-  zsh_cv_off_t_is_64_bit=no)])
+]])],[zsh_cv_off_t_is_64_bit=yes],[zsh_cv_off_t_is_64_bit=no],[zsh_cv_off_t_is_64_bit=no])])
   if test x$zsh_cv_off_t_is_64_bit = xyes; then
     AC_DEFINE(OFF_T_IS_64_BIT)
   fi
 
   AC_CACHE_CHECK(if ino_t is 64 bit, zsh_cv_ino_t_is_64_bit,
-  [AC_TRY_RUN([
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <sys/types.h>
 
 main() { return sizeof(ino_t) < 8; }
-],
-  zsh_cv_ino_t_is_64_bit=yes,
-  zsh_cv_ino_t_is_64_bit=no,
-  zsh_cv_ino_t_is_64_bit=no)])
+]])],[zsh_cv_ino_t_is_64_bit=yes],[zsh_cv_ino_t_is_64_bit=no],[zsh_cv_ino_t_is_64_bit=no])])
   if test x$zsh_cv_ino_t_is_64_bit = xyes; then
     AC_DEFINE(INO_T_IS_64_BIT)
   fi
@@ -1103,8 +1075,7 @@ fi
 
 dnl We'll blithely assume (f)printf supports the same types as sprintf.
 AC_CACHE_CHECK(for %lld printf support, zsh_cv_printf_has_lld,
-[AC_TRY_RUN(
-[#include <stdio.h>
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdio.h>
 #include <string.h>
 int main(int argc, char **argv)
 {
@@ -1116,10 +1087,7 @@ int main(int argc, char **argv)
    }
    return 1;
 }
-],
-zsh_cv_printf_has_lld=yes,
-zsh_cv_printf_has_lld=no,
-zsh_cv_printf_has_lld=no)])
+]])],[zsh_cv_printf_has_lld=yes],[zsh_cv_printf_has_lld=no],[zsh_cv_printf_has_lld=no])])
 AH_TEMPLATE(PRINTF_HAS_LLD,
 [Define to 1 if printf and sprintf support %lld for long long.])
 if test x$zsh_cv_printf_has_lld = xyes; then
@@ -1130,11 +1098,9 @@ dnl Check for sigset_t.  Currently I'm looking in
 dnl <sys/types.h> and <signal.h>.  Others might need
 dnl to be added.
 AC_CACHE_CHECK(for sigset_t, zsh_cv_type_sigset_t,
-[AC_TRY_COMPILE(
-[#define _POSIX_C_SOURCE 200809L
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#define _POSIX_C_SOURCE 200809L
 #include <sys/types.h>
-#include <signal.h>], [sigset_t tempsigset;],
-  zsh_cv_type_sigset_t=yes, zsh_cv_type_sigset_t=no)])
+#include <signal.h>]], [[sigset_t tempsigset;]])],[zsh_cv_type_sigset_t=yes],[zsh_cv_type_sigset_t=no])])
 AH_TEMPLATE([sigset_t],
 [Define to `unsigned int' if <sys/types.h> or <signal.h> doesn't define])
 if test x$zsh_cv_type_sigset_t = xno; then
@@ -1271,9 +1237,7 @@ AH_TEMPLATE([USE_LOCAL_H_ERRNO],
 [Define to 1 if h_errno is not defined by the system.])
 AC_CACHE_CHECK(if we need our own h_errno,
   zsh_cv_decl_h_errno_use_local,
-  [AC_TRY_LINK( ,[extern int h_errno; h_errno = 0;],
-  zsh_cv_decl_h_errno_use_local=no,
-  zsh_cv_decl_h_errno_use_local=yes)])
+  [AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[extern int h_errno; h_errno = 0;]])],[zsh_cv_decl_h_errno_use_local=no],[zsh_cv_decl_h_errno_use_local=yes])])
 
 if test x$zsh_cv_decl_h_errno_use_local = xyes; then
   AC_DEFINE(USE_LOCAL_H_ERRNO)
@@ -1361,7 +1325,7 @@ AH_TEMPLATE([TGETENT_ACCEPTS_NULL],
 [Define to 1 if tgetent() accepts NULL as a buffer.])
 AC_CACHE_CHECK(if tgetent accepts NULL,
 zsh_cv_func_tgetent_accepts_null,
-[AC_TRY_RUN([
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[
 main()
 {
     char buf[4096];
@@ -1375,20 +1339,17 @@ main()
     }
     exit((r1 != r2) || r2 == -1);
 }
-],
-  if test -f conftest.tgetent; then
+]])],[if test -f conftest.tgetent; then
     zsh_cv_func_tgetent_accepts_null=yes
   else
     zsh_cv_func_tgetent_accepts_null=no
-  fi,
-  zsh_cv_func_tgetent_accepts_null=no,
-  zsh_cv_func_tgetent_accepts_null=no)])
+  fi],[zsh_cv_func_tgetent_accepts_null=no],[zsh_cv_func_tgetent_accepts_null=no])])
 if test x$zsh_cv_func_tgetent_accepts_null = xyes; then
   AC_DEFINE(TGETENT_ACCEPTS_NULL)
 fi
 AC_CACHE_CHECK(if tgetent returns 0 on success,
 zsh_cv_func_tgetent_zero_success,
-[AC_TRY_RUN([
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[
 main()
 {
     char buf[4096];
@@ -1402,14 +1363,11 @@ main()
     }
     exit(r1 == r2);
 }
-],
-  if test -f conftest.tgetent0; then
+]])],[if test -f conftest.tgetent0; then
     zsh_cv_func_tgetent_zero_success=yes
   else
     zsh_cv_func_tgetent_zero_success=no
-  fi,
-  zsh_cv_func_tgetent_zero_success=no,
-  zsh_cv_func_tgetent_zero_success=no)])
+  fi],[zsh_cv_func_tgetent_zero_success=no],[zsh_cv_func_tgetent_zero_success=no])])
 AH_TEMPLATE([TGETENT_SUCCESS],
 [Define to what tgetent() returns on success (0 on HP-UX X/Open curses).])
 if test x$zsh_cv_func_tgetent_zero_success = xyes; then
@@ -1729,33 +1687,27 @@ if test x$zsh_cv_path_term_header != xnone; then
   fi
 
   AC_MSG_CHECKING(if boolcodes is available)
-  AC_TRY_LINK($term_includes, [char **test = boolcodes; puts(*test);],
-  AC_DEFINE(HAVE_BOOLCODES) boolcodes=yes, boolcodes=no)
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = boolcodes; puts(*test);]])],[AC_DEFINE(HAVE_BOOLCODES) boolcodes=yes],[boolcodes=no])
   AC_MSG_RESULT($boolcodes)
 
   AC_MSG_CHECKING(if numcodes is available)
-  AC_TRY_LINK($term_includes, [char **test = numcodes; puts(*test);],
-  AC_DEFINE(HAVE_NUMCODES) numcodes=yes, numcodes=no)
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = numcodes; puts(*test);]])],[AC_DEFINE(HAVE_NUMCODES) numcodes=yes],[numcodes=no])
   AC_MSG_RESULT($numcodes)
 
   AC_MSG_CHECKING(if strcodes is available)
-  AC_TRY_LINK($term_includes, [char **test = strcodes; puts(*test);],
-  AC_DEFINE(HAVE_STRCODES) strcodes=yes, strcodes=no)
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = strcodes; puts(*test);]])],[AC_DEFINE(HAVE_STRCODES) strcodes=yes],[strcodes=no])
   AC_MSG_RESULT($strcodes)
 
   AC_MSG_CHECKING(if boolnames is available)
-  AC_TRY_LINK($term_includes, [char **test = boolnames; puts(*test);],
-  AC_DEFINE(HAVE_BOOLNAMES) boolnames=yes, boolnames=no)
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = boolnames; puts(*test);]])],[AC_DEFINE(HAVE_BOOLNAMES) boolnames=yes],[boolnames=no])
   AC_MSG_RESULT($boolnames)
 
   AC_MSG_CHECKING(if numnames is available)
-  AC_TRY_LINK($term_includes, [char **test = numnames; puts(*test);],
-  AC_DEFINE(HAVE_NUMNAMES) numnames=yes, numnames=no)
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = numnames; puts(*test);]])],[AC_DEFINE(HAVE_NUMNAMES) numnames=yes],[numnames=no])
   AC_MSG_RESULT($numnames)
 
   AC_MSG_CHECKING(if strnames is available)
-  AC_TRY_LINK($term_includes, [char **test = strnames; puts(*test);],
-  AC_DEFINE(HAVE_STRNAMES) strnames=yes, strnames=no)
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[$term_includes]], [[char **test = strnames; puts(*test);]])],[AC_DEFINE(HAVE_STRNAMES) strnames=yes],[strnames=no])
   AC_MSG_RESULT($strnames)
 
   dnl There are apparently defective terminal library headers on some
@@ -1764,9 +1716,7 @@ if test x$zsh_cv_path_term_header != xnone; then
   tgoto_includes="$term_includes
 /* guaranteed to clash with any valid tgoto prototype */
 extern void tgoto(int **stuff, float **more_stuff);"
-  AC_TRY_LINK($tgoto_includes,
-  [int *stuff; float *more_stuff; tgoto(&stuff, &more_stuff);],
-  AC_DEFINE(TGOTO_PROTO_MISSING) tgotoprotomissing=yes, tgotoprotomissing=no)
+  AC_LINK_IFELSE([AC_LANG_PROGRAM([[$tgoto_includes]], [[int *stuff; float *more_stuff; tgoto(&stuff, &more_stuff);]])],[AC_DEFINE(TGOTO_PROTO_MISSING) tgotoprotomissing=yes],[tgotoprotomissing=no])
   AC_MSG_RESULT($tgotoprotomissing)
 else
   ZSH_TERM_H=
@@ -1832,19 +1782,16 @@ AH_TEMPLATE([rlim_t],
 DEFAULT_RLIM_T=long
 AC_CACHE_CHECK(if rlim_t is longer than a long,
 zsh_cv_rlim_t_is_longer,
-[AC_TRY_RUN([
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
 #include <sys/resource.h>
-main(){struct rlimit r;exit(sizeof(r.rlim_cur) <= sizeof(long));}],
-zsh_cv_rlim_t_is_longer=yes,
-zsh_cv_rlim_t_is_longer=no,
-zsh_cv_rlim_t_is_longer=yes)])
+main(){struct rlimit r;exit(sizeof(r.rlim_cur) <= sizeof(long));}]])],[zsh_cv_rlim_t_is_longer=yes],[zsh_cv_rlim_t_is_longer=no],[zsh_cv_rlim_t_is_longer=yes])])
 if test x$zsh_cv_rlim_t_is_longer = xyes; then
   AC_CACHE_CHECK(if rlim_t is a quad,
   zsh_cv_rlim_t_is_quad_t,
-  [AC_TRY_RUN([
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
@@ -1856,10 +1803,7 @@ main() {
   r.rlim_cur = 0;
   sprintf(buf, "%qd", r.rlim_cur);
   exit(strcmp(buf, "0"));
-}],
-  zsh_cv_rlim_t_is_quad_t=yes,
-  zsh_cv_rlim_t_is_quad_t=no,
-  zsh_cv_rlim_t_is_quad_t=no)])
+}]])],[zsh_cv_rlim_t_is_quad_t=yes],[zsh_cv_rlim_t_is_quad_t=no],[zsh_cv_rlim_t_is_quad_t=no])])
   if test x$zsh_cv_rlim_t_is_quad_t = xyes; then
     AC_DEFINE(RLIM_T_IS_QUAD_T)
     DEFAULT_RLIM_T=quad_t
@@ -1870,15 +1814,12 @@ main() {
 else
   AC_CACHE_CHECK(if the rlim_t is unsigned,
   zsh_cv_type_rlim_t_is_unsigned,
-  [AC_TRY_RUN([
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
 #include <sys/resource.h>
-  main(){struct rlimit r;r.rlim_cur=-1;exit(r.rlim_cur<0);}],
-  zsh_cv_type_rlim_t_is_unsigned=yes,
-  zsh_cv_type_rlim_t_is_unsigned=no,
-  zsh_cv_type_rlim_t_is_unsigned=no)])
+  main(){struct rlimit r;r.rlim_cur=-1;exit(r.rlim_cur<0);}]])],[zsh_cv_type_rlim_t_is_unsigned=yes],[zsh_cv_type_rlim_t_is_unsigned=no],[zsh_cv_type_rlim_t_is_unsigned=no])])
   if test x$zsh_cv_type_rlim_t_is_unsigned = xyes; then
     AC_DEFINE(RLIM_T_IS_UNSIGNED)
     DEFAULT_RLIM_T="unsigned $DEFAULT_RLIM_T"
@@ -1886,15 +1827,12 @@ else
 fi
 
 AC_CACHE_CHECK(for rlim_t, zsh_cv_type_rlim_t,
-[AC_TRY_COMPILE([
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 #include <sys/types.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #endif
-#include <sys/resource.h>],
-[rlim_t l;],
-zsh_cv_type_rlim_t=yes,
-zsh_cv_type_rlim_t=no)])
+#include <sys/resource.h>]], [[rlim_t l;]])],[zsh_cv_type_rlim_t=yes],[zsh_cv_type_rlim_t=no])])
 if test x$zsh_cv_type_rlim_t = xno; then
   AC_DEFINE_UNQUOTED(rlim_t, $DEFAULT_RLIM_T)
 fi
@@ -1931,7 +1869,7 @@ AH_TEMPLATE([RLIMIT_VMEM_IS_RSS],
 [Define to 1 if RLIMIT_VMEM and RLIMIT_RSS both exist and are equal.])
 AC_CACHE_CHECK(if RLIMIT_VMEM and RLIMIT_RSS are the same,
 zsh_cv_rlimit_vmem_is_rss,
-[AC_TRY_RUN([
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <sys/types.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
@@ -1944,10 +1882,7 @@ int ret = 1;
 if (RLIMIT_RSS == RLIMIT_VMEM) ret = 0;
 #endif
 return ret;
-}],
-  zsh_cv_rlimit_vmem_is_rss=yes,
-  zsh_cv_rlimit_vmem_is_rss=no,
-  zsh_cv_rlimit_vmem_is_rss=no)])
+}]])],[zsh_cv_rlimit_vmem_is_rss=yes],[zsh_cv_rlimit_vmem_is_rss=no],[zsh_cv_rlimit_vmem_is_rss=no])])
 
 if test x$zsh_cv_rlimit_vmem_is_rss = xyes; then
   AC_DEFINE(RLIMIT_VMEM_IS_RSS)
@@ -1958,7 +1893,7 @@ AH_TEMPLATE([RLIMIT_VMEM_IS_AS],
 [Define to 1 if RLIMIT_VMEM and RLIMIT_AS both exist and are equal.])
 AC_CACHE_CHECK(if RLIMIT_VMEM and RLIMIT_AS are the same,
 zsh_cv_rlimit_vmem_is_as,
-[AC_TRY_RUN([
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <sys/types.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
@@ -1971,10 +1906,7 @@ int ret = 1;
 if (RLIMIT_AS == RLIMIT_VMEM) ret = 0;
 #endif
 return ret;
-}],
-  zsh_cv_rlimit_vmem_is_as=yes,
-  zsh_cv_rlimit_vmem_is_as=no,
-  zsh_cv_rlimit_vmem_is_as=no)])
+}]])],[zsh_cv_rlimit_vmem_is_as=yes],[zsh_cv_rlimit_vmem_is_as=no],[zsh_cv_rlimit_vmem_is_as=no])])
 
 if test x$zsh_cv_rlimit_vmem_is_as = xyes; then
   AC_DEFINE(RLIMIT_VMEM_IS_AS)
@@ -1985,7 +1917,7 @@ AH_TEMPLATE([RLIMIT_RSS_IS_AS],
 [Define to 1 if RLIMIT_RSS and RLIMIT_AS both exist and are equal.])
 AC_CACHE_CHECK(if RLIMIT_RSS and RLIMIT_AS are the same,
 zsh_cv_rlimit_rss_is_as,
-[AC_TRY_RUN([
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <sys/types.h>
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
@@ -1998,10 +1930,7 @@ int ret = 1;
 if (RLIMIT_AS == RLIMIT_RSS) ret = 0;
 #endif
 return ret;
-}],
-  zsh_cv_rlimit_rss_is_as=yes,
-  zsh_cv_rlimit_rss_is_as=no,
-  zsh_cv_rlimit_rss_is_as=no)])
+}]])],[zsh_cv_rlimit_rss_is_as=yes],[zsh_cv_rlimit_rss_is_as=no],[zsh_cv_rlimit_rss_is_as=no])])
 
 if test x$zsh_cv_rlimit_rss_is_as = xyes; then
   AC_DEFINE(RLIMIT_RSS_IS_AS)
@@ -2108,7 +2037,7 @@ AH_TEMPLATE([GETCWD_CALLS_MALLOC],
 if test x$ac_cv_func_getcwd = xyes; then
   AC_CACHE_CHECK(whether getcwd calls malloc to allocate memory,
   zsh_cv_getcwd_malloc,
-  [AC_TRY_RUN([
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <unistd.h>
 #include <string.h>
 int main() {
@@ -2120,10 +2049,7 @@ int main() {
     }
     return 1;
 }
-],
-  zsh_cv_getcwd_malloc=yes,
-  zsh_cv_getcwd_malloc=no,
-  zsh_cv_getcwd_malloc=no)])
+]])],[zsh_cv_getcwd_malloc=yes],[zsh_cv_getcwd_malloc=no],[zsh_cv_getcwd_malloc=no])])
   if test x$zsh_cv_getcwd_malloc = xyes; then
     AC_DEFINE(GETCWD_CALLS_MALLOC)
   fi
@@ -2176,9 +2102,8 @@ dnl brk/sbrk PROTOTYPES
 dnl -------------------
 AC_CACHE_CHECK(for brk() prototype in <unistd.h>,
 zsh_cv_header_unistd_h_brk_proto,
-[AC_TRY_COMPILE([#include <unistd.h>
-double brk();], [int i;],
-zsh_cv_header_unistd_h_brk_proto=no, zsh_cv_header_unistd_h_brk_proto=yes)])
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>
+double brk();]], [[int i;]])],[zsh_cv_header_unistd_h_brk_proto=no],[zsh_cv_header_unistd_h_brk_proto=yes])])
 AH_TEMPLATE([HAVE_BRK_PROTO],
 [Define to 1 if there is a prototype defined for brk() on your system.])
 if test x$zsh_cv_header_unistd_h_brk_proto = xyes; then
@@ -2187,9 +2112,8 @@ fi
 
 AC_CACHE_CHECK(for sbrk() prototype in <unistd.h>,
 zsh_cv_header_unistd_h_sbrk_proto,
-[AC_TRY_COMPILE([#include <unistd.h>
-double sbrk();], [int i;],
-zsh_cv_header_unistd_h_sbrk_proto=no, zsh_cv_header_unistd_h_sbrk_proto=yes)])
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <unistd.h>
+double sbrk();]], [[int i;]])],[zsh_cv_header_unistd_h_sbrk_proto=no],[zsh_cv_header_unistd_h_sbrk_proto=yes])])
 AH_TEMPLATE([HAVE_SBRK_PROTO],
 [Define to 1 if there is a prototype defined for sbrk() on your system.])
 if test x$zsh_cv_header_unistd_h_sbrk_proto = xyes; then
@@ -2204,10 +2128,8 @@ AH_TEMPLATE([HAVE_MKNOD_PROTO],
 if test "$ac_cv_prog_cc_stdc" != no; then
   AC_CACHE_CHECK(for mknod prototype in <sys/stat.h>,
   zsh_cv_header_sys_stat_h_mknod_proto,
-  [AC_TRY_COMPILE([#include <sys/stat.h>
-   int mknod(double x);], [int i;],
-  zsh_cv_header_sys_stat_h_mknod_proto=no,
-  zsh_cv_header_sys_stat_h_mknod_proto=yes)])
+  [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/stat.h>
+   int mknod(double x);]], [[int i;]])],[zsh_cv_header_sys_stat_h_mknod_proto=no],[zsh_cv_header_sys_stat_h_mknod_proto=yes])])
   if test x$zsh_cv_header_sys_stat_h_mknod_proto = xyes; then
     AC_DEFINE(HAVE_MKNOD_PROTO)
   fi
@@ -2218,24 +2140,20 @@ dnl presence and location of ioctl prototype
 dnl ----------------------------------------
 AC_CACHE_CHECK(for ioctl prototype in <unistd.h> or <termios.h>,
 zsh_cv_header_unistd_h_termios_h_ioctl_proto,
-[AC_TRY_COMPILE([
+[AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
 #ifdef HAVE_TERMIOS_H
 # include <termios.h>
 #endif
-double ioctl();], [int i;],
-zsh_cv_header_unistd_h_termios_h_ioctl_proto=no,
-zsh_cv_header_unistd_h_termios_h_ioctl_proto=yes)])
+double ioctl();]], [[int i;]])],[zsh_cv_header_unistd_h_termios_h_ioctl_proto=no],[zsh_cv_header_unistd_h_termios_h_ioctl_proto=yes])])
 
 if test x$zsh_cv_header_unistd_h_termios_h_ioctl_proto = xno; then
   AC_CACHE_CHECK(for ioctl prototype in <sys/ioctl.h>,
   zsh_cv_header_sys_ioctl_h_ioctl_proto,
-  [AC_TRY_COMPILE([#include <sys/ioctl.h>
-  double ioctl();], [int i;],
-  zsh_cv_header_sys_ioctl_h_ioctl_proto=no,
-  zsh_cv_header_sys_ioctl_h_ioctl_proto=yes)])
+  [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/ioctl.h>
+  double ioctl();]], [[int i;]])],[zsh_cv_header_sys_ioctl_h_ioctl_proto=no],[zsh_cv_header_sys_ioctl_h_ioctl_proto=yes])])
 else
   zsh_cv_header_sys_ioctl_h_ioctl_proto=no
 fi
@@ -2260,9 +2178,7 @@ AH_TEMPLATE([SELECT_IN_SYS_SOCKET_H],
 if test x$ac_cv_header_sys_select_h != xyes; then
   AC_CACHE_CHECK(for select() in <sys/socket.h>,
   zsh_cv_header_socket_h_select_proto,
-  [AC_TRY_COMPILE([#include <sys/socket.h>], [fd_set fd;],
-  zsh_cv_header_socket_h_select_proto=yes, 
-  zsh_cv_header_socket_h_select_proto=no)])
+  [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <sys/socket.h>]], [[fd_set fd;]])],[zsh_cv_header_socket_h_select_proto=yes],[zsh_cv_header_socket_h_select_proto=no])])
   if test x$zsh_cv_header_socket_h_select_proto = xyes; then
     AC_DEFINE(SELECT_IN_SYS_SOCKET_H)
   fi
@@ -2279,7 +2195,7 @@ zsh_cv_sys_fifo,
 [if test "$host_os" = cygwin; then
 zsh_cv_sys_fifo=yes
 else
-AC_TRY_RUN([
+AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <fcntl.h>
 #include <signal.h>
 main()
@@ -2306,10 +2222,7 @@ main()
     unlink("/tmp/fifo$$");
     exit(ret);
 }
-],
-  zsh_cv_sys_fifo=yes,
-  zsh_cv_sys_fifo=no,
-  zsh_cv_sys_fifo=yes)
+]])],[zsh_cv_sys_fifo=yes],[zsh_cv_sys_fifo=no],[zsh_cv_sys_fifo=yes])
 fi])
 AH_TEMPLATE([HAVE_FIFOS],
 [Define to 1 if system has working FIFOs.])
@@ -2323,7 +2236,7 @@ dnl for instance, BeOS R4.51 doesn't support hard links yet
 dnl -----------
 AC_CACHE_CHECK(if link() works,
 zsh_cv_sys_link,
-[AC_TRY_RUN([
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <unistd.h>
 #include <fcntl.h>
 main()
@@ -2341,10 +2254,7 @@ main()
     unlink(newfile);
     exit(ret<0);
 }
-],
-  zsh_cv_sys_link=yes,
-  zsh_cv_sys_link=no,
-  zsh_cv_sys_link=yes)])
+]])],[zsh_cv_sys_link=yes],[zsh_cv_sys_link=no],[zsh_cv_sys_link=yes])])
 AH_TEMPLATE([HAVE_LINK],
 [Define to 1 if system has working link().])
 if test x$zsh_cv_sys_link = xyes; then
@@ -2357,7 +2267,7 @@ dnl should set errno to ESRCH, but some like BeOS R4.51 set to EINVAL
 dnl -----------
 AC_CACHE_CHECK(if kill(pid, 0) returns ESRCH correctly,
 zsh_cv_sys_killesrch,
-[AC_TRY_RUN([
+[AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <unistd.h>
 #include <signal.h>
 #include <errno.h>
@@ -2367,10 +2277,7 @@ main()
     while (pid && (kill(pid, 0) == 0 || errno != ESRCH)) pid >>= 1;
     exit(errno!=ESRCH);
 }
-],
-  zsh_cv_sys_killesrch=yes,
-  zsh_cv_sys_killesrch=no,
-  zsh_cv_sys_killesrch=yes)])
+]])],[zsh_cv_sys_killesrch=yes],[zsh_cv_sys_killesrch=no],[zsh_cv_sys_killesrch=yes])])
 AH_TEMPLATE([BROKEN_KILL_ESRCH],
 [Define to 1 if kill(pid, 0) doesn't return ESRCH, ie BeOS R4.51.])
 if test x$zsh_cv_sys_killesrch = xno; then
@@ -2386,7 +2293,7 @@ Define to 1 if sigsuspend() is broken, ie BeOS R4.51.])
 if test x$signals_style = xPOSIX_SIGNALS; then
     AC_CACHE_CHECK(if POSIX sigsuspend() works,
     zsh_cv_sys_sigsuspend,
-    [AC_TRY_RUN([
+    [AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <signal.h>
 #include <unistd.h>
 int child=0;
@@ -2411,10 +2318,7 @@ main() {
         exit(child==0);
     }
 }
-],
-      zsh_cv_sys_sigsuspend=yes,
-      zsh_cv_sys_sigsuspend=no,
-      zsh_cv_sys_sigsuspend=yes)])
+]])],[zsh_cv_sys_sigsuspend=yes],[zsh_cv_sys_sigsuspend=no],[zsh_cv_sys_sigsuspend=yes])])
     if test x$zsh_cv_sys_sigsuspend = xno; then
       AC_DEFINE(BROKEN_POSIX_SIGSUSPEND)
     fi
@@ -2427,11 +2331,11 @@ dnl -----------
 AH_TEMPLATE([BROKEN_TCSETPGRP],
 [Define to 1 if tcsetpgrp() doesn't work, ie BeOS R4.51.])
 AC_ARG_WITH(tcsetpgrp,
-AC_HELP_STRING([--with-tcsetpgrp], [assumes that tcsetpgrp() exists and works correctly]),[
+AS_HELP_STRING([--with-tcsetpgrp],[assumes that tcsetpgrp() exists and works correctly]),[
 case "x$withval" in
     xyes) zsh_working_tcsetpgrp=yes;;
     xno)  zsh_working_tcsetpgrp=no;;
-    *)    AC_ERROR([please use --with-tcsetpgrp=yes or --with-tcsetpgrp=no]);;
+    *)    AC_MSG_ERROR(please use --with-tcsetpgrp=yes or --with-tcsetpgrp=no);;
 esac],[zsh_working_tcsetpgrp=check])
 if test "x$ac_cv_func_tcsetpgrp" = xyes; then
 case "x$zsh_working_tcsetpgrp" in
@@ -2439,7 +2343,7 @@ case "x$zsh_working_tcsetpgrp" in
     trap "" TTOU > /dev/null 2>&1 || :
     AC_CACHE_CHECK(if tcsetpgrp() actually works,
     zsh_cv_sys_tcsetpgrp,
-    [AC_TRY_RUN([
+    [AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <sys/types.h>
 #include <unistd.h>
 #include <fcntl.h>
@@ -2452,14 +2356,13 @@ main() {
     if (ret < 0) exit(1);
     exit(0);
 }
-],
-      zsh_cv_sys_tcsetpgrp=yes, [
+]])],[zsh_cv_sys_tcsetpgrp=yes],[
 case $? in
     1) zsh_cv_sys_tcsetpgrp=no;;
     2) zsh_cv_sys_tcsetpgrp=notty;;
     *) zsh_cv_sys_tcsetpgrp=error;;
 esac
-      ], zsh_cv_sys_tcsetpgrp=yes)])
+      ],[zsh_cv_sys_tcsetpgrp=yes])])
     case "x$zsh_cv_sys_tcsetpgrp" in
       xno)    AC_DEFINE(BROKEN_TCSETPGRP);;
       xyes)   :;;
@@ -2485,7 +2388,7 @@ AH_TEMPLATE([GETPWNAM_FAKED],
 if test x$ac_cv_func_getpwnam = xyes; then
     AC_CACHE_CHECK(if getpwnam() is faked,
     zsh_cv_sys_getpwnam_faked,
-    [AC_TRY_RUN([
+    [AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <pwd.h>
 main() {
     struct passwd *pw1, *pw2;
@@ -2497,10 +2400,7 @@ main() {
     pw2=getpwnam(buf);
     exit(pw1!=0 && pw2!=0 && !strcmp(name, pw2->pw_name));
 }
-],
-      zsh_cv_sys_getpwnam_faked=no,
-      zsh_cv_sys_getpwnam_faked=yes,
-      zsh_cv_sys_getpwnam_faked=no)])
+]])],[zsh_cv_sys_getpwnam_faked=no],[zsh_cv_sys_getpwnam_faked=yes],[zsh_cv_sys_getpwnam_faked=no])])
     if test x$zsh_cv_sys_getpwnam_faked = xyes; then
       AC_DEFINE(GETPWNAM_FAKED)
     fi
@@ -2544,13 +2444,11 @@ if test x$ac_cv_have_dev_ptmx = xyes -o x$ac_cv_func_posix_openpt = xyes && \
    test x$ac_cv_func_ptsname = xyes; then
    AC_CACHE_CHECK([if /dev/ptmx is usable],
    ac_cv_use_dev_ptmx,
-   [AC_TRY_COMPILE([#ifdef __linux
+   [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#ifdef __linux
 #define _GNU_SOURCE 1
 #endif
 #include <stdlib.h>
-int ptsname();], ,
-   ac_cv_use_dev_ptmx=no,
-   ac_cv_use_dev_ptmx=yes)])
+int ptsname();]], [[]])],[ac_cv_use_dev_ptmx=no],[ac_cv_use_dev_ptmx=yes])])
    if test x$ac_cv_use_dev_ptmx = xyes; then
      AC_DEFINE(USE_DEV_PTMX)
    fi
@@ -2560,7 +2458,7 @@ dnl -----------------
 dnl multibyte support
 dnl -----------------
 AC_ARG_ENABLE(multibyte,
-AC_HELP_STRING([--enable-multibyte], [support multibyte characters]),
+AS_HELP_STRING([--enable-multibyte],[support multibyte characters]),
 [zsh_cv_c_unicode_support=$enableval],
 [AC_CACHE_VAL(zsh_cv_c_unicode_support,
   AC_MSG_NOTICE([checking for functions supporting multibyte characters])
@@ -2596,7 +2494,7 @@ dnl
 AH_TEMPLATE([ENABLE_UNICODE9],
 [Define to 1 if you want use unicode9 character widths.])
 AC_ARG_ENABLE(unicode9,
-AC_HELP_STRING([--enable-unicode9], [compile with unicode9 character widths]),
+AS_HELP_STRING([--enable-unicode9],[compile with unicode9 character widths]),
 [if test x$enableval = xyes; then
   AC_DEFINE(ENABLE_UNICODE9)
 fi])
@@ -2663,10 +2561,7 @@ if test x$zsh_cv_c_unicode_support = xyes; then
 
   AC_CACHE_CHECK(if the wcwidth() and/or iswprint() functions are broken,
   zsh_cv_c_broken_wcwidth,
-  [AC_TRY_RUN([$locale_prog],
-  zsh_cv_c_broken_wcwidth=yes,
-  zsh_cv_c_broken_wcwidth=no,
-  zsh_cv_c_broken_wcwidth=no)])
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[$locale_prog]])],[zsh_cv_c_broken_wcwidth=yes],[zsh_cv_c_broken_wcwidth=no],[zsh_cv_c_broken_wcwidth=no])])
   if test x$zsh_cv_c_broken_wcwidth = xyes; then
     AC_DEFINE(ENABLE_UNICODE9)
   fi
@@ -2695,10 +2590,7 @@ if test x$zsh_cv_c_unicode_support = xyes; then
 
   AC_CACHE_CHECK(if the isprint() function is broken,
   zsh_cv_c_broken_isprint,
-  [AC_TRY_RUN([$locale_prog],
-  zsh_cv_c_broken_isprint=yes,
-  zsh_cv_c_broken_isprint=no,
-  zsh_cv_c_broken_isprint=no)])
+  [AC_RUN_IFELSE([AC_LANG_SOURCE([[$locale_prog]])],[zsh_cv_c_broken_isprint=yes],[zsh_cv_c_broken_isprint=no],[zsh_cv_c_broken_isprint=no])])
   if test x$zsh_cv_c_broken_isprint = xyes; then
     AC_DEFINE(BROKEN_ISPRINT)
   fi
@@ -2710,7 +2602,7 @@ dnl
 AH_TEMPLATE([LIBC_MUSL],
 [Define to 1 if musl is being used as the C library])
 AC_ARG_ENABLE(libc-musl,
-AC_HELP_STRING([--enable-libc-musl], [compile with musl as the C library]),
+AS_HELP_STRING([--enable-libc-musl],[compile with musl as the C library]),
 [if test x$enableval = xyes; then
   AC_DEFINE(LIBC_MUSL)
 fi])
@@ -2719,7 +2611,7 @@ dnl
 dnl static user lookup
 dnl
 AC_ARG_ENABLE(dynamic-nss,
-	      AC_HELP_STRING([--disable-dynamic-nss], [do not call
+	      AS_HELP_STRING([--disable-dynamic-nss],[do not call
 			      functions that will require dynamic NSS
 			      modules]),
 [zsh_cv_c_dynamic_nss=$enableval],
@@ -2822,7 +2714,7 @@ elif test "$host_os" = cygwin; then
 elif test "x$dynamic" = xyes; then
   AC_CACHE_CHECK(if your system uses ELF binaries,
    zsh_cv_sys_elf,
-   [AC_TRY_RUN([/* Test for whether ELF binaries are produced */
+   [AC_RUN_IFELSE([AC_LANG_SOURCE([[/* Test for whether ELF binaries are produced */
 #include <fcntl.h>
 #include <stdlib.h>
 main(argc, argv)
@@ -2837,10 +2729,7 @@ char *argv[];
 		exit(0); /* succeed (yes, it's ELF) */
 	else
 		exit(1); /* fail */
-}],
-  zsh_cv_sys_elf=yes,
-  zsh_cv_sys_elf=no,
-  zsh_cv_sys_elf=yes)])
+}]])],[zsh_cv_sys_elf=yes],[zsh_cv_sys_elf=no],[zsh_cv_sys_elf=yes])])
 
   # We use [0-9]* in case statements, so need to change quoting
   changequote(, )
@@ -2969,18 +2858,17 @@ char *argv[];
 AC_CACHE_CHECK(if we can use -rdynamic, zsh_cv_rdynamic_available,
 old_LDFLAGS="$LDFLAGS"
 LDFLAGS="$LDFLAGS -rdynamic"
-AC_TRY_LINK([], [], [zsh_cv_rdynamic_available=yes
-EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-rdynamic}"],
-[zsh_cvs_rdynamic_available=no])
+AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])],[zsh_cv_rdynamic_available=yes
+EXTRA_LDFLAGS="${EXTRA_LDFLAGS=-rdynamic}"],[zsh_cvs_rdynamic_available=no])
 LDFLAGS="$old_LDFLAGS")
   AC_CACHE_CHECK(if your dlsym() needs a leading underscore,
    zsh_cv_func_dlsym_needs_underscore,
    [echo failed >conftestval && cat >conftest.c <<EOM
 fred () { }
 EOM
-    AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest.c 1>&AC_FD_CC) &&
-    AC_TRY_COMMAND($DLLD $LDFLAGS $DLLDFLAGS -o conftest.$DL_EXT conftest.o 1>&AC_FD_CC) &&
-    AC_TRY_RUN([
+    AC_TRY_COMMAND($CC -c $CFLAGS $CPPFLAGS $DLCFLAGS conftest.c 1>&AS_MESSAGE_LOG_FD) &&
+    AC_TRY_COMMAND($DLLD $LDFLAGS $DLLDFLAGS -o conftest.$DL_EXT conftest.o 1>&AS_MESSAGE_LOG_FD) &&
+    AC_RUN_IFELSE([AC_LANG_SOURCE([[
 #include <stdio.h>
 #ifdef HPUX10DYNAMIC
 #include <dl.h>
@@ -3032,10 +2920,8 @@ main()
     else
         fprintf (f, "no") ;
     exit(0);
-}], zsh_cv_func_dlsym_needs_underscore=`cat conftestval`,
-    zsh_cv_func_dlsym_needs_underscore=failed
-    dynamic=no,
-    zsh_cv_func_dlsym_needs_underscore=no)])
+}]])],[zsh_cv_func_dlsym_needs_underscore=`cat conftestval`],[zsh_cv_func_dlsym_needs_underscore=failed
+    dynamic=no],[zsh_cv_func_dlsym_needs_underscore=no])])
   if test "x$zsh_cv_func_dlsym_needs_underscore" = xyes; then
     AC_DEFINE(DLSYM_NEEDS_UNDERSCORE)
   elif test "x$zsh_cv_func_dlsym_needs_underscore" != xno; then
-- 
2.17.0


^ permalink raw reply	[relevance 3%]

* [PATCH] Completion: Add _bash, update _sh
@ 2018-06-09  5:15  5% dana
  0 siblings, 0 replies; 200+ results
From: dana @ 2018-06-09  5:15 UTC (permalink / raw)
  To: Zsh workers

This adds full completion for bash. Hopefully that's not heresy or something.

It also updates _sh to stop completing for bash (of course), and to start
completing for ash, dash, yash, and a few others that were missing.

PS: I find zsh's own completion function a bit underwhelming. It's a good demo
of `_arguments --`, but zsh has so many options, and the --help output is so
non-descriptive about them, that relying on that just doesn't feel very nice
somehow. I'd like to improve it, but the only way i can think to achieve that is
to manually enumerate each shell option (180 of them!) and their variant name(s)
(482 total!). That's certainly doable, but idk, not sure if it's the right
direction.

PPS: I think workers # 42918 was missed:
http://www.zsh.org/mla/workers/2018/msg00722.html

dana


diff --git a/Completion/Unix/Command/_bash b/Completion/Unix/Command/_bash
new file mode 100644
index 000000000..260ca64fb
--- /dev/null
+++ b/Completion/Unix/Command/_bash
@@ -0,0 +1,85 @@
+#compdef bash
+
+local ret=1
+local -a context line state state_descr args tmp cmd
+local -A opt_args val_args
+
+cmd=( $words[1] --noprofile --norc )
+
+args=(
+  # Long options must appear before short options (take care — some of these are
+  # duplicated in the s group below!)
+  + l
+  '!--debug'
+  '--debugger[enable extended debugging mode]'
+  '(-D --dump-po-strings --dump-strings)--dump-po-strings[like -D, but display in gettext PO format]'
+  '(-D --dump-po-strings --dump-strings)--dump-strings[display strings subject to language translation]'
+  '(: -)--help[display help information]'
+  # The usual = is deliberately omitted here
+  '(--init-file --rcfile)'{--init-file,--rcfile}'[load specified file instead of ~/.bashrc]: :_files'
+  '(-l --login)--login[act as login shell]'
+  '--noediting[disable readline editing]'
+  '--noprofile[do not load /etc/profile, ~/.bash_profile, etc.]'
+  '--norc[do not load ~/.bashrc]'
+  '--posix[enable POSIX mode]'
+  '(-r --restricted)--restricted[act as restricted shell]'
+  '(: -)--version[display version information]'
+  # This is ugly, but this way the + variants have accurate descriptions. Note
+  # that bash does accept + variants of -i, -l, -s, etc., but they don't seem to
+  # actually do anything, so we don't bother with them
+  + s
+  '(l)'{'-a[','+a[do not '}'mark all functions and variables for export]'
+  '(l)'{'-B[','+B[do not '}'enable brace expansion]'
+  '(l)'{'-b[','+b[do not '}'report status of terminated background jobs immediately]'
+  '(l)'{'-C[','+C[do not '}'prevent output redirection from overwriting existing files]'
+  '(l 1 -)-c[execute specified command string]:command string:_cmdstring:argv[0]:'
+  '(l)-D[display strings subject to language translation]'
+  '(l)'{'-E[','+E[do not '}'make functions and subshells inherit ERR traps]'
+  '(l)'{'-e[','+e[do not '}'exit immediately on non-zero return]'
+  '(l)'{'-f[','+f[do not '}'disable file globbing]'
+  '(l)'{'-H[','+H[do not '}'enable history substitution]'
+  '(l)'{'-h[','+h[do not '}'hash commands]'
+  '(l)-i[act as interactive shell]'
+  '(l)'{'-k[','+k[do not '}'act on variable assignments in command arguments]'
+  '(l)-l[act as login shell]'
+  '(l)'{'-m[','+m[do not '}'enable job control]'
+  '(l)'{'-n[','+n[do not '}'read (syntax-check) commands only]'
+  '(l)*'{'-O[','+O[un'}'set specified `shopt` option]: :->shopt-options'
+  '(l)*'{'-o[','+o[un'}'set specified `set` option]: :->set-options'
+  '(l)'{'-P[','+P[do not '}'resolve cd paths]'
+  '(l)'{'-p[','+p[do not '}'enable privileged mode]'
+  '(l)-r[act as restricted shell]'
+  '(l 1 -c)-s[read commands from standard input]'
+  '(l)'{'-T[','+T[do not '}'make functions and subshells inherit DEBUG and RETURN traps]'
+  '(l)'{'-t[','+t[do not '}'exit after executing one command]'
+  '(l)'{'-u[','+u[do not '}'treat unset variables as an error during parameter expansion]'
+  '(l)'{'-v[','+v[do not '}'print shell input lines as they are read]'
+  '(l)'{'-x[','+x[do not '}'print command trace]'
+  + o
+  '(-)1:script file:_files'
+  '(-)*:: :->args'
+)
+
+_arguments -s -S -A '-*' : $args && ret=0
+
+case $state in
+  args)
+    if [[ -n ${opt_args[(i)s--[cs]]} ]]; then
+      _files && ret=0
+    else
+      _normal && ret=0
+    fi
+    ;;
+  set-options)
+    tmp=( ${(f)"$( _call_program set-options ${(q-)cmd} -c '"shopt -o"' )"} )
+    tmp=( ${tmp%%[[:space:]]*} )
+    _values -w '`set` option' $tmp && ret=0
+    ;;
+  shopt-options)
+    tmp=( ${(f)"$( _call_program shopt-options ${(q-)cmd} -c shopt )"} )
+    tmp=( ${tmp%%[[:space:]]*} )
+    _values -w '`shopt` option' $tmp && ret=0
+    ;;
+esac
+
+return ret

diff --git a/Completion/Unix/Command/_sh b/Completion/Unix/Command/_sh
index 2afb46621..39d299c58 100644
--- a/Completion/Unix/Command/_sh
+++ b/Completion/Unix/Command/_sh
@@ -1,4 +1,4 @@
-#compdef sh ksh bash csh tcsh rc
+#compdef sh ash csh dash ksh ksh88 ksh93 mksh oksh pdksh rc tcsh yash
 
 if (( CURRENT == ${words[(i)-c]} + 1 )); then
   _cmdstring


^ permalink raw reply	[relevance 5%]

* [PATCH] Completion: Improve _man
@ 2018-06-10  6:06  5% dana
    0 siblings, 1 reply; 200+ results
From: dana @ 2018-06-10  6:06 UTC (permalink / raw)
  To: Zsh workers

Here's a *much* more complicated change. It enhances _man as follows:

* Complete all options to most major man variants
* More accurately match sections
* Better handle certain edge cases (see below)
* Show descriptions for sections (suggested by Mikael i think)

Notes:

* The function would previously use the provided section name as the leading
  part of a directory pattern, so that on Solaris for example if you gave it 3p
  it would also complete pages from 3pool, 3proc, &c. I couldn't think of a
  reason this functionality would be desirable (man won't show you a page from a
  section it's not in anyway), so i made it match exactly

* The function previously behaved kind of strangely when given multiple operands
  (where the first one is potentially an unknown section name). For example, if
  you do `man zz <TAB>` on macOS it just keeps completing the word 'fuzzy'
  because it wants to use zz as part of a glob to match page names. I changed
  that so that if the section name (given either as the first operand or as an
  argument to -s/-S) doesn't seem valid, it's treated as a page name, and
  subsequent completion possibilities are thus returned from all sections

  This can still be a problem — `man 2to3 <TAB>` doesn't work well, for example,
  because the function thinks 2to3 looks like a (Solaris-style) section name. I
  know this can be fixed, but see my last two notes :/

* It looks like someone put some special effort into making the function at
  least partially usable on AIX, so i tried to account for that platform as
  well... but i don't actually have access to it myself, so the extent of my
  testing there was just OSTYPE=aix

* I chose sections to describe pretty arbitrarily. Solaris variants have 9034234
  different sections and i didn't want to enumerate every single one, but some
  of them seem like they might come up a lot in regular use. It may be nice in
  the future to give separate tags to the described and undescribed ones, so
  that navigation is a little nicer for people with `group-name ''`

* There were a bunch of options whose purpose wasn't very clear to me, so i
  couldn't complete them as well as i wanted to. I put @todos where this was the
  case. If anyone knows how they actually work, feel free to change them
  accordingly

* There are some bits that were (or are now) redundant or otherwise not great —
  see the todo note someone left about the sects array for example — but i just
  left them alone because i was afraid of breaking something

* Could someone please review the way i return from _arguments? I don't think
  it's correct (because it doesn't account for prefix-needed?), but i've been
  looking at this for too long

I've tested this on several different platforms, but it's still a pretty major
alteration to a function that i imagine sees a lot of traffic, so i'm a little
nervous about it. If someone else could try it out that might be a good idea

dana


diff --git a/Completion/Unix/Command/_man b/Completion/Unix/Command/_man
index 67810e1dc..14e9e75b5 100644
--- a/Completion/Unix/Command/_man
+++ b/Completion/Unix/Command/_man
@@ -1,16 +1,169 @@
 #compdef man apropos whatis
 
+# Notes:
+# - Solaris is seemingly the only OS that doesn't allow the `man n page` syntax;
+#   you must use `man -s n page`
+# - We assume that Linux distributions are using either man-db or mandoc
+# - @todo Option exclusivity isn't super accurate
+# - @todo Solaris man accepts a single hyphen as the first option to disable
+#   paging (like AIX's -c); we don't support that
+# - @todo Linux apropos/whatis take options; we don't complete them yet
+
 _man() {
-  local dirs expl mrd awk
+  local dirs expl mrd awk variant noinsert
+  local -a context line state state_descr args modes
+  local -aU sects
+  local -A opt_args val_args sect_descs
 
-  if (( $words[(I)-M] == (( $CURRENT - 1 )) )); then
-    _directories && return 0
-  fi
+  if [[ $service == man ]]; then
+    # We'll treat all mandoc-based systems (Alpine, various Illumos distros,
+    # etc.) as OpenBSD
+    _pick_variant -r variant openbsd='-S subsection' $OSTYPE ---
+
+    modes=(
+      -f -K -k -l -R -w -W
+      --apropos
+      --global-apropos
+      --local-file
+      --location
+      --location-cat
+      --recode
+      --whatis
+      --where
+      --where-cat
+    )
+    [[ $variant == darwin* ]] && modes+=( -t )
+
+    args=(
+      "(${(j< >)modes})"{-f,--whatis}'[display short description (like whatis)]'
+      "(${(j< >)modes})"{-k,--apropos}'[search for keyword (like apropos)]'
+      '(-M --manpath)'{-M+,--manpath=}'[specify manual search path]:manual search path:_sequence -s\: _directories'
+    )
+    if [[ $variant == (darwin|dragonfly|freebsd|linux)* ]]; then
+      args+=(
+        '(-a -S -s --all --sections)'{-a,--all}'[display all matching pages]'
+        '(-P --pager)'{-P+,--pager=}'[specify output pager]:pager:_path_commands'
+        # @todo Could enumerate these
+        '(-p --preprocessor)'{-p+,--preprocessor=}'[specify roff preprocessor sequence]:preprocessor sequence'
+      )
+    else
+      args+=( '(-s)-a[display all matching pages]' )
+    fi
+    [[ $variant == (aix|solaris)* ]] || args+=(
+      '(-C --config-file)'{-C+,--config-file=}'[specify configuration file]:configuration file:_files'
+      "(${(j< >)modes})"{-w,--path,--where}'[display file locations]'
+    )
+    [[ $variant == (aix|netbsd|openbsd)* ]] || args+=(
+      # @todo FreeBSD allows this to be given multiple times
+      '(-d --debug)'{-d,--debug}'[display debugging information]'
+    )
+    [[ $variant == (darwin|dragonfly|freebsd|linux|solaris|aix)* ]] && args+=(
+      '(-7 -H -t --ascii --html --troff)'{-t,--troff}'[format man page using troff]'
+    )
+    [[ $variant == (darwin|linux)* ]] && args+=(
+      "(${(j< >)modes})"{-K,--global-apropos}'[search for keyword in all pages]'
+      '(-m --systems)'{-m+,--systems=}'[search manual of specified system]:operating system'
+    )
+    [[ $variant == (darwin|dragonfly|freebsd)* ]] && args+=(
+      '(: -)-h[display help information]'
+      '(-a)-S+[specify manual sections to search]: :->sects'
+    )
+    [[ $variant == (dragonfly|freebsd)* ]] && args+=(
+      # @todo Could enumerate these
+      '-m[search manual of specified architecture]:architecture'
+      '-o[use non-localized man pages]'
+    )
+    [[ $variant == (netbsd|openbsd)* ]] && args+=(
+      '-c[disable paging]'
+      '-m[augment manual search path]:manual search path:_sequence -s\: _directories'
+      '(-a)-s+[specify manual section to search]: :->sects'
+    )
+    [[ $variant == linux* ]] && args+=(
+      '(: -)'{-\?,--help}'[display help information]'
+      '(-7 -t -H -T -Z --ascii --html --troff --troff-device --ditroff)'{-7,--asci}'[translate man pages for 7-bit terminal]'
+      '(-D --default)'{-D,--default}'[reset man to default options]'
+      # @todo Could enumerate these
+      '(-E --encoding)'{-E+,--encoding=}'[specify output encoding]:encoding'
+      '(-e --extension)'{-e+,--extension=}'[specify sub-extension]:sub-extension'
+      '(-H --html)'{-H-,--html=-}'[produce HTML output for specified browser]::Web browser:_path_commands'
+      '(-i -I --ignore-case --match-case)'{-i,--ignore-case}'[search case-insensitively]'
+      '(-i -I --ignore-case --match-case)'{-I,--match-case}'[search case-sensitively]'
+      '(-L --locale)'{-L+,--locale=}'[specify locale]:locale:_locales'
+      "(${(j< >)modes})"{-l+,--local-file=}'[format and display specified file]:*:::manual file:_files'
+      "!(${(j< >)modes})"{--location,--location-cat}
+      '--names-only[match only page names (with --regex or --wildcard)]'
+      '(--nh --no-hyphenation)'{--nh,--no-hyphenation}'[disable hyphenation]'
+      '(--nj --no-justification)'{--nj,--no-justification}'[disable justification]'
+      '--no-subpages[do not combine pairs of page names into single page name]'
+      # @todo Could enumerate these
+      "(${(j< >)modes})"{-R+,--recode=}'[output man page in specified encoding]:encoding'
+      '(-r --prompt)'{-r+,--prompt=}'[specify prompt for less]:less prompt'
+      '(-a --all --wildcard)--regex[treat page name as regular expression]'
+      '(-a -S -s --all --sections)'{-S+,-s+,--sections=}'[specify manual sections to search]: :->sects'
+      # @todo Could enumerate these
+      '(-T -t --troff --troff-device)'{-T-,--troff-device=-}'[specify roff output device]::roff output device'
+      '(-u --update)'{-u,--update}'[update database caches]'
+      '(: -)--usage[display brief usage information]'
+      '(: -)'{-V,--version}'[display version information]'
+      "(${(j< >)modes})"{-W,--where-cat}'[display cat file locations]'
+      '--warnings=[enable specified groff warnings]:groff warnings'
+      '(-a --all --regex)--wildcard[treat page name as shell glob]'
+      # @todo Could enumerate these
+      '(-X --gxditview)'{-X-,--gxditview=-}'[display output in gxditview using specified DPI (default: 75)]::DPI'
+      # @todo Post-process how?
+      '(-t --troff -Z --ditroff)'{-Z,--ditroff}'[post-process output for chosen device]'
+    )
+    [[ $variant == darwin* ]] && args+=(
+      # We use _files here because browsers are usually in /Applications, which
+      # typically isn't in PATH
+      '-B+[specify browser to use for HTML files]:Web browser:_files'
+      '-c[reformat source man page]'
+      # @todo -d should be exclusive with this above
+      '(-d)-D[display man page along with debugging information]'
+      '(-D -F --preformat)'{-F,--preformat}'[format man page only (do not display)]'
+      '-H+[specify command to render HTML as text]:HTML pager:_path_commands'
+      # --help and --version are undocumented but functional
+      '(: -)--help[display help information]'
+      # -s is also undocumented; it's provided for compatibility with Solaris
+      '!(-S)-s+: :->sects'
+      '(: -)'{-v,--version}'[display version information]'
+      "(${(j< >)modes})-W[display file locations, one per line, with no other information]"
+    )
+    [[ $variant == netbsd* ]] && args+=(
+      '-h[display only synopsis lines]'
+      '(: -)-p[display manual search path]'
+      '-S+[display only man pages with file names matching specified string]:search string'
+    )
+    [[ $variant == openbsd* ]] && args+=(
+      "(${(j< >)modes})-l+[format and display specified file]:*:::manual file:_files"
+      # @todo Could enumerate these
+      '-S[search manual of specified architecture]:architecture'
+    )
+    [[ $variant == solaris* ]] && args+=(
+      "(${(j< >)modes})-l[display file locations]"
+      '-r[format man page only (do not display)]'
+      '(-a)-s+[specify manual sections to search]: :->sects'
+      # @todo Does this in fact want a file path?
+      '-T+[format man page using specified macro package]:macro package:_files'
+    )
+    [[ $variant == aix* ]] && args+=(
+      '-c[display man page using cat]'
+      '-F[display only first matching entry]'
+      '-m[only search paths specified by -M/MANPATH]'
+      '-r[search remotely]'
+    )
 
-  if [[ $service == man ]] && (( $words[(I)-l] + $words[(I)--local-file] )); then
-    _files || return 0
+    # Strip (most) long options from non-Linux platforms
+    if [[ $variant == darwin* ]]; then
+      args=( ${(M)args:#((#s)|*\))(\*|)(-[^-]|--(help|path|pref|vers))*} )
+    elif [[ $variant != linux* ]]; then
+      args=( ${(M)args:#((#s)|*\))(\*|)-[^-]*} )
+    fi
   fi
 
+  _arguments -s -S : $args '*::: :->man' && return 0
+  [[ -n $state ]] || return 1
+
   if (( ! $#_manpath )); then
     local mp
     mp=( ${(s.:.)$(manpath 2>/dev/null)} )
@@ -23,14 +176,16 @@ _man() {
   fi
 
   (( $#_manpath )) ||
-      _manpath=( /usr/man(-/) /(opt|usr)/(pkg|dt|share|X11R6|local)/(cat|)man(-/) )
+  _manpath=( /usr/man(-/) /(opt|usr)/(pkg|dt|share|X11R6|local)/(cat|)man(-/) )
 
-  integer index=$words[(I)-M]
-  if (( index )); then
-    local opt
-    opt=$words[index+1]
-    _manpath=($opt)
-  fi
+  # Override man path
+  [[ -n ${opt_args[-M]} ]] &&
+  _manpath=( ${(s<:>)opt_args[-M]} )
+
+  # Augment man path
+  [[ $variant == (netbsd|openbsd)* ]] &&
+  [[ -n ${opt_args[-m]} ]] &&
+  _manpath+=( ${(s<:>)opt_args[-m]} )
 
   # `sman' is the SGML manual directory for Solaris 7.
   # 1M is system administrator commands on SVR4
@@ -44,52 +199,158 @@ _man() {
   # $sect_dirname is from the filesystem, the "3" in "/usr/share/man/man3"
   # These are used by _man_pages
   local sect sect_dirname
-  if [[ $OSTYPE = solaris* ]]; then
-    sect=${${words[(R)-s*]#-s}:-$words[$words[(i)-s]+1]}
-    sect="${sect//,/|}"
-  elif [[ -n ${sect:=$words[$words[(i)-S]+1]} || -n ${sect:=$MANSECT} ]]; then
-    sect="${sect//:/|}"
-    sect="${sect//,/|}"
-  elif (( CURRENT > 2 )); then
-    case $words[2] in
-      (-a) sect='*';;
-      (-*) ;;
-      (*)  sect=$words[2];;
-    esac
+
+  # Take care: We can't use the sections from these options until we've finished
+  # completing them; otherwise (e.g.) -s1:<TAB> will give no results
+  if
+    [[ $service != man ]] || [[ $state == sects ]] || (( $+opt_args[-a] ))
+  then
+    sect='*'
+  elif
+    [[ $variant == (darwin|linux)* ]] &&
+    [[ -n ${opt_args[(i)-S|-s|--sections]} ]]
+  then
+    noinsert=1
+    sect=${opt_args[${opt_args[(i)-S|-s|--sections]}]//[:,]/|}
+  elif
+    [[ $variant == (netbsd|openbsd|solaris)* ]] && (( $+opt_args[-s] ))
+  then
+    noinsert=1
+    sect=${opt_args[-s]//,/|}
+  elif [[ $variant == (dragonfly|freebsd)* ]] && (( $+opt_args[-S] )); then
+    noinsert=1
+    sect=${opt_args[-S]//:/|}
+  elif (( CURRENT > 1 )) && [[ $variant != solaris* ]]; then
+    noinsert=1
+    sect=$words[1]
+  elif [[ -n ${sect:=$MANSECT} ]]; then
+    sect=${sect//:/|}
   fi
 
-  if [[ $sect = (<->*|1M|l|n) || $sect = *\|* ]]; then
-    () {
-      local -a sects=( ${(s.|.)sect} )
-      if [[ $sect != (l|n) ]]; then
-        sects=( ${sects%%[^0-9]#} )
-      fi
-      dirs=( $^_manpath/(sman|man|cat)${^sects}*/ )
-    }
-    if [[ $sect == *\|* ]]; then sect="($sect)"; fi
+  # Colons may have been escaped
+  sect=${(Q)sect}
+
+  if [[ $sect = (<->*|[lnopx]) || $sect = *\|* ]]; then
+    sects=( ${(s.|.)sect} )
+    dirs=( $^_manpath/(sman|man|cat)${^sects}/ )
+    sect=${(j<|>)sects}
+    [[ $sect == *'|'* ]] && sect="($sect)"
     awk="\$2 == \"$sect\" {print \$1}"
   else
+    sect=
     dirs=( $^_manpath/(sman|man|cat)*/ )
     awk='{print $1}'
   fi
+
+  # Ignore directories with no pages inside
+  dirs=( ${^dirs}(#qFN) )
+
   # Solaris 11 and on have a man-index directory that doesn't contain manpages
   dirs=( ${dirs:#*/man-index/} )
-  if [[ $OSTYPE = solaris* && ( $words[CURRENT] = -s* || $words[CURRENT-1] == -s ) ]]; then
-    [[ $words[CURRENT] = -s* ]] && compset -P '-s'
-    sects=( ${(o)${dirs##*(man|cat)}%/} )
-    _wanted sections expl 'section' compadd -a sects
-  elif zstyle -t ":completion:${curcontext}:manuals" separate-sections; then
-    typeset -U sects
-    local ret=1
+  sects=( ${(o)${dirs##*(man|cat)}%/} )
+
+  # If we've got this far, we can build our look-up table for descriptions of
+  # the more common sections. Unless otherwise labelled, the more specific ones
+  # come from Solaris or one of its variants
+  (( $#sects )) && () {
+    sect_descs=(
+      0        'library headers'
+      1        'general commands'
+      1cups    'CUPS commands'
+      1m       'maintenance commands'
+      1openssl 'OpenSSL commands'
+      2        'system calls'
+      3        'library functions'
+      3c       'C library functions'
+      3curses  'curses library functions'
+      3elf     'ELF library functions'
+      3f       'Fortran library functions'
+      3lua     'Lua features' # NetBSD
+      3mail    'mailbox library functions'
+      3openssl 'OpenSSL library functions'
+      3pam     'PAM library functions'
+      3pool    'pool configuration library functions'
+      3proc    'process control library functions'
+      3x11     'Xlib functions'
+      3xcurses 'curses library functions [X/Open]'
+      4        'devices and drivers'
+      5        'file formats and conventions'
+      3openssl 'OpenSSL configuration files'
+      6        'games'
+      7        'miscellanea'
+      8        'maintenance commands and procedures'
+      9        'kernel features'
+      9lua     'Lua kernel bindings' # NetBSD
+      l        'local documentation' # AIX, etc.
+      n        'new documentation' # AIX, etc.
+      o        'old documentation' # AIX, etc.
+      p        'public documentation' # AIX, etc.
+      x        'X11 features'
+    )
+
+    # Add POSIX variants
+    for 1 in ${(k)sect_descs}; do
+      [[ $1 == <-> ]] || continue
+      sect_descs+=( "${1}p" "${sect_descs[$1]} [POSIX]" )
+    done
+
+    # Add OS-specific stuff that's too risky for or overrides the general list
+    [[ $OSTYPE == darwin*  ]] && sect_descs+=( n 'Tcl/Tk features' )
+    [[ $OSTYPE == openbsd* ]] && sect_descs+=( 3p 'Perl features' )
+    [[ $OSTYPE == solaris* ]] && sect_descs+=(
+      1t  'Tcl/Tk features'
+      3m  'mathematical library functions'
+      4   'file formats and conventions'
+      5   'miscellanea'
+      7   'special files'
+      7d  'devices'
+      7fs 'file systems'
+      7i  'ioctl requests'
+      7m  'STREAMS modules'
+      7p  'protocols'
+      9e  'driver entry points'
+      9f  'driver functions'
+      9p  'driver properties'
+      9s  'driver data structures'
+    )
+  }
 
-    sects=( ${(o)${dirs##*(man|cat)}%/} )
+  [[ $state == sects ]] && {
+    local s
+    local -a specs
+
+    (( $#sects )) || {
+      _message 'manual section'
+      return 1
+    }
+
+    # Build specs from descriptions
+    for s in $sects; do
+      specs+=( "${s}[${(b)sect_descs[$s]}]" )
+    done
+
+    if [[ $variant == (darwin|dragonfly|freebsd|linux)* ]]; then
+      _values -s : 'manual section' $specs
+    elif [[ $variant == solaris* ]]; then
+      _values -s , 'manual section' $specs
+    else
+      _values 'manual section' $specs
+    fi
+    return
+  }
+
+  if zstyle -t ":completion:${curcontext}:manuals" separate-sections; then
+    local d ret=1
 
     (( $#sects )) || return 1
 
     _tags manuals.${^sects}
     while _tags; do
       for sect_dirname in $sects; do
-        _requested manuals.$sect_dirname expl "manual page, section $sect_dirname" _man_pages &&
+        d=$sect_dirname
+        (( $+sect_descs[$d] )) && d+=" (${sect_descs[$d]})"
+
+        _requested manuals.$sect_dirname expl "manual page, section $d" _man_pages &&
             ret=0
       done
       (( ret )) || return 0
@@ -113,7 +374,7 @@ _man_pages() {
   local pages sopt
 
   # What files corresponding to manual pages can end in.
-  local suf='.((?|<->*)(|.gz|.bz2|.Z|.lzma))'
+  local suf='.((?|<->*|ntcl)(|.gz|.bz2|.Z|.lzma))'
 
   if [[ $PREFIX$SUFFIX = */* ]]; then
     # Easy way to test for versions of man that allow file names.
@@ -138,8 +399,8 @@ _man_pages() {
   # beginning with .<->: that handles problem cases like files called
   # `POSIX.1.5'.
 
-  [[ $OSTYPE = solaris* ]] && sopt='-s '
-  if ((CURRENT > 2)) ||
+  [[ $variant = solaris* ]] && sopt='-s '
+  if ((CURRENT > 1 || noinsert)) ||
       ! zstyle -t ":completion:${curcontext}:manuals.$sect_dirname" insert-sections
   then
     compadd "$@" - ${pages%$~suf}


^ permalink raw reply	[relevance 5%]

* Re: [PATCH] Completion: Improve _man (3)
  @ 2018-06-15 15:14  2%                     ` dana
  0 siblings, 0 replies; 200+ results
From: dana @ 2018-06-15 15:14 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Mikael Magnusson, Zsh workers

On 15 Jun 2018, at 09:39, Mikael Magnusson <mikachu@gmail.com> wrote:
>Please don't use () for 1 {} a b c style loops in actual scripts, it
>is not guaranteed to work.

Someone on IRC told me it was safe :(

Fixed though


On 15 Jun 2018, at 09:47, Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>This lists utime(2), utime(3), utf8(3perl), utf8(7), and utime.h(7posix), among
>others.  Should there be separate groups for each section, i.e., one group for
>each of (2 3 3perl 7 7posix)?  (This is orthogonal to dana's patch.)

Is your system set up so that each of those sub-sections has its own directory,
or are they pooled together into (in this case) 3 and 7? From my own testing,
they should be grouped separately in the former case, but not in the latter due
to the fact that (sub-)sections to complete are derived from the names of the
directories rather than the files.

That could be changed (in fact, assuming it introduces no inaccuracies, i think
it'd be preferable), but it would require reworking the _man_pages stuff, since
we don't know the page names until we get to the bottom of that function. I'll
think about that.


On 15 Jun 2018, at 09:55, Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>Now that you mention it, I think completion functions may not use shortloops
>syntax: compinit doesn't reset that option.

I'd considered that, but that syntax doesn't seem to be affected by short_loops.
That's why i thought it'd be OK.


On 15 Jun 2018, at 09:55, Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>That _still_ leaves us with a «for 1» loop, which I'm not actually sure is a
>documented syntax, but it's useful enough that I'd welcome a regression test
>for it.

Do you mean using 1 as the parameter name, or using for loops without the
`in ...` bit? The latter is specified by POSIX.


Unrelated, but i noticed an issue as i was testing. Maybe this is another case
of me fixing something that actually needs to be a certain way, but, with the
current function, if i create a page test.1dana and then do `man 1d <TAB>` i get
a bunch of results like this:

  perl5101delta perl5121delta perl5141delta ...

That doesn't seem desirable, does it? Is there any reason the glob pattern
shouldn't be restricted to *.$sect* (*.1d*) rather than *$sect* (*1d*)? The only
issue i can think of is that a few systems (like IRIX i believe) don't put the
(sub-)section in the file name at all, but those should already be broken, so...

Anticipating that the answer is no, i've added the dot in the attached patch —
let me know if it seems like a bad choice, though.

dana


diff --git a/Completion/Unix/Command/_man b/Completion/Unix/Command/_man
index 11c2fab7f..7fd92bac5 100644
--- a/Completion/Unix/Command/_man
+++ b/Completion/Unix/Command/_man
@@ -4,6 +4,11 @@
 # - Solaris is seemingly the only OS that doesn't allow the `man n page` syntax;
 #   you must use `man -s n page`
 # - We assume that Linux distributions are using either man-db or mandoc
+# - @todo Would be nice to support completing the initial operand as a section
+#   name (on non-Solaris systems)
+# - @todo We don't support the man-db syntax <name>.<section> (e.g., `ls.1`)
+# - @todo We don't support the man-db feature of 'sub-pages' — that is, treating
+#   pairs of operands like `git diff` as `git-diff`
 # - @todo Option exclusivity isn't super accurate
 # - @todo Solaris man accepts a single hyphen as the first option to disable
 #   paging (like AIX's -c); we don't support that
@@ -220,7 +225,15 @@ _man() {
   elif [[ $variant == (dragonfly|freebsd)* ]] && (( $+opt_args[-S] )); then
     noinsert=1
     sect=${opt_args[-S]//:/|}
-  elif (( CURRENT > 1 )) && [[ $variant != solaris* ]]; then
+  # It's only a small help, but, per man-db, we can avoid treating an initial
+  # operand like `8139too` as a section name by ensuring that only the first
+  # character is a digit. This doesn't do much for stuff like `2to3`, but we can
+  # at least special-case a few common patterns for now
+  elif
+    (( CURRENT > 1 )) &&
+    [[ $variant != solaris* ]] &&
+    [[ ${${(Q)words[1]}##(2to3|7z)*} == ([0-9](|[^0-9[:punct:]]*)|[lnopx]) ]]
+  then
     noinsert=1
     sect=$words[1]
   elif [[ -n ${sect:=$MANSECT} ]]; then
@@ -232,7 +245,25 @@ _man() {
 
   if [[ $sect = (<->*|[lnopx]) || $sect = *\|* ]]; then
     sects=( ${(s.|.)sect} )
-    dirs=( $^_manpath/(sman|man|cat)${^sects}(|.*)/ )
+
+    # Most man implementations support partial matching of a page's
+    # (sub-)section name — e.g., `3per` for `3perl`. The (sub-)section name may
+    # or may not correspond to the directory name (most systems combine
+    # sub-sections), but we'll assume that if it starts with a number and we're
+    # not on Solaris (which doesn't support this feature at all) that we can do
+    # a match against the leading number. This is irritating if you DO want the
+    # exact sub-section specified, but unfortunately there's no way to determine
+    # this programmatically — i guess we could add a style to control it
+    () {
+      for 1; do
+        if [[ $OSTYPE == solaris* || $1 != <->* ]]; then
+          dirs+=( $^_manpath/(sman|man|cat)$1(|.*)/ )
+        else
+          dirs+=( $^_manpath/(sman|man|cat)${1%%[^0-9]#}*/ )
+        fi
+      done
+    } $sects
+
     sect=${(j<|>)sects}
     [[ $sect == *'|'* ]] && sect="($sect)"
     awk="\$2 == \"$sect\" {print \$1}"
@@ -281,7 +312,7 @@ _man() {
       8        'maintenance commands and procedures'
       9        'kernel features'
       9lua     'Lua kernel bindings' # NetBSD
-      l        'local documentation' # AIX, etc.
+      l        'local documentation' # AIX, etc. — TCL on some systems?
       n        'new documentation' # AIX, etc.
       o        'old documentation' # AIX, etc.
       p        'public documentation' # AIX, etc.
@@ -380,14 +411,14 @@ _man_pages() {
   local pages sopt
 
   # What files corresponding to manual pages can end in.
-  local suf='.((?|<->*|ntcl)(|.gz|.bz2|.Z|.lzma))'
+  local suf='.((?|<->*|ntcl)(|.gz|.bz2|.z|.Z|.lzma))'
 
   if [[ $PREFIX$SUFFIX = */* ]]; then
     # Easy way to test for versions of man that allow file names.
     # This can't be a normal man page reference.
     # Try to complete by glob first.
     if [[ -n $sect_dirname ]]; then
-      _path_files -g "*.*$sect_dirname*(|.gz|.bz2|.Z|.lzma)" "$expl[@]"
+      _path_files -g "*.*$sect_dirname*(|.gz|.bz2|.z|.Z|.lzma)" "$expl[@]"
     else
       _path_files -g "*$suf" "$expl[@]" && return
       _path_files "$expl[@]"
@@ -396,7 +427,7 @@ _man_pages() {
   fi
 
   pages=( ${(M)dirs:#*$sect_dirname/} )
-  pages=( ${^pages}/"*$sect${sect:+"*"}" );
+  pages=( ${^pages}/"*${sect:+.$sect"*"}" )
   pages=( ${^~pages}(N:t) )
 
   (($#mrd)) && pages[$#pages+1]=($(awk $awk $mrd))


^ permalink raw reply	[relevance 2%]

* Re: $userdirs empty in non-interactive shells
  @ 2018-06-17 14:14  0% ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2018-06-17 14:14 UTC (permalink / raw)
  To: Zsh hackers list

Any opinion on the question and follow ups below?

I ran into it once again today (when trying to use
${(k)userdirs} as a list of user names).

Original discussion at
https://www.zsh.org/mla/workers/2017/msg01756.html
(worker 42092).

2017-12-07 11:29:50 +0000, Stephane Chazelas:
> $userdirs seems to only work in interactive shells. That seems
> to be due to:
> 
> > mod_export void
> > adduserdir(char *s, char *t, int flags, int always)
> > {
> >     Nameddir nd;
> >     char *eptr;
> >
> >     /* We don't maintain a hash table in non-interactive shells. */
> >     if (!interact)
> >         return;
> 
> Why? It seems to me it would make as much sense to hash it in
> non-interactive shells. It may be useful to be able to flush
> that cache though.
> 
> Note that upon expanding $userdirs in non-interactive shells,
> the code still loops over the pw entries.
> 
> So, if we don't want to change the behaviour, we may still want
> to disable $userdirs altogether in non-interactive shells (and
> update the documentation).


2017-12-08 12:23:45 +0000, Stephane Chazelas:
> 2017-12-07 11:29:50 +0000, Stephane Chazelas:
> [...]
> > >     /* We don't maintain a hash table in non-interactive shells. */
> > >     if (!interact)
> > >         return;
> [...]
> 
> If I remove that code, that breaks one of the tests:
> 
> > --- /tmp/zsh.ztst.2996/ztst.out 2017-12-07 21:31:18.051388484 +0000
> > +++ /tmp/zsh.ztst.2996/ztst.tout        2017-12-07 21:31:18.047388673 +0000
> > @@ -1,4 +1,4 @@
> >  ~/install/cvs/zsh/Test/options.tmp
> > -~/install/cvs/zsh/Test/options.tmp/tmpcd ~/install/cvs/zsh/Test/options.tmp
> > -~/install/cvs/zsh/Test/options.tmp/tmpcd ~/install/cvs/zsh/Test/options.tmp/tmpcd ~/install/cvs/zsh/Test/options.tmp
> > -~/install/cvs/zsh/Test/options.tmp/tmpcd ~/install/cvs/zsh/Test/options.tmp/tmpcd ~/install/cvs/zsh/Test/options.tmp
> > +~cdablevar2 ~/install/cvs/zsh/Test/options.tmp
> > +~cdablevar2 ~cdablevar2 ~/install/cvs/zsh/Test/options.tmp
> > +~cdablevar2 ~cdablevar2 ~/install/cvs/zsh/Test/options.tmp
> > Test ./E01options.ztst failed: output differs from expected as shown above for:
> >   dirs
> >   pushd $mydir/tmpcd
> >   dirs
> >   pushd $mydir/tmpcd
> >   dirs
> >   setopt pushdignoredups
> >   pushd $mydir/tmpcd
> >   dirs
> >   unsetopt pushdignoredups
> >   popd >/dev/null
> >   popd >/dev/null
> > Was testing: PUSHD_IGNOREDUPS option
> > ./E01options.ztst: test failed.
> 
> That's because an earlier test does a cd a:
> 
> cd cdablevar2
> 
> under setopt cdablevars with cdablevar2 containing that same
> tmpcd directory, and so an entry is added in that hash.
> 
> That's one case where an earlier test has side effects on later
> tests.
> 
> While the documentation doesn't mentions that that "hashing" only
> happens in interactve shells, the test code acknowleges it to
> some extent:
> 
> ># Test various shell options.
> ># Interactive options not tested here:
> [...]
> >#    AUTO_NAME_DIRS  (named directory table not maintained)
> 
> That also means:
> 
> $ zsh -fc 'cd ~bin; dirs'
> /bin
> $ zsh -fic 'cd ~bin; dirs'
> ~bin
> 
> BTW, I just realised that named dirs in variables took precedence
> over user home dirs:
> 
> $ zsh -fc 'cd -P -- ~bin && pwd'
> /bin
> $ bin=/tmp zsh -fc 'cd -P -- ~bin && pwd'
> /tmp
> 
> Is that intentional? I can't say I like the  idea. It would need
> to be disabled in "sh" emulation if we wanted to be POSIX
> compliant in that regard (but then there are a few other
> namespace clashes that would need to be addressed like for
> keywords, special variables if we wanted to go all the way
> there).


2017-12-08 19:46:52 +0000, Stephane Chazelas:
> 2017-12-08 12:31:50 +0000, Peter Stephenson:
> > On Fri, 8 Dec 2017 12:23:46 +0000
> > Stephane Chazelas <stephane.chazelas@gmail.com> wrote:
> > > BTW, I just realised that named dirs in variables took precedence
> > > over user home dirs:
> > > 
> > > $ zsh -fc 'cd -P -- ~bin && pwd'
> > > /bin
> > > $ bin=/tmp zsh -fc 'cd -P -- ~bin && pwd'
> > > /tmp
> > > 
> > > Is that intentional?
> > 
> > Yes, it allows you to override locations associated with users without
> > jumping through lots of hoops.
> [...]
> 
> Ah OK.
> 
> What would be a typical use case?
> 
> Is that the reason why named dirs were introduced in the first place?


^ permalink raw reply	[relevance 0%]

* [PATCH] Completion: Minor improvements to _comm and _sed
@ 2018-06-17 21:49  4% dana
  2018-06-20 13:06  8% ` Oliver Kiddle
  0 siblings, 1 reply; 200+ results
From: dana @ 2018-06-17 21:49 UTC (permalink / raw)
  To: Zsh workers

Some minor improvements to _comm and _sed:

* Call _arguments with -S and (where appropriate) -A
* Fix some option-spec syntax errors in _comm
* Add some missing options to _comm
* Don't complete `sed -r` on Darwin
* Change all option specs in _sed to use -o+/--opt= instead of -o-/--opt=- (i'm
  not aware of any platform where the latter is appropriate)

dana


diff --git a/Completion/Unix/Command/_comm b/Completion/Unix/Command/_comm
index d1d835f6a..f8c535a44 100644
--- a/Completion/Unix/Command/_comm
+++ b/Completion/Unix/Command/_comm
@@ -1,6 +1,6 @@
 #compdef comm gcomm
 
-local -a args
+local -a args aopts=( -A '-*' )
 
 args=(
   '-1[suppress lines unique to first file]'
@@ -11,10 +11,13 @@ args=(
 )
 
 if _pick_variant gnu=GNU unix --version; then
+  aopts=( )
   args+=(
     '(--nocheck-order)--check-order[check input is correctly sorted]'
     "(--check-order)--nocheck-order[don't check input is correctly sorted]"
-    '--output-delimiter=:delimiter'
+    '--output-delimiter=[specify column delimiter]:delimiter'
+    '--total[display summary]'
+    '(-z --zero-terminated)'{-z,--zero-terminated}'[use NUL as line delimiter]'
     '(- : *)--version[display version information]'
     '(- : *)--help[display help information]'
   )
@@ -24,4 +27,4 @@ elif [[ $OSTYPE = (openbsd|netbsd)* ]]; then
   args+=( '-f[case insensitive comparison of lines]' )
 fi
 
-_arguments -s $args
+_arguments -s -S $aopts : $args

diff --git a/Completion/Unix/Command/_sed b/Completion/Unix/Command/_sed
index 15b92a634..80218051b 100644
--- a/Completion/Unix/Command/_sed
+++ b/Completion/Unix/Command/_sed
@@ -1,11 +1,12 @@
 #compdef sed gsed psed s2p
 
-local args inplace extended
+local inplace extended
+local -a args aopts=( -A '-*' )
 
 args=(
   '(-n --quiet --silent)'{-n,--quiet,--silent}'[suppress automatic printing of pattern space]'
-  '(1)*'{-e,--expression=-}'[specify sed commands to run]:sed script'
-  '(1)*'{-f,--file=-}'[add contents of file to commands to run]:file:_files'
+  '(1)*'{-e+,--expression=}'[specify sed commands to run]:sed script'
+  '(1)*'{-f+,--file=}'[add contents of file to commands to run]: :_files'
   '(-e)1: :_guard "^-*" sed script'
   '*:input file:_files'
 )
@@ -18,11 +19,12 @@ if [[ $service = (psed|s2p) ]]; then
     '-a[delay opening files listed with w function]'
   )
 elif _pick_variant gnu=GNU unix --version; then
+  aopts=( )
   args+=(
     '--follow-symlinks[follow symlinks when processing in place]'
-    '(-i --in-place)'{-i-,--in-place=-}$inplace
+    '(-i --in-place)'{-i+,--in-place=}$inplace
     '(-c --copy)'{-c,--copy}'[copy instead of rename when shuffling files in in-place mode]'
-    '(-l --line-length)'{-l,--line-length=-}'[specify line-wrap length for the l command]'
+    '(-l --line-length)'{-l+,--line-length=}'[specify line-wrap length for the l command]'
     '(-r)--posix[disable GNU extensions]'
     '(-E -r --regexp-extended)'{-E,-r,--regexp-extended}$extended
     '(-s --separate)'{-s,--separate}'[consider files separately instead of as a combined stream]'
@@ -37,20 +39,21 @@ else
   case $OSTYPE in
     openbsd*|freebsd*|netbsd*|darwin*|dragonfly*)
       args+=(
-	'(-r -E)'{-r,-E}$extended
+	'(-r -E)'-E$extended
 	'-a[delay opening files listed with w function]'
       )
     ;|
-    darwin*|freebsd*|netbsd*|openbsd*)  args+=( '-i'$inplace ) ;|
+    openbsd*|freebsd*|netbsd*|dragonfly*) args+=( '(-r -E)'-r$extended ) ;|
+    darwin*|freebsd*|netbsd*|openbsd*) args+=( '-i+'$inplace ) ;|
     darwin*|freebsd*|netbsd*) args+=( '-l[make output line buffered]' ) ;|
     freebsd*) args+=( '-u[disable data buffering]' ) ;|
     freebsd*|netbsd*)
       args+=(
-        '-I[edit files in-place, treating all files as a single input stream]::suffix for backup'
+        '-I+[edit files in-place, treating all files as a single input stream]::suffix for backup'
       )
     ;;
     openbsd*) args+=( '-u[make output line buffered]' ) ;;
   esac
 fi
 
-_arguments -s "$args[@]"
+_arguments -s -S $aopts : "$args[@]"


^ permalink raw reply	[relevance 4%]

* Re: zsh/stat: output atime/mtime/ctime with nanoseconds
  @ 2018-06-20  5:12  5%   ` dana
  0 siblings, 0 replies; 200+ results
From: dana @ 2018-06-20  5:12 UTC (permalink / raw)
  To: Oliver Kiddle; +Cc: zsh-workers

On 19 Jun 2018, at 14:17, Oliver Kiddle <okiddle@yahoo.co.uk> wrote:
>Checking back, it seems the patch in workers/24059 was never applied.

How is this? Building on the earlier patch, it:

* Adds a zgettime() function to conveniently get nanosecond-precision clock
  time, or at least something approximating it (Peter had independently added ns
  precision to zsh/datetime between then and now, so i figured it was best to
  centralize that functionality)

* Updates ztrftime() to support %9. and %N

* Updates prompt expansion to format times with nanoseconds

* Updates zsh/datetime to use zgettime()

* Updates zsh/stat to format times with nanoseconds

* Updates documentation and tests

I'm not sure about the error-handling / fall-back stuff in zgettime(). Is an
error message necessary? I put one in because pws had one, but he wasn't falling
back to gettimeofday() like i am. Also the ret++/ret-- stuff may be a bit
strange; i couldn't think of anything cleaner.

One remaining (not new) issue is the fact that the strftime built-in doesn't
support sub-second times. That might not be feasible for `strftime -r`, but the
regular one could accept an optional third operand maybe?

dana


diff --git a/configure.ac b/configure.ac
index 7644ebe52..53c30c2cf 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1113,6 +1113,14 @@ zsh_TYPE_EXISTS([
 #endif
 ], struct timezone)
 
+dnl Check for struct timespec since POSIX only gained it in 2008
+zsh_TYPE_EXISTS([
+#define _GNU_SOURCE 1
+#ifdef HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+], struct timespec)
+
 dnl Check for utmp structures, for watch
 zsh_TYPE_EXISTS([
 #ifdef HAVE_SYS_TYPES_H
diff --git a/Src/zsh_system.h b/Src/zsh_system.h
index 5339b496f..8289ee97c 100644
--- a/Src/zsh_system.h
+++ b/Src/zsh_system.h
@@ -250,6 +250,14 @@ struct timezone {
 };
 #endif
 
+/* Used to provide compatibility with clock_gettime() */
+#if !defined(HAVE_STRUCT_TIMESPEC) && !defined(ZSH_OOT_MODULE)
+struct timespec {
+    time_t tv_sec;
+    long tv_nsec;
+};
+#endif
+
 /* There's more than one non-standard way to get at this data */
 #if !defined(HAVE_STRUCT_DIRENT_D_INO) && defined(HAVE_STRUCT_DIRENT_D_STAT)
 # define d_ino d_stat.st_ino
diff --git a/Src/compat.c b/Src/compat.c
index a130d9264..7b5c4411c 100644
--- a/Src/compat.c
+++ b/Src/compat.c
@@ -94,6 +94,39 @@ gettimeofday(struct timeval *tv, struct timezone *tz)
 #endif
 
 
+/* Provide clock time with nanoseconds */
+
+/**/
+mod_export int
+zgettime(struct timespec *ts)
+{
+    int ret = -1;
+
+#ifdef HAVE_CLOCK_GETTIME
+    struct timespec dts;
+    if (clock_gettime(CLOCK_REALTIME, &dts) < 0) {
+	zwarn("unable to retrieve time: %e", errno);
+	ret--;
+    } else {
+	ret++;
+	ts->tv_sec = (time_t) dts.tv_sec;
+	ts->tv_nsec = (long) dts.tv_nsec;
+    }
+#endif
+
+    if (ret) {
+	struct timeval dtv;
+	struct timezone dtz;
+	gettimeofday(&dtv, &dtz);
+	ret++;
+	ts->tv_sec = (time_t) dtv.tv_sec;
+	ts->tv_nsec = (long) dtv.tv_usec * 1000;
+    }
+
+    return ret;
+}
+
+
 /* compute the difference between two calendar times */
 
 /**/

diff --git a/Src/utils.c b/Src/utils.c
index b41851700..ee2ad207f 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -3224,7 +3224,7 @@ ztrftimebuf(int *bufsizeptr, int decr)
 
 /**/
 mod_export int
-ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long usec)
+ztrftime(char *buf, int bufsize, char *fmt, struct tm *tm, long nsec)
 {
     int hr12;
 #ifdef HAVE_STRFTIME
@@ -3299,15 +3299,15 @@ morefmt:
 	    case '.':
 		if (ztrftimebuf(&bufsize, digs))
 		    return -1;
-		if (digs > 6)
-		    digs = 6;
-		if (digs < 6) {
+		if (digs > 9)
+		    digs = 9;
+		if (digs < 9) {
 		    int trunc;
-		    for (trunc = 5 - digs; trunc; trunc--)
-			usec /= 10;
-		    usec  = (usec + 5) / 10;
+		    for (trunc = 8 - digs; trunc; trunc--)
+			nsec /= 10;
+		    nsec = (nsec + 8) / 10;
 		}
-		sprintf(buf, "%0*ld", digs, usec);
+		sprintf(buf, "%0*ld", digs, nsec);
 		buf += digs;
 		break;
 	    case '\0':
@@ -3369,6 +3369,12 @@ morefmt:
 		    *buf++ = '0' + tm->tm_min / 10;
 		*buf++ = '0' + tm->tm_min % 10;
 		break;
+	    case 'N':
+		if (ztrftimebuf(&bufsize, 9))
+		    return -1;
+		sprintf(buf, "%09ld", nsec);
+		buf += 9;
+		break;
 	    case 'S':
 		if (tm->tm_sec > 9 || !strip)
 		    *buf++ = '0' + tm->tm_sec / 10;

diff --git a/Src/prompt.c b/Src/prompt.c
index 95da52559..959ed8e3d 100644
--- a/Src/prompt.c
+++ b/Src/prompt.c
@@ -273,8 +273,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
     char *ss, *hostnam;
     int t0, arg, test, sep, j, numjobs, len;
     struct tm *tm;
-    struct timezone dummy_tz;
-    struct timeval tv;
+    struct timespec ts;
     time_t timet;
     Nameddir nd;
 
@@ -664,8 +663,8 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
 			tmfmt = "%l:%M%p";
 			break;
 		    }
-		    gettimeofday(&tv, &dummy_tz);
-		    tm = localtime(&tv.tv_sec);
+		    zgettime(&ts);
+		    tm = localtime(&ts.tv_sec);
 		    /*
 		     * Hack because strftime won't say how
 		     * much space it actually needs.  Try to add it
@@ -675,7 +674,7 @@ putpromptchar(int doprint, int endchar, unsigned int *txtchangep)
 		     */
 		    for(j = 0, t0 = strlen(tmfmt)*8; j < 3; j++, t0*=2) {
 			addbufspc(t0);
-			if ((len = ztrftime(bv->bp, t0, tmfmt, tm, tv.tv_usec))
+			if ((len = ztrftime(bv->bp, t0, tmfmt, tm, ts.tv_nsec))
 			    >= 0)
 			    break;
 		    }

diff --git a/Src/Modules/datetime.c b/Src/Modules/datetime.c
index 6e9047bc5..be378b347 100644
--- a/Src/Modules/datetime.c
+++ b/Src/Modules/datetime.c
@@ -180,66 +180,30 @@ getcurrentsecs(UNUSED(Param pm))
 }
 
 static double
-getcurrentrealtime(Param pm)
+getcurrentrealtime(UNUSED(Param pm))
 {
-#ifdef HAVE_CLOCK_GETTIME
     struct timespec now;
-
-    if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
-	zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno);
-	return (double)0.0;
-    }
-
+    zgettime(&now);
     return (double)now.tv_sec + (double)now.tv_nsec * 1e-9;
-#else
-    struct timeval now;
-    struct timezone dummy_tz;
-
-    (void)pm;
-    gettimeofday(&now, &dummy_tz);
-
-    return (double)now.tv_sec + (double)now.tv_usec * 1e-6;
-#endif
 }
 
 static char **
-getcurrenttime(Param pm)
+getcurrenttime(UNUSED(Param pm))
 {
     char **arr;
     char buf[DIGBUFSIZE];
-
-#ifdef HAVE_CLOCK_GETTIME
     struct timespec now;
 
-    if (clock_gettime(CLOCK_REALTIME, &now) < 0) {
-	zwarn("%s: unable to retrieve time: %e", pm->node.nam, errno);
-	return NULL;
-    }
-
-    arr = (char **)zhalloc(3 * sizeof(*arr));
-    sprintf(buf, "%ld", (long)now.tv_sec);
-    arr[0] = dupstring(buf);
-    sprintf(buf, "%ld", now.tv_nsec);
-    arr[1] = dupstring(buf);
-    arr[2] = NULL;
-
-    return arr;
-#else
-    struct timeval now;
-    struct timezone dummy_tz;
-
-    (void)pm;
-    gettimeofday(&now, &dummy_tz);
+    zgettime(&now);
 
     arr = (char **)zhalloc(3 * sizeof(*arr));
     sprintf(buf, "%ld", (long)now.tv_sec);
     arr[0] = dupstring(buf);
-    sprintf(buf, "%ld", (long)now.tv_usec * 1000);
+    sprintf(buf, "%ld", (long)now.tv_nsec);
     arr[1] = dupstring(buf);
     arr[2] = NULL;
 
     return arr;
-#endif
 }
 
 static struct builtin bintab[] = {

diff --git a/Src/Modules/stat.c b/Src/Modules/stat.c
index 66baa1292..50a6a9bb2 100644
--- a/Src/Modules/stat.c
+++ b/Src/Modules/stat.c
@@ -188,7 +188,7 @@ static char *timefmt;
 
 /**/
 static void
-stattimeprint(time_t tim, char *outbuf, int flags)
+stattimeprint(time_t tim, long nsecs, char *outbuf, int flags)
 {
     if (flags & STF_RAW) {
 	sprintf(outbuf, "%ld", (unsigned long)tim);
@@ -199,7 +199,7 @@ stattimeprint(time_t tim, char *outbuf, int flags)
 	char *oend = outbuf + strlen(outbuf);
 	/* Where the heck does "40" come from? */
 	int len = ztrftime(oend, 40, timefmt, (flags & STF_GMT) ? gmtime(&tim) :
-			   localtime(&tim), 0L);
+			   localtime(&tim), nsecs);
 	if (len > 0)
 	    metafy(oend, len, META_NOALLOC);
 	if (flags & STF_RAW)
@@ -291,15 +291,27 @@ statprint(struct stat *sbuf, char *outbuf, char *fname, int iwhich, int flags)
 	break;
 
     case ST_ATIM:
-	stattimeprint(sbuf->st_atime, optr, flags);
+#ifdef GET_ST_ATIME_NSEC
+	stattimeprint(sbuf->st_atime, GET_ST_ATIME_NSEC(*sbuf), optr, flags);
+#else
+	stattimeprint(sbuf->st_atime, 0L, optr, flags);
+#endif
 	break;
 
     case ST_MTIM:
-	stattimeprint(sbuf->st_mtime, optr, flags);
+#ifdef GET_ST_MTIME_NSEC
+	stattimeprint(sbuf->st_mtime, GET_ST_MTIME_NSEC(*sbuf), optr, flags);
+#else
+	stattimeprint(sbuf->st_mtime, 0L, optr, flags);
+#endif
 	break;
 
     case ST_CTIM:
-	stattimeprint(sbuf->st_ctime, optr, flags);
+#ifdef GET_ST_CTIME_NSEC
+	stattimeprint(sbuf->st_ctime, GET_ST_CTIME_NSEC(*sbuf), optr, flags);
+#else
+	stattimeprint(sbuf->st_ctime, 0L, optr, flags);
+#endif
 	break;
 
     case ST_BLKSIZE:

diff --git a/Test/D01prompt.ztst b/Test/D01prompt.ztst
index 11f18dc71..56b7c294a 100644
--- a/Test/D01prompt.ztst
+++ b/Test/D01prompt.ztst
@@ -108,6 +108,14 @@
   if (( $date2[7,8] != $date3[1,2] )); then
     print "Years do not agree in $date2, $date3"
   fi
+  # These are somewhat questionable, but...
+  ns=( ${="$(print -P '%D{%9.} %D{%N}')"} )
+  if [[ $ns[1] != [0-9](#c9) ]] || [[ $ns[2] != [0-9](#c9) ]]; then
+    print "Nanosecond lengths/formats are not as expected in $ns[1], $ns[2]"
+  fi
+  if (( ($ns2[2] - $ns[1]) > 5000000 )); then
+    print "Nanoseconds differ too much in $ns[1], $ns[2]"
+  fi
 0:Dates produced by prompt escapes
 
   mkdir foo

diff --git a/Doc/Zsh/prompt.yo b/Doc/Zsh/prompt.yo
index 3c8f2a094..909012c8e 100644
--- a/Doc/Zsh/prompt.yo
+++ b/Doc/Zsh/prompt.yo
@@ -198,11 +198,15 @@ endsitem()
 In addition, if the system supports the POSIX tt(gettimeofday) system
 call, tt(%.) provides decimal fractions of a second since the epoch with
 leading zeroes.  By default three decimal places are provided, but a
-number of digits up to 6 may be given following the tt(%); hence tt(%6.)
-outputs microseconds.  A typical example of this is the format
-`tt(%D{%H:%M:%S.%.})'.
+number of digits up to 9 may be given following the tt(%); hence tt(%6.)
+outputs microseconds, and tt(%9.) outputs nanoseconds.  (The latter
+requires a nanosecond-precision tt(clock_gettime); systems lacking this
+will return a value multiplied by the appropriate power of 10.)  A typical
+example of this is the format `tt(%D{%H:%M:%S.%.})'.
 
-The GNU extension that a `tt(-)' between the tt(%) and the
+The GNU extension tt(%N) is handled as a synonym for tt(%9.).
+
+Additionally, the GNU extension that a `tt(-)' between the tt(%) and the
 format character causes a leading zero or space to be stripped
 is handled directly by the shell for the format characters tt(d), tt(f),
 tt(H), tt(k), tt(l), tt(m), tt(M), tt(S) and tt(y); any other format

diff --git a/Doc/Zsh/mod_stat.yo b/Doc/Zsh/mod_stat.yo
index 96349061e..9caed1e45 100644
--- a/Doc/Zsh/mod_stat.yo
+++ b/Doc/Zsh/mod_stat.yo
@@ -114,7 +114,11 @@ named files; no list of file names is allowed in this case.
 )
 item(tt(-F) var(fmt))(
 Supplies a tt(strftime) (see manref(strftime)(3)) string for the
-formatting of the time elements.  The tt(-s) option is implied.
+formatting of the time elements.  The format string supports all of the
+zsh extensions described in
+ifzman(the section EXPANSION OF PROMPT SEQUENCES in zmanref(zshmisc))\
+ifnzman(noderef(Prompt Expansion)).
+The tt(-s) option is implied.
 )
 item(tt(-g))(
 Show the time elements in the GMT time zone.  The


^ permalink raw reply	[relevance 5%]

* Re: [PATCH] Completion: Minor improvements to _comm and _sed
  2018-06-17 21:49  4% [PATCH] Completion: Minor improvements to _comm and _sed dana
@ 2018-06-20 13:06  8% ` Oliver Kiddle
  0 siblings, 0 replies; 200+ results
From: Oliver Kiddle @ 2018-06-20 13:06 UTC (permalink / raw)
  To: dana; +Cc: Zsh workers

On 17 Jun, dana wrote:
>
> * Change all option specs in _sed to use -o+/--opt= instead of -o-/--opt=- (i'm
>   not aware of any platform where the latter is appropriate)

GNU sed appears to require that for the -i option as far as I can tell:
% sed -i .bak 's/d/f/'
sed: -e expression #1, char 1: unknown command: `.'
Same with --in-place: you need the =.
-i is the only one where the argument is optional. Not allowing space
separators often goes along with optional arguments.

It also appears that -i implies -s for GNU sed.

The following _sed patch, does quite a bit more besides by completing
the sed functions in the sed expression itself. This covers all the GNU
sed extension commands but I didn't check through sed on various other
systems for variations in the commands. I've tried to make the
patterns reasonably forgiving of odd syntax but _regex_arguments tends
to lead to somewhat unforgiving completions.

_expand mangles some things like single-quoted newlines which doesn't
especially help.

Oliver

diff --git a/Completion/Unix/Command/_sed b/Completion/Unix/Command/_sed
index 80218051b..f03278364 100644
--- a/Completion/Unix/Command/_sed
+++ b/Completion/Unix/Command/_sed
@@ -1,16 +1,60 @@
 #compdef sed gsed psed s2p
 
-local inplace extended
-local -a args aopts=( -A '-*' )
+local variant inplace extended ign sep separator
+local -i nest=0
+local -a args aopts sedexpr cmds_none cmds_slash cmds_end substflags expl bsnl nl labels excl dedup
+local -a step range negate mods
+aopts=( -A '-*' )
+bsnl=( $'\\\n' )
+nl=$'\n'
+compquote nl
 
+cmds_none=(
+  '{:start group'
+  'q:quit after printing pattern space'
+  'h:copy pattern space to hold space'
+  '\::place label'
+  '#:comment'
+  '=:print current line number'
+  'a:append text'
+  'i:insert text'
+  'r:append contents of file'
+  'b:branch'
+  't:branch if s command has been successful'
+  'c:replace line with text'
+  'l:list current line in visually unambiguous form'
+  'w:write pattern space to file'
+)
+cmds_slash=(
+  's:substitute regex'
+  'y:transliterate characters'
+)
+cmds_end=(
+  'd:delete pattern space'
+  'D:delete up to the first newline in the pattern space'
+  'g:copy hold space to pattern space'
+  'G:append hold space to pattern space'
+  'H:append pattern space to hold space'
+  'n:read the next line of input into pattern space'
+  'N:append the next line of input to the pattern space'
+  'p:print the current pattern space'
+  'P:print upto the first newline of the current pattern space'
+  'x:exchange hold and pattern spaces'
+  '}:end group'
+)
+substflags=(
+  'g:replace all matches to the regular expression'
+  'p:print new pattern space if substitution made'
+  'w:write result to named file if substitution made'
+)
 args=(
   '(-n --quiet --silent)'{-n,--quiet,--silent}'[suppress automatic printing of pattern space]'
-  '(1)*'{-e+,--expression=}'[specify sed commands to run]:sed script'
-  '(1)*'{-f+,--file=}'[add contents of file to commands to run]: :_files'
-  '(-e)1: :_guard "^-*" sed script'
+  '(1)*'{-e+,--expression=}'[specify sed commands to run]:sed script:_sed_expressions'
+  '(1)*'{-f+,--file=}'[add contents of file to commands to run]:file:_files'
+  '(-e)1:sed script:_sed_expressions'
   '*:input file:_files'
 )
-inplace='[edit files in-place, running scripts separately for each file]::suffix for backup'
+inplace='[edit files in-place, running scripts separately for each file]:: :_guard "^(*[@/; ]*|?(#c6,)|-*)" "suffix for backup"'
 extended='[use extended regular expressions]'
 
 if [[ $service = (psed|s2p) ]]; then
@@ -18,11 +62,12 @@ if [[ $service = (psed|s2p) ]]; then
     "${(@)args:#(|\(*\))(|\*)--*}"
     '-a[delay opening files listed with w function]'
   )
-elif _pick_variant gnu=GNU unix --version; then
+elif _pick_variant -r variant gnu=GNU unix --version; then
   aopts=( )
+  (( $#words > 2 )) && ign='!'
   args+=(
     '--follow-symlinks[follow symlinks when processing in place]'
-    '(-i --in-place)'{-i+,--in-place=}$inplace
+    '(-i --in-place -s --separate)'{-i-,--in-place=-}$inplace
     '(-c --copy)'{-c,--copy}'[copy instead of rename when shuffling files in in-place mode]'
     '(-l --line-length)'{-l+,--line-length=}'[specify line-wrap length for the l command]'
     '(-r)--posix[disable GNU extensions]'
@@ -31,9 +76,28 @@ elif _pick_variant gnu=GNU unix --version; then
     '--sandbox[block commands that can affect the system (r/w/W/e)]'
     '(-u --unbuffered)'{-u,--unbuffered}'[disable data buffering]'
     '(-z --null-data)'{-z,--null-data}'[separate lines by NUL characters]'
-    '(- 1 :)--help[print program usage]'
-    '(- 1 :)--version[print program version]'
+    "${ign}(- 1 :)--help[print program usage]"
+    "${ign}(- 1 :)--version[print program version]"
   )
+  if [[ -z ${words[(r)--posix]} ]]; then
+    cmds_none+=(
+      'R:append a line from file'
+      'T:branch if no s command has been successful'
+      'W:write the first line of pattern space to file'
+      'v:fail if GNU extensions not supported or older than specified version'
+    )
+    cmds_end+=(
+      "e:execute a command and include it's output"
+      'F:print the filename of the current input file'
+      'Q:quit'
+      'z:empty the pattern space'
+    )
+    substflags+=(
+      'e:execute pattern space as a command and replace with result'
+      {i,I}':case-insensitive regular expression matching'
+      {m,M}':multi-line matching'
+    )
+  fi
 else
   args=( "${(@)args:#(|\(*\))(|\*)--*}" )
   case $OSTYPE in
@@ -49,11 +113,142 @@ else
     freebsd*) args+=( '-u[disable data buffering]' ) ;|
     freebsd*|netbsd*)
       args+=(
-        '-I+[edit files in-place, treating all files as a single input stream]::suffix for backup'
+        '-I+[edit files in-place, treating all files as a single input stream]:: :_guard "^(*[@/; \\\]*|?(#c6,)|-*)" "suffix for backup"'
       )
     ;;
     openbsd*) args+=( '-u[make output line buffered]' ) ;;
   esac
 fi
 
+zstyle -s ":completion:${curcontext}:address-forms" list-separator separator || separator=--
+step=( "~ $separator step" )
+negate=( "! $separator negated" )
+range=( ", $separator range" )
+mods=( "I $separator case-insensitive" "M $separator multi-line" )
+
+sedexpr=(
+  \( /$'*\0[ \t\n]#'/ \) # strip off any preceding arguments - handled by _arguments
+  \(
+    # Handle an optional address range
+    \(
+      \(
+        \(
+          '///' '/[^/]#//' ':regexes:regex:' # skip /pattern/
+        \|
+          '/\\(?)/' -'sep=${match#?}' # handle \xpatternx
+          \( '/\?/' \| '/?/' -'[[ $match != $sep ]]' \) \# '/?/' -'[[ $match = $sep ]]' ':regexes:regex:'
+        \)
+        $'/[ \t]#/'
+        \( \| '/[IM]##/' -'dedup=( ${(s..)match} )' ':address-forms:address form:compadd -S "" -d mods -F dedup I M' \) \#
+      \|
+        '/([0-9]##|$)[ \t]#/' # line number
+        \(
+          '/\~[ \t]#/' # addr1~N
+          '/[0-9]##[ \t]#/' ': _message -e steps "number - match where line number is a multiple"'
+        \| '//' ':address-forms:address form:compadd -S "" -d step \~' \)
+      \|
+        '/[]/' ': _guard "^([sy]|[^0-9$/\\\]*)" "address - line number or /pattern/"'
+      \)
+      \( # range end, also optional
+        '/[ \t]#,[ \t]#/' -'excl=( \\\# : )' # exclude comments and labels after ranges
+        \(
+          '///' '/[^/]#//' ':regexes:regex:' # handle /pattern/
+        \|
+          '/\\(?)/' -'sep=${match#?}' # handle \xpatternx
+          \( '/\?/' \| '/?/' -'[[ $match != $sep ]]' \) \# '/?/' -'[[ $match = $sep ]]' ':regexes:regex - 2:'
+        \|
+          '/+[ \t]#/' # addr1,+N
+          '/[0-9]##/' ': _message -e number "number of following lines"'
+        \|
+          '/\~[ \t]#/' # addr1,~N
+          '/[0-9]##/' ': _message -e number "following lines until line number is a multiple of specified number"'
+        \|
+          '/([0-9]##|$)/' # line number
+        \|
+          '/[]/' ': _message -e ranges "ending line - [+~]number, $ or /pattern/"'
+        \)
+      \|
+        '//' -'excl=( \\\# : )' ':address-forms:address form:compadd -S "" -d range ,'
+      \)
+      \(
+        '/!/' ':address-forms:address form:compadd -S "" -d negate !'
+      \| \)
+    \| // -'excl=( \{ )' \) # { ... } is only useful following a range so exclude {
+
+    $'/[ \t]#/' -'(( nest )) || excl+=( \} )' # whitespace + exclude } without preceding {
+    \( # First commands, for which the pattern fully terminates them
+      '/e[ \t]#/' $'/((\\\n|\\[^\n]|[^\\\n])##\n|[\n;])/' ':commands:command:_cmdstring' # GNU extension
+    \|
+      $'/{[ ;\t\n]#/' -'((++nest,1))' # opening brace
+    \|
+      '/\#/' # comments
+      $'/[^\n]##\n[\n; \t]#/' ':comments:comment:'
+    \|
+      $'/[aci]/' # a, c and i commands
+      \(
+        $'/[ \t]#/' -'[[ $variant = gnu && $+opt_args[--posix] = 0 ]]' # GNU allows, e.g. 'c string'
+      \|
+        $'/[ \t]#/' $'/\\\n/' ':newlines:newline:compadd -Q -S "" "$bsnl"'
+      \)
+      $'/(\\\n|\\[^\n]|[^\\\n])##\n[\n; \t]#/' ':strings:string:'
+    \|
+      $'/[RrwW][ \t]#/' $'/[^\n]##\n[\n; \t]#/' ':files:file:_files -S ""'
+    \| # Now commands with shared termination handling
+      \(
+        # branches/labels, GNU sed allows an empty label
+        $'/[:btT][ \t]#/' $'/[^ \t\n;]#/' $'%[ \t\n;]%' -'labels+=( $match )'
+            ':labels:label: _wanted -x labels expl label compadd -S "" -a labels'
+      \|
+        '/l/' $'/[ \t]#<->/' ':width:width:'
+      \|
+        '/s(?)/' -'sep=${match#s}' # Substitutions
+        \( '/\\?/' \| '/?/' -'[[ $match != $sep ]]' \) \#
+        '/?/' -'[[ $match = $sep ]]' ':regexes:source regex:'
+        \( '/\\?/' \| '/?/' -'[[ $match != $sep ]]' \) \#
+        '/?/' -'[[ $match = $sep ]]' ':regexes:substitute string (back-references with & and \1 .. \9):'
+        \( # Substitution flags
+          $'/w[ \t]#/' $'/[^\n]##/' $'%\n%' ':files:file:_files -S ""'
+        \|
+          # pass existing flags, building exclusion list from them
+          $'/[gpiImM0-9]#/' -'excl=( ${(s..)${${${match/[iI]/iI}/[mM]/mM}}/e/ew} )'
+          \(
+            '//' -'[[ -z ${excl[(r)[0-9]]} ]]' # exclude if numbers already there
+            '//' '%[^egpiImM0-9]%' ': _message -e numbers "number - substitute nth match"'
+          \|
+            '//' '%[^egpiImM0-9]%' $':flags:flag: _describe -t flags flag substflags -S "" -F excl'
+          \)
+        \)
+      \|
+        '/y(?)/' -'sep=${match#y}' # Character transliterations
+        \( '/\?/' \| '/?/' -'[[ $match != $sep ]]' \) \# '/?/' -'[[ $match = $sep ]]' ':source:source:'
+        \( '/\?/' \| '/?/' -'[[ $match != $sep ]]' \) \# '/?/' -'[[ $match = $sep ]]' ':dest:dest:'
+      \|
+        '/[qQ]/' -'[[ $variant = gnu && $+opt_args[--posix] = 0 ]]'
+        $'/[\t ]#<->/' '%[^0-9]%' ':exit-codes:exit code:'
+      \|
+        '/[=dDFhHgGnNpPqQxz]/' # stand-alone commands that take no argument
+        \( $'/[ \t]#/' $'%[#\n;}]%' \| $'/[ \t]/' '/[]/' ': _message "no arguments"' \| \)
+      \|
+        $'/v[ \t]#/' $'/[^\n;}]#/' $'%[\n;}]%' ':versions:version:'
+      \|
+        $'/}[ \t]#/' -'((--nest,1))' # closing }
+      \|
+        /'[]'/ ':commands:command: _describe -t sed-commands "sed command" cmds_none -S "" -F excl -- cmds_slash -S / -- cmds_end -F excl -r \; -S $nl'
+      \)
+      $'/[ \t]#/'
+      \( $'/}[ \t]#/' -'((--nest,1))' \| \) # closing } is allowed by GNU sed without preceding ; or newline
+      \(
+       '/\#/' $'/[^\n]##\n[\n; \t]#/' ':comments:comment:' # line end comments
+      \|
+        # add in and auto-removable newline if command is terminated
+        $'/[;\n][ ;\t\n]#/' $':separators:separator:compadd -r ";" -S $nl ""'
+      \|
+        $'/{[ \t]#/' -'((++nest,1))' # opening {, keep count of nesting level
+      \)
+    \)
+  \) \#
+)
+
+_regex_arguments _sed_expressions "$sedexpr[@]"
+
 _arguments -s -S $aopts : "$args[@]"


^ permalink raw reply	[relevance 8%]

* POSIX: reserved words should not expand into aliases
@ 2018-07-15 12:16  9% Ravi (Tom) Hale
  2018-07-15 19:04  5% ` dana
  0 siblings, 1 reply; 200+ results
From: Ravi (Tom) Hale @ 2018-07-15 12:16 UTC (permalink / raw)
  To: zsh-workers

The [POSIX Shell Command Language section 
2.3.1](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_03_01) 
says:

 > However, reserved words in correct grammatical context shall not be 
candidates for alias substitution.

It seems that both `{ba,z}sh` have a bug here.

How do I access a reserved word if an alias is hiding it?

Here `if` works normally:

     % if : ; then echo true; fi
     true

After aliasing `if`:

     % alias if=date
     % if : ; then echo true; fi
     zsh: parse error near `then'

Prefixing with `builtin` or `\` doesn't help:

     % builtin if : ; then echo true; fi
     zsh: parse error near `then'
     % \if : ; then echo true; fi
     zsh: parse error near `then'

How do I access a reserved word in the pathological case that it hidden 
by an alias?


Prior art: https://unix.stackexchange.com/q/456408/143394


^ permalink raw reply	[relevance 9%]

* Re: POSIX: reserved words should not expand into aliases
  2018-07-15 12:16  9% POSIX: reserved words should not expand into aliases Ravi (Tom) Hale
@ 2018-07-15 19:04  5% ` dana
  0 siblings, 0 replies; 200+ results
From: dana @ 2018-07-15 19:04 UTC (permalink / raw)
  To: Ravi (Tom) Hale; +Cc: zsh-workers

On 15 Jul 2018, at 07:16, Ravi (Tom) Hale <ravi@hale.ee> wrote:
>It seems that both `{ba,z}sh` have a bug here.
>
>How do I access a reserved word if an alias is hiding it?

http://zsh.sourceforge.net/Doc/Release/Options.html#Shell-Emulation

>POSIX_ALIASES <K> <S>
>When this option is set, reserved words are not candidates for alias expansion:
>it is still possible to declare any of them as an alias, but the alias will
>never be expanded. Reserved words are described in Reserved Words.

dana


^ permalink raw reply	[relevance 5%]

* PATCH: document compatibility more clearly
       [not found]     <CGME20180716085234eucas1p21c8563e648c24a6e714cc3d24862560a@eucas1p2.samsung.com>
@ 2018-07-16  8:52  5% ` Peter Stephenson
  0 siblings, 0 replies; 200+ results
From: Peter Stephenson @ 2018-07-16  8:52 UTC (permalink / raw)
  To: Zsh hackers' list

Probably ought to be clearer about this right at the start of the
manual, as otherwise people make (not unnatural) assumptions.

pws

diff --git a/Doc/Zsh/intro.yo b/Doc/Zsh/intro.yo
index 510a7bd..a7a2f32 100644
--- a/Doc/Zsh/intro.yo
+++ b/Doc/Zsh/intro.yo
@@ -38,8 +38,13 @@ sect(Description)
 )\
 Zsh is a UNIX command interpreter (shell) usable as an interactive
 login shell and as a shell script command processor.  Of the standard shells,
-zsh most closely resembles bf(ksh) but includes many enhancements.  Zsh
-has command line editing, builtin spelling correction, programmable
+zsh most closely resembles bf(ksh) but includes many enhancements.  It
+does not provide compatibility with POSIX or other shells in its
+default operating mode:  see
+ifnzman(the section noderef(Compatibility))\
+ifzman(the section Compatibility below).
+
+Zsh has command line editing, builtin spelling correction, programmable
 command completion, shell functions (with autoloading), a history
 mechanism, and a host of other features.
 includefile(Zsh/metafaq.yo)\x01



^ permalink raw reply	[relevance 5%]

* Re: BUG: Shell builtin `which` prints non-existent commands to stdout
  @ 2018-09-24 22:32  3%         ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2018-09-24 22:32 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: Zsh Workers

2018-09-24 13:51:24 +0100, Peter Stephenson:
> On Mon, 24 Sep 2018 14:29:33 +0200
[...]
> +  The issue is that if you run:
> +  verb(
> +    which non-existent-command
> +  )
> +  the error message goes, unusually, to standard output rather than
> +  to standard error.  Other shells send this message to standard error,
> +  as they would if the command was about to be executed but could not be
> +  found.
[...]

About "other shells" above, AFAIK, tcsh is the only other shell
that has a "which" builtin and it does also send "command not
found" to stdout. So zsh behaves like every other shells in that
regard.

To phrase my last comment differently, there are also a variety
of "which" command implementations outside of any shell, the
first of which (which appeared in 3BSD) was primarily intended
for csh users (as it read your ~/.cshrc for aliases) and was
written as a csh script that did also send the "command not
found" to stdout.

That's also the one still found on many commercial Unices.

There is also a GNU "which" command (which tries to generalise
what that csh script did for other shells) and sends the
command-not-found to stderr.

Debian has a POSIX sh which script that doesn't output any
command-not-found error and doesn't look-up aliases of any
shell (it writes the usage message on stdout followed by the
"illegal option" on stderr).

BSDs which has now been merged with whereis and been rewritten in
C and no longer looks up your csh aliases and like the Debian
script doesn't output any command-not-found error.

See also
https://unix.stackexchange.com/questions/85249/why-not-use-which-what-to-use-then

-- 
Stephane

^ permalink raw reply	[relevance 3%]

* [PATCH] [long] typeset doesn't report tied parameters (and related issues)
  @ 2018-10-07 13:35  5% ` Stephane Chazelas
    2018-10-09 10:59  3%   ` local read-only variables (Was:[PATCH] [long] typeset doesn't report tied parameters (and related issues)) Stephane Chazelas
  0 siblings, 2 replies; 200+ results
From: Stephane Chazelas @ 2018-10-07 13:35 UTC (permalink / raw)
  To: Zsh hackers list

2018-09-24 22:05:50 +0100, Stephane Chazelas:
> From:
> https://unix.stackexchange.com/questions/470956/how-to-print-all-the-attributes-of-a-zsh-parameter
[...]

Hi, that's a follow up on my previous patch about typeset -p
to report unique parameters.

It would be useful for "typeset -p", or "typeset +m" or
${(t)param} or $parameters[param] to report the fact that the
array is tied (and how for typeset -p) including for special
ones that are created as such (like PATH to path).

See also discussion at
https://unix.stackexchange.com/questions/470956/how-to-print-all-the-attributes-of-a-zsh-parameter#comment860654_471031

This patch attempts to address that.  While writing it, I found
a number of related "issues" or possible areas of improvement,
most of them minor.  That ends up being quite a large change.  I
didn't initially intend to spend that much effort on it, I'm not
very familiar with the source code (though a lot more now than
when I started). So even though I've done quite a bit of
testing, I think it should be reviewed by someone with more
intimate knowledge of the code.

Other issues:
- typeset -T A a; echo ${(t)a}
  scalar-tag_local

  "tag_local" is a "function" thing. Changed to "-tied"

- $ typeset -T A=a:b a +; typeset -T A a; echo $a
  a:b

  Change: force an assignment upon change to join character
  (I suspect there's a better way to do that one)

- typeset +x -T VAR var doesn't honour the +x

- $ zsh -c 'manpath=(a:b c); typeset -p manpath; (){local MANPATH}; typeset -p manpath'
  typeset -a manpath=( a:b c )
  typeset -a manpath=( a b c )
  Not addressed by this patch. More generally, in those cases (same for
  tied=() vs tied=('') which cannot be preserved upon conversion
  to colonarray) it's not always clear when and how it's going
  to be "fixed", but I think we can live with it.

-  zsh 'readonly PATH; readonly -p'
  prints nothing. That's because readonly specials are not
  output. But I think the intention was for specials that are
  read-only because they are so by design as they are only be
  meant to be set by the shell (things like $#, $?, $PID...).
  However for the ones that the user set as read-only, they
  should be output by "readonly". It's quite common to make
  specials readonly (like IFS, PATH...)

- attributes of read-only params can be changed even in POSIX
  mode like in ksh. Not changed, but documented.

- $ ARGV0=sh zsh -c 'readonly A=1; readonly -p'
  typeset -r A=1

  Changed to "readonly A=1" when POSIXBUILTIN is on and not in
  ksh emulation. That one may not be necessary. The idea is to
  be able to pass the output of "export -p" or "readonly -p" to
  other POSIX shells.

  But then note:

  $ emulate sh
  $ a=$'\x80' export -p a
  export a=$'\M-\C-@'

  POSIX is currently specifying $'...', currently including \c@,
  \x80, \200, but not \C-@ nor \M-X. IMO, these days, outputting
  \x80 here would be more useful.

  http://austingroupbugs.net/view.php?id=249#c590
  still work in progress.


diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 0141305b4..4039595df 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -2029,6 +2029,9 @@ scalar version causes a split on all separators (which cannot be quoted).
 It is possible to apply tt(-T) to two previously tied variables but with a
 different separator character, in which case the variables remain joined
 as before but the separator is changed.
+
+When an existing scalar is tied to a new array, the value of the scalar
+is preserved but no attribute other than export will be preserved.
 )
 enditem()
 
@@ -2076,12 +2079,12 @@ flag.
 )
 item(tt(-U))(
 For arrays (but not for associative arrays), keep only the first
-occurrence of each duplicated value.  This may also be set for
-colon-separated special parameters like tt(PATH) or tt(FIGNORE), etc.
-Note the flag takes effect on assignment, and the type of the
-variable being assigned to is determinative; for variables with
-shared values it is therefore recommended to set the flag for
-all interfaces, e.g. `tt(typeset -U PATH path)'.
+occurrence of each duplicated value.  This may also be set for tied
+parameters (see tt(-T)) or colon-separated special parameters like
+tt(PATH) or tt(FIGNORE), etc.  Note the flag takes effect on assignment,
+and the type of the variable being assigned to is determinative; for
+variables with shared values it is therefore recommended to set the flag
+for all interfaces, e.g. `tt(typeset -U PATH path)'.
 
 This flag has a different meaning when used with tt(-f); see below.
 )
@@ -2174,10 +2177,17 @@ be turned off.
 If the tt(POSIX_BUILTINS) option is set, the readonly attribute is
 more restrictive: unset variables can be marked readonly and cannot then
 be set; furthermore, the readonly attribute cannot be removed from any
-variable.  Note that in zsh (unlike other shells) it is still possible
-to create a local variable of the same name as this is considered a
-different variable (though this variable, too, can be marked readonly).
-)
+variable.
+
+It is still possible to change other attributes of the variable though,
+some of which like tt(-U) or tt(-Z) would affect the value. More generally,
+the readonly attribute should not be relied on as a security mechanism.
+
+Note that in zsh (like in pdksh but unlike most other shells) it is
+still possible to create a local variable of the same name as this is
+considered a different variable (though this variable, too, can be marked
+readonly). Special variables that have been made readonly retain their value
+and readonly attribute when made local.)
 item(tt(-t))(
 Tags the named parameters.  Tags have no special meaning to the shell.
 This flag has a different meaning when used with tt(-f); see above.
diff --git a/Src/Modules/db_gdbm.c b/Src/Modules/db_gdbm.c
index ed702b912..12dd839cf 100644
--- a/Src/Modules/db_gdbm.c
+++ b/Src/Modules/db_gdbm.c
@@ -809,7 +809,7 @@ myfreeparamnode(HashNode hn)
 
     zsfree(pm->node.nam);
     /* If this variable was tied by the user, ename was ztrdup'd */
-    if (pm->node.flags & PM_TIED && pm->ename) {
+    if (!(pm->node.flags & PM_SPECIAL) && pm->ename) {
         zsfree(pm->ename);
         pm->ename = NULL;
     }
diff --git a/Src/Modules/parameter.c b/Src/Modules/parameter.c
index 783c36df3..76824cf58 100644
--- a/Src/Modules/parameter.c
+++ b/Src/Modules/parameter.c
@@ -75,6 +75,8 @@ paramtypestr(Param pm)
 	    val = dyncat(val, "-readonly");
 	if (f & PM_TAGGED)
 	    val = dyncat(val, "-tag");
+	if (f & PM_TIED)
+	    val = dyncat(val, "-tied");
 	if (f & PM_EXPORTED)
 	    val = dyncat(val, "-export");
 	if (f & PM_UNIQUE)
@@ -2194,67 +2196,67 @@ static const struct gsu_array historywords_gsu =
 static struct paramdef partab[] = {
     SPECIALPMDEF("aliases", 0,
 	    &pmraliases_gsu, getpmralias, scanpmraliases),
-    SPECIALPMDEF("builtins", PM_READONLY, NULL, getpmbuiltin, scanpmbuiltins),
+    SPECIALPMDEF("builtins", PM_READONLY_SPECIAL, NULL, getpmbuiltin, scanpmbuiltins),
     SPECIALPMDEF("commands", 0, &pmcommands_gsu, getpmcommand, scanpmcommands),
     SPECIALPMDEF("dirstack", PM_ARRAY,
 	    &dirs_gsu, NULL, NULL),
     SPECIALPMDEF("dis_aliases", 0,
 	    &pmdisraliases_gsu, getpmdisralias, scanpmdisraliases),
-    SPECIALPMDEF("dis_builtins", PM_READONLY,
+    SPECIALPMDEF("dis_builtins", PM_READONLY_SPECIAL,
 	    NULL, getpmdisbuiltin, scanpmdisbuiltins),
     SPECIALPMDEF("dis_functions", 0, 
 	    &pmdisfunctions_gsu, getpmdisfunction, scanpmdisfunctions),
-    SPECIALPMDEF("dis_functions_source", PM_READONLY, NULL,
+    SPECIALPMDEF("dis_functions_source", PM_READONLY_SPECIAL, NULL,
 		 getpmdisfunction_source, scanpmdisfunction_source),
     SPECIALPMDEF("dis_galiases", 0,
 	    &pmdisgaliases_gsu, getpmdisgalias, scanpmdisgaliases),
-    SPECIALPMDEF("dis_patchars", PM_ARRAY|PM_READONLY,
+    SPECIALPMDEF("dis_patchars", PM_ARRAY|PM_READONLY_SPECIAL,
 	    &dispatchars_gsu, NULL, NULL),
-    SPECIALPMDEF("dis_reswords", PM_ARRAY|PM_READONLY,
+    SPECIALPMDEF("dis_reswords", PM_ARRAY|PM_READONLY_SPECIAL,
 	    &disreswords_gsu, NULL, NULL),
     SPECIALPMDEF("dis_saliases", 0,
 	    &pmdissaliases_gsu, getpmdissalias, scanpmdissaliases),
-    SPECIALPMDEF("funcfiletrace", PM_ARRAY|PM_READONLY,
+    SPECIALPMDEF("funcfiletrace", PM_ARRAY|PM_READONLY_SPECIAL,
 	    &funcfiletrace_gsu, NULL, NULL),
-    SPECIALPMDEF("funcsourcetrace", PM_ARRAY|PM_READONLY,
+    SPECIALPMDEF("funcsourcetrace", PM_ARRAY|PM_READONLY_SPECIAL,
 	    &funcsourcetrace_gsu, NULL, NULL),
-    SPECIALPMDEF("funcstack", PM_ARRAY|PM_READONLY,
+    SPECIALPMDEF("funcstack", PM_ARRAY|PM_READONLY_SPECIAL,
 	    &funcstack_gsu, NULL, NULL),
     SPECIALPMDEF("functions", 0, &pmfunctions_gsu, getpmfunction,
 		 scanpmfunctions),
-    SPECIALPMDEF("functions_source", PM_READONLY, NULL,
+    SPECIALPMDEF("functions_source", PM_READONLY_SPECIAL, NULL,
 		 getpmfunction_source, scanpmfunction_source),
-    SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY,
+    SPECIALPMDEF("functrace", PM_ARRAY|PM_READONLY_SPECIAL,
 	    &functrace_gsu, NULL, NULL),
     SPECIALPMDEF("galiases", 0,
 	    &pmgaliases_gsu, getpmgalias, scanpmgaliases),
-    SPECIALPMDEF("history", PM_READONLY,
+    SPECIALPMDEF("history", PM_READONLY_SPECIAL,
 	    NULL, getpmhistory, scanpmhistory),
-    SPECIALPMDEF("historywords", PM_ARRAY|PM_READONLY,
+    SPECIALPMDEF("historywords", PM_ARRAY|PM_READONLY_SPECIAL,
 	    &historywords_gsu, NULL, NULL),
-    SPECIALPMDEF("jobdirs", PM_READONLY,
+    SPECIALPMDEF("jobdirs", PM_READONLY_SPECIAL,
 	    NULL, getpmjobdir, scanpmjobdirs),
-    SPECIALPMDEF("jobstates", PM_READONLY,
+    SPECIALPMDEF("jobstates", PM_READONLY_SPECIAL,
 	    NULL, getpmjobstate, scanpmjobstates),
-    SPECIALPMDEF("jobtexts", PM_READONLY,
+    SPECIALPMDEF("jobtexts", PM_READONLY_SPECIAL,
 	    NULL, getpmjobtext, scanpmjobtexts),
-    SPECIALPMDEF("modules", PM_READONLY,
+    SPECIALPMDEF("modules", PM_READONLY_SPECIAL,
 	    NULL, getpmmodule, scanpmmodules),
     SPECIALPMDEF("nameddirs", 0,
 	    &pmnameddirs_gsu, getpmnameddir, scanpmnameddirs),
     SPECIALPMDEF("options", 0,
 	    &pmoptions_gsu, getpmoption, scanpmoptions),
-    SPECIALPMDEF("parameters", PM_READONLY,
+    SPECIALPMDEF("parameters", PM_READONLY_SPECIAL,
 	    NULL, getpmparameter, scanpmparameters),
-    SPECIALPMDEF("patchars", PM_ARRAY|PM_READONLY,
+    SPECIALPMDEF("patchars", PM_ARRAY|PM_READONLY_SPECIAL,
 	    &patchars_gsu, NULL, NULL),
-    SPECIALPMDEF("reswords", PM_ARRAY|PM_READONLY,
+    SPECIALPMDEF("reswords", PM_ARRAY|PM_READONLY_SPECIAL,
 	    &reswords_gsu, NULL, NULL),
     SPECIALPMDEF("saliases", 0,
 	    &pmsaliases_gsu, getpmsalias, scanpmsaliases),
-    SPECIALPMDEF("userdirs", PM_READONLY,
+    SPECIALPMDEF("userdirs", PM_READONLY_SPECIAL,
 	    NULL, getpmuserdir, scanpmuserdirs),
-    SPECIALPMDEF("usergroups", PM_READONLY,
+    SPECIALPMDEF("usergroups", PM_READONLY_SPECIAL,
 	    NULL, getpmusergroups, scanpmusergroups)
 };
 
diff --git a/Src/builtin.c b/Src/builtin.c
index 4abc7da35..c5b319b68 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -64,7 +64,7 @@ static struct builtin builtins[] =
     BUILTIN("enable", 0, bin_enable, 0, -1, BIN_ENABLE, "afmprs", NULL),
     BUILTIN("eval", BINF_PSPECIAL, bin_eval, 0, -1, BIN_EVAL, NULL, NULL),
     BUILTIN("exit", BINF_PSPECIAL, bin_break, 0, 1, BIN_EXIT, NULL, NULL),
-    BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, 0, "E:%F:%HL:%R:%TUZ:%afhi:%lp:%rtu", "xg"),
+    BUILTIN("export", BINF_PLUSOPTS | BINF_MAGICEQUALS | BINF_PSPECIAL | BINF_ASSIGN, (HandlerFunc)bin_typeset, 0, -1, BIN_EXPORT, "E:%F:%HL:%R:%TUZ:%afhi:%lp:%rtu", "xg"),
     BUILTIN("false", 0, bin_false, 0, -1, 0, NULL, NULL),
     /*
      * We used to behave as if the argument to -e was optional.
@@ -2258,6 +2258,22 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 	    } else if (pm->env && !(pm->node.flags & PM_HASHELEM))
 		delenv(pm);
 	    DPUTS(ASG_ARRAYP(asg), "BUG: typeset got array value where scalar expected");
+	    if (altpm) {
+		struct tieddata* tdp = (struct tieddata *) pm->u.data;
+		if (tdp) {
+		    if (tdp->joinchar != joinchar && !asg->value.scalar) {
+			/*
+			 * Reassign the scalar to itself to do the splitting with
+			 * the new joinchar
+			 */
+			tdp->joinchar = joinchar;
+			if (!(pm = assignsparam(pname, ztrdup(getsparam(pname)), 0)))
+			    return NULL;
+		    }
+		}
+		else
+		    DPUTS(!tdp, "BUG: no join character to update");
+	    }
 	    if (asg->value.scalar &&
 		!(pm = assignsparam(pname, ztrdup(asg->value.scalar), 0)))
 		return NULL;
@@ -2325,6 +2341,9 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 	    zerrnam(cname, "%s: can only have a single instance", pname);
 	    return pm;
 	}
+
+	on |= pm->node.flags & PM_TIED;
+
 	/*
 	 * For specials, we keep the same struct but zero everything.
 	 * Maybe it would be easier to create a new struct but copy
@@ -2476,7 +2495,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 	return NULL;
     }
 
-    if (altpm && PM_TYPE(pm->node.flags) == PM_SCALAR) {
+    if (altpm && PM_TYPE(pm->node.flags) == PM_SCALAR && !(pm->node.flags & PM_SPECIAL)) {
 	/*
 	 * It seems safer to set this here than in createparam(),
 	 * to make sure we only ever use the colonarr functions
@@ -2646,7 +2665,17 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 
     /* Given no arguments, list whatever the options specify. */
     if (OPT_ISSET(ops,'p')) {
-	printflags |= PRINT_TYPESET;
+
+	if (isset(POSIXBUILTINS) && SHELL_EMULATION() != EMULATE_KSH) {
+	  if (func == BIN_EXPORT)
+	    printflags |= PRINT_POSIX_EXPORT;
+	  else if (func == BIN_READONLY)
+	    printflags |= PRINT_POSIX_READONLY;
+	  else
+	    printflags |= PRINT_TYPESET;
+	} else
+	    printflags |= PRINT_TYPESET;
+
 	if (OPT_HASARG(ops,'p')) {
 	    char *eptr;
 	    int pflag = (int)zstrtol(OPT_ARG(ops,'p'), &eptr, 10);
@@ -2662,13 +2691,20 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
     }
     hasargs = *argv != NULL || (assigns && firstnode(assigns));
     if (!hasargs) {
+	int exclude = 0;
 	if (!OPT_ISSET(ops,'p')) {
 	    if (!(on|roff))
 		printflags |= PRINT_TYPE;
 	    if (roff || OPT_ISSET(ops,'+'))
 		printflags |= PRINT_NAMEONLY;
+	} else if (printflags & (PRINT_POSIX_EXPORT|PRINT_POSIX_READONLY)) {
+	    /*
+	     * For POSIX export/readonly, exclude non-scalars unless
+	     * explicitly requested.
+	     */
+	    exclude = (PM_ARRAY|PM_HASHED) & ~(on|roff);
 	}
-	scanhashtable(paramtab, 1, on|roff, 0, paramtab->printnode, printflags);
+	scanhashtable(paramtab, 1, on|roff, exclude, paramtab->printnode, printflags);
 	unqueue_signals();
 	return 0;
     }
@@ -2683,6 +2719,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 	struct asgment asg0, asg2;
 	char *oldval = NULL, *joinstr;
 	int joinchar, nargs;
+	int already_tied = 0;
 
 	if (OPT_ISSET(ops,'m')) {
 	    zwarnnam(name, "incompatible options for -T");
@@ -2765,47 +2802,81 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 	    joinchar = joinstr[1] ^ 32;
 	else
 	    joinchar = *joinstr;
-	/*
-	 * Keep the old value of the scalar.  We need to do this
-	 * here as if it is already tied to the same array it
-	 * will be unset when we retie the array.  This is all
-	 * so that typeset -T is idempotent.
-	 *
-	 * We also need to remember here whether the damn thing is
-	 * exported and pass that along.  Isn't the world complicated?
-	 */
-	if ((pm = (Param) paramtab->getnode(paramtab, asg0.name))
-	    && !(pm->node.flags & PM_UNSET)
-	    && (locallevel == pm->level || !(on & PM_LOCAL))) {
-	    if (pm->node.flags & PM_TIED) {
+
+	pm = (Param) paramtab->getnode(paramtab, asg0.name);
+	apm = (Param) paramtab->getnode(paramtab, asg->name);
+
+	if (pm && (pm->node.flags & (PM_SPECIAL|PM_TIED)) == (PM_SPECIAL|PM_TIED)) {
+	    /*
+	     * Only allow typeset -T on special tied parameters if the tied
+	     * parameter and join char are the same
+	     */
+	    if (strcmp(pm->ename, asg->name) || !(apm->node.flags & PM_SPECIAL)) {
+		zwarnnam(name, "%s special parameter can only be tied to special parameter %s", asg0.name, pm->ename);
+		unqueue_signals();
+		return 1;
+	    }
+	    if (joinchar != ':') {
+		zwarnnam(name, "cannot change the join character of special tied parameters");
 		unqueue_signals();
-		if (PM_TYPE(pm->node.flags) != PM_SCALAR) {
-		    zwarnnam(name, "already tied as non-scalar: %s", asg0.name);
-		} else if (!strcmp(asg->name, pm->ename)) {
+		return 1;
+	    }
+	    already_tied = 1;
+	} else if (apm && (apm->node.flags & (PM_SPECIAL|PM_TIED)) == (PM_SPECIAL|PM_TIED)) {
+	    /*
+	     * For the array variable, this covers attempts to tie the
+	     * array to a different scalar or to the scalar after it has
+	     * been made non-special
+	     */
+	    zwarnnam(name, "%s special parameter can only be tied to special parameter %s", asg->name, apm->ename);
+	    unqueue_signals();
+	    return 1;
+	} else if (pm) {
+	    if (!(pm->node.flags & PM_UNSET)
+		&& (locallevel == pm->level || !(on & PM_LOCAL))) {
+		if (pm->node.flags & PM_TIED) {
+		    if (PM_TYPE(pm->node.flags) != PM_SCALAR) {
+			zwarnnam(name, "already tied as non-scalar: %s", asg0.name);
+			unqueue_signals();
+			return 1;
+		    } else if (!strcmp(asg->name, pm->ename)) {
+			already_tied = 1;
+		    } else {
+			zwarnnam(name, "can't tie already tied scalar: %s",
+				 asg0.name);
+			unqueue_signals();
+			return 1;
+		    }
+		} else {
 		    /*
-		     * Already tied in the fashion requested.
+		     * Variable already exists in the current scope but is not tied.
+		     * We're preserving its value and export attribute but no other
+		     * attributes upon converting to "tied".
 		     */
-		    struct tieddata *tdp = (struct tieddata*)pm->u.data;
-		    int flags = (asg->flags & ASG_KEY_VALUE) ?
-			ASSPM_KEY_VALUE : 0;
-		    /* Update join character */
-		    tdp->joinchar = joinchar;
-		    if (asg0.value.scalar)
-			assignsparam(asg0.name, ztrdup(asg0.value.scalar), 0);
-		    else if (asg->value.array)
-			assignaparam(
-			    asg->name, zlinklist2array(asg->value.array),flags);
-		    return 0;
-		} else {
-		    zwarnnam(name, "can't tie already tied scalar: %s",
-			    asg0.name);
+		    if (!asg0.value.scalar && !asg->value.array &&
+			!(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))
+			oldval = ztrdup(getsparam(asg0.name));
+		    on |= (pm->node.flags & ~roff) & PM_EXPORTED;
 		}
-		return 1;
 	    }
-	    if (!asg0.value.scalar && !asg->value.array &&
-		!(PM_TYPE(pm->node.flags) & (PM_ARRAY|PM_HASHED)))
-		oldval = ztrdup(getsparam(asg0.name));
-	    on |= (pm->node.flags & PM_EXPORTED);
+	}
+	if (already_tied) {
+	    int ret;
+	    /*
+	     * If already tied, we still need to call typeset_single on
+	     * both the array and colonarray, if only to update the attributes
+	     * of both, and of course to set the new value if one is provided
+	     * for either of them.
+	     */
+	    ret = !(typeset_single(name, asg0.name, pm,
+				   func, on, off, roff, &asg0, apm,
+				   ops, joinchar) &&
+		    typeset_single(name, asg->name, apm,
+				   func, (on | PM_ARRAY) & ~PM_EXPORTED,
+				   off & ~PM_ARRAY, roff, asg, NULL, ops, 0)
+		   );
+	    unqueue_signals();
+	    return ret;
 	}
 	/*
 	 * Create the tied array; this is normal except that
@@ -2832,9 +2903,7 @@ bin_typeset(char *name, char **argv, LinkList assigns, Options ops, int func)
 	 * Create the tied colonarray.  We make it as a normal scalar
 	 * and fix up the oddities later.
 	 */
-	if (!(pm=typeset_single(name, asg0.name,
-				(Param)paramtab->getnode(paramtab,
-							 asg0.name),
+	if (!(pm=typeset_single(name, asg0.name, pm,
 				func, on, off, roff, &asg0, apm,
 				ops, joinchar))) {
 	    if (oldval)
diff --git a/Src/hashtable.h b/Src/hashtable.h
index 21398e17c..f6778664e 100644
--- a/Src/hashtable.h
+++ b/Src/hashtable.h
@@ -63,6 +63,7 @@
 #define BIN_UNALIAS  29
 #define BIN_UNFUNCTION  30
 #define BIN_UNSET    31
+#define BIN_EXPORT   32
 
 /* These currently depend on being 0 and 1. */
 #define BIN_SETOPT    0
diff --git a/Src/params.c b/Src/params.c
index f7ecff32a..089a958ae 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -290,7 +290,7 @@ static initparam special_params[] ={
 #define GSU(X) BR((GsuScalar)(void *)(&(X)))
 #define NULL_GSU BR((GsuScalar)(void *)NULL)
 #define IPDEF1(A,B,C) {{NULL,A,PM_INTEGER|PM_SPECIAL|C},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0}
-IPDEF1("#", pound_gsu, PM_READONLY),
+IPDEF1("#", pound_gsu, PM_READONLY_SPECIAL),
 IPDEF1("ERRNO", errno_gsu, PM_UNSET),
 IPDEF1("GID", gid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF1("EGID", egid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
@@ -300,11 +300,11 @@ IPDEF1("SAVEHIST", savehist_gsu, PM_RESTRICTED),
 IPDEF1("SECONDS", intseconds_gsu, 0),
 IPDEF1("UID", uid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
 IPDEF1("EUID", euid_gsu, PM_DONTIMPORT | PM_RESTRICTED),
-IPDEF1("TTYIDLE", ttyidle_gsu, PM_READONLY),
+IPDEF1("TTYIDLE", ttyidle_gsu, PM_READONLY_SPECIAL),
 
 #define IPDEF2(A,B,C) {{NULL,A,PM_SCALAR|PM_SPECIAL|C},BR(NULL),GSU(B),0,0,NULL,NULL,NULL,0}
 IPDEF2("USERNAME", username_gsu, PM_DONTIMPORT|PM_RESTRICTED),
-IPDEF2("-", dash_gsu, PM_READONLY),
+IPDEF2("-", dash_gsu, PM_READONLY_SPECIAL),
 IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT),
 IPDEF2("HOME", home_gsu, PM_UNSET),
 IPDEF2("TERM", term_gsu, PM_UNSET),
@@ -337,7 +337,7 @@ LCIPDEF("LC_TIME"),
 # endif
 #endif /* USE_LOCALE */
 
-#define IPDEF4(A,B) {{NULL,A,PM_INTEGER|PM_READONLY|PM_SPECIAL},BR((void *)B),GSU(varint_readonly_gsu),10,0,NULL,NULL,NULL,0}
+#define IPDEF4(A,B) {{NULL,A,PM_INTEGER|PM_READONLY_SPECIAL},BR((void *)B),GSU(varint_readonly_gsu),10,0,NULL,NULL,NULL,0}
 IPDEF4("!", &lastpid),
 IPDEF4("$", &mypid),
 IPDEF4("?", &lastval),
@@ -377,10 +377,9 @@ IPDEF7("PS3", &prompt3),
 IPDEF7R("PS4", &prompt4),
 IPDEF7("SPROMPT", &sprompt),
 
-#define IPDEF9F(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,NULL,C,NULL,0}
-#define IPDEF9(A,B,C) IPDEF9F(A,B,C,0)
-IPDEF9F("*", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
-IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
+#define IPDEF9(A,B,C,D) {{NULL,A,D|PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT},BR((void *)B),GSU(vararray_gsu),0,0,NULL,C,NULL,0}
+IPDEF9("*", &pparams, NULL, PM_ARRAY|PM_READONLY_SPECIAL|PM_DONTIMPORT),
+IPDEF9("@", &pparams, NULL, PM_ARRAY|PM_READONLY_SPECIAL|PM_DONTIMPORT),
 
 /*
  * This empty row indicates the end of parameters available in
@@ -389,17 +388,17 @@ IPDEF9F("@", &pparams, NULL, PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT|PM_READONLY),
 {{NULL,NULL,0},BR(NULL),NULL_GSU,0,0,NULL,NULL,NULL,0},
 
 #define IPDEF8(A,B,C,D) {{NULL,A,D|PM_SCALAR|PM_SPECIAL},BR((void *)B),GSU(colonarr_gsu),0,0,NULL,C,NULL,0}
-IPDEF8("CDPATH", &cdpath, "cdpath", 0),
-IPDEF8("FIGNORE", &fignore, "fignore", 0),
-IPDEF8("FPATH", &fpath, "fpath", 0),
-IPDEF8("MAILPATH", &mailpath, "mailpath", 0),
-IPDEF8("WATCH", &watch, "watch", 0),
-IPDEF8("PATH", &path, "path", PM_RESTRICTED),
-IPDEF8("PSVAR", &psvar, "psvar", 0),
-IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY),
+IPDEF8("CDPATH", &cdpath, "cdpath", PM_TIED),
+IPDEF8("FIGNORE", &fignore, "fignore", PM_TIED),
+IPDEF8("FPATH", &fpath, "fpath", PM_TIED),
+IPDEF8("MAILPATH", &mailpath, "mailpath", PM_TIED),
+IPDEF8("WATCH", &watch, "watch", PM_TIED),
+IPDEF8("PATH", &path, "path", PM_RESTRICTED|PM_TIED),
+IPDEF8("PSVAR", &psvar, "psvar", PM_TIED),
+IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, "zsh_eval_context", PM_READONLY_SPECIAL|PM_TIED),
 
 /* MODULE_PATH is not imported for security reasons */
-IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED),
+IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED|PM_TIED),
 
 #define IPDEF10(A,B) {{NULL,A,PM_ARRAY|PM_SPECIAL},BR(NULL),GSU(B),10,0,NULL,NULL,NULL,0}
 
@@ -409,7 +408,7 @@ IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED),
  */
 
 /* All of these have sh compatible equivalents.                */
-IPDEF1("ARGC", argc_gsu, PM_READONLY),
+IPDEF1("ARGC", argc_gsu, PM_READONLY_SPECIAL),
 IPDEF2("HISTCHARS", histchars_gsu, PM_DONTIMPORT),
 IPDEF4("status", &lastval),
 IPDEF7("prompt", &prompt),
@@ -417,20 +416,20 @@ IPDEF7("PROMPT", &prompt),
 IPDEF7("PROMPT2", &prompt2),
 IPDEF7("PROMPT3", &prompt3),
 IPDEF7("PROMPT4", &prompt4),
-IPDEF8("MANPATH", &manpath, "manpath", 0),
-IPDEF9("argv", &pparams, NULL),
-IPDEF9("fignore", &fignore, "FIGNORE"),
-IPDEF9("cdpath", &cdpath, "CDPATH"),
-IPDEF9("fpath", &fpath, "FPATH"),
-IPDEF9("mailpath", &mailpath, "MAILPATH"),
-IPDEF9("manpath", &manpath, "MANPATH"),
-IPDEF9("psvar", &psvar, "PSVAR"),
-IPDEF9("watch", &watch, "WATCH"),
+IPDEF8("MANPATH", &manpath, "manpath", PM_TIED),
+IPDEF9("argv", &pparams, NULL, 0),
+IPDEF9("fignore", &fignore, "FIGNORE", PM_TIED),
+IPDEF9("cdpath", &cdpath, "CDPATH", PM_TIED),
+IPDEF9("fpath", &fpath, "FPATH", PM_TIED),
+IPDEF9("mailpath", &mailpath, "MAILPATH", PM_TIED),
+IPDEF9("manpath", &manpath, "MANPATH", PM_TIED),
+IPDEF9("psvar", &psvar, "PSVAR", PM_TIED),
+IPDEF9("watch", &watch, "WATCH", PM_TIED),
 
-IPDEF9F("zsh_eval_context", &zsh_eval_context, "ZSH_EVAL_CONTEXT", PM_READONLY),
+IPDEF9("zsh_eval_context", &zsh_eval_context, "ZSH_EVAL_CONTEXT", PM_TIED|PM_READONLY_SPECIAL),
 
-IPDEF9F("module_path", &module_path, "MODULE_PATH", PM_RESTRICTED),
-IPDEF9F("path", &path, "PATH", PM_RESTRICTED),
+IPDEF9("module_path", &module_path, "MODULE_PATH", PM_TIED|PM_RESTRICTED),
+IPDEF9("path", &path, "PATH", PM_TIED|PM_RESTRICTED),
 
 /* These are known to zsh alone. */
 
@@ -451,7 +450,7 @@ IPDEF8("MAILPATH", &mailpath, NULL, 0),
 IPDEF8("WATCH", &watch, NULL, 0),
 IPDEF8("PATH", &path, NULL, PM_RESTRICTED),
 IPDEF8("PSVAR", &psvar, NULL, 0),
-IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY),
+IPDEF8("ZSH_EVAL_CONTEXT", &zsh_eval_context, NULL, PM_READONLY_SPECIAL),
 
 /* MODULE_PATH is not imported for security reasons */
 IPDEF8("MODULE_PATH", &module_path, NULL, PM_DONTIMPORT|PM_RESTRICTED),
@@ -464,7 +463,7 @@ IPDEF8("MODULE_PATH", &module_path, NULL, PM_DONTIMPORT|PM_RESTRICTED),
  * and $@, this is not readonly.  This parameter is not directly
  * visible in user space.
  */
-static initparam argvparam_pm = IPDEF9F("", &pparams, NULL, \
+static initparam argvparam_pm = IPDEF9("", &pparams, NULL, \
 				 PM_ARRAY|PM_SPECIAL|PM_DONTIMPORT);
 
 #undef BR
@@ -5024,10 +5023,10 @@ arrfixenv(char *s, char **t)
     if (!(pm->node.flags & PM_EXPORTED))
 	return;
 
-    if (pm->node.flags & PM_TIED)
-	joinchar = STOUC(((struct tieddata *)pm->u.data)->joinchar);
-    else
+    if (pm->node.flags & PM_SPECIAL)
 	joinchar = ':';
+    else
+	joinchar = STOUC(((struct tieddata *)pm->u.data)->joinchar);
 
     addenv(pm, t ? zjoin(t, joinchar, 1) : "");
 }
@@ -5650,7 +5649,7 @@ freeparamnode(HashNode hn)
 	pm->gsu.s->unsetfn(pm, 1);
     zsfree(pm->node.nam);
     /* If this variable was tied by the user, ename was ztrdup'd */
-    if (pm->node.flags & PM_TIED)
+    if (!(pm->node.flags & PM_SPECIAL))
 	zsfree(pm->ename);
     zfree(pm, sizeof(struct param));
 }
@@ -5685,7 +5684,9 @@ static const struct paramtypes pmtypes[] = {
     { PM_UPPER, "uppercase", 'u', 0},
     { PM_READONLY, "readonly", 'r', 0},
     { PM_TAGGED, "tagged", 't', 0},
-    { PM_EXPORTED, "exported", 'x', 0}
+    { PM_EXPORTED, "exported", 'x', 0},
+    { PM_UNIQUE, "unique", 'U', 0},
+    { PM_TIED, "tied", 'T', 0}
 };
 
 #define PMTYPES_SIZE ((int)(sizeof(pmtypes)/sizeof(struct paramtypes)))
@@ -5774,10 +5775,6 @@ printparamvalue(Param p, int printflags)
 	}
 	break;
     }
-    if ((printflags & (PRINT_KV_PAIR|PRINT_LINE)) == PRINT_KV_PAIR)
-	putchar(' ');
-    else if (!(printflags & PRINT_KV_PAIR))
-	putchar('\n');
 }
 
 /**/
@@ -5785,36 +5782,41 @@ mod_export void
 printparamnode(HashNode hn, int printflags)
 {
     Param p = (Param) hn;
+    Param peer = NULL;
 
     if (p->node.flags & PM_UNSET) {
-	if (isset(POSIXBUILTINS) && (p->node.flags & PM_READONLY) &&
-	    (printflags & PRINT_TYPESET))
-	{
+	if (printflags & (PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT) &&
+	    p->node.flags & (PM_READONLY|PM_EXPORTED)) {
 	    /*
-	     * Special POSIX rules: show the parameter as readonly
+	     * Special POSIX rules: show the parameter as readonly/exported
 	     * even though it's unset, but with no value.
 	     */
 	    printflags |= PRINT_NAMEONLY;
 	}
-	else if (p->node.flags & PM_EXPORTED)
-	    printflags |= PRINT_NAMEONLY;
 	else
 	    return;
     }
     if (p->node.flags & PM_AUTOLOAD)
 	printflags |= PRINT_NAMEONLY;
 
-    if (printflags & PRINT_TYPESET) {
-	if ((p->node.flags & (PM_READONLY|PM_SPECIAL)) ==
-	    (PM_READONLY|PM_SPECIAL) ||
-	    (p->node.flags & PM_AUTOLOAD)) {
+    if (printflags & (PRINT_TYPESET|PRINT_POSIX_READONLY|PRINT_POSIX_EXPORT)) {
+	if (p->node.flags & (PM_RO_BY_DESIGN|PM_AUTOLOAD)) {
 	    /*
 	     * It's not possible to restore the state of
 	     * these, so don't output.
 	     */
 	    return;
 	}
-	if (locallevel && p->level >= locallevel) {
+	/*
+	 * The zsh variants of export -p/readonly -p also report other
+	 * flags to indicate other attributes or scope. The POSIX variants
+	 * don't.
+	 */
+	if (printflags & PRINT_POSIX_EXPORT) {
+	    printf("export ");
+	} else if (printflags & PRINT_POSIX_READONLY) {
+	    printf("readonly ");
+	} else if (locallevel && p->level >= locallevel) {
 	    printf("typeset ");	    /* printf("local "); */
 	} else if ((p->node.flags & PM_EXPORTED) &&
 		   !(p->node.flags & (PM_ARRAY|PM_HASHED))) {
@@ -5861,22 +5863,48 @@ printparamnode(HashNode hn, int printflags)
 		}
 	    }
 	}
-	if (p->node.flags & PM_UNIQUE) {
-	    if (!doneminus) {
-	      putchar('-');
-	      doneminus = 1;
-	    }
-	    putchar('U');
-	}
 	if (doneminus)
 	    putchar(' ');
+
+	if (p->node.flags & PM_TIED) {
+	    /*
+	     * For scalars tied to arrays,s
+	     *   * typeset +m outputs
+	     *      array tied SCALAR array
+	     *      tied array SCALAR
+	     *   * typeset -p outputs:
+	     *      typeset -T SCALAR array  (for hidden values)
+	     *      typeset -T SCALAR array=(values)
+	     *      for both scalar and array (flags may be different)
+	     *
+	     * We choose to print the value for the array instead of the scalar
+	     * as scalars can't disambiguate between
+	     * typeset -T SCALAR array=()
+	     * and
+	     * typeset -T SCALAR array=('')
+	     * (same for (a b:c)...)
+	     */
+	    Param tmp = (Param) paramtab->getnode(paramtab, p->ename);
+
+	    /*
+	     * Swap param and tied peer for typeset -p output
+	     */
+	    if (!(printflags & PRINT_TYPESET) || (p->node.flags & PM_ARRAY))
+		peer = tmp;
+	    else {
+		peer = p;
+		p = tmp;
+	    }
+
+	    quotedzputs(peer->node.nam, stdout);
+	    putchar(' ');
+	}
     }
 
     if ((printflags & PRINT_NAMEONLY) ||
-	((p->node.flags & PM_HIDEVAL) && !(printflags & PRINT_INCLUDEVALUE))) {
-	zputs(p->node.nam, stdout);
-	putchar('\n');
-    } else {
+	((p->node.flags & PM_HIDEVAL) && !(printflags & PRINT_INCLUDEVALUE)))
+	quotedzputs(p->node.nam, stdout);
+    else {
 	if (printflags & PRINT_KV_PAIR) {
 	    if (printflags & PRINT_LINE)
 		printf("\n  ");
@@ -5888,4 +5916,22 @@ printparamnode(HashNode hn, int printflags)
 
 	printparamvalue(p, printflags);
     }
+    if (peer && (printflags & PRINT_TYPESET) && !(p->node.flags & PM_SPECIAL)) {
+	/*
+	 * append the join char for tied parameters if different from colon
+	 * for typeset -p output.
+	 */
+	unsigned char joinchar = STOUC(((struct tieddata *)peer->u.data)->joinchar);
+	if (joinchar != ':') {
+	    char buf[2];
+	    buf[0] = joinchar;
+	    buf[1] = '\0';
+	    putchar(' ');
+	    quotedzputs(buf, stdout);
+	}
+    }
+    if ((printflags & (PRINT_KV_PAIR|PRINT_LINE)) == PRINT_KV_PAIR)
+	putchar(' ');
+    else if (!(printflags & PRINT_KV_PAIR))
+	putchar('\n');
 }
diff --git a/Src/subst.c b/Src/subst.c
index c1021fbf3..c706b9688 100644
--- a/Src/subst.c
+++ b/Src/subst.c
@@ -2552,8 +2552,8 @@ paramsubst(LinkList l, LinkNode n, char **str, int qt, int pf_flags,
 		    val = dyncat(val, "-readonly");
 		if (f & PM_TAGGED)
 		    val = dyncat(val, "-tag");
-		if (f & PM_TAGGED_LOCAL)
-		    val = dyncat(val, "-tag_local");
+		if (f & PM_TIED)
+		    val = dyncat(val, "-tied");
 		if (f & PM_EXPORTED)
 		    val = dyncat(val, "-export");
 		if (f & PM_UNIQUE)
diff --git a/Src/zsh.h b/Src/zsh.h
index b81db1527..8d39a0493 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -1886,18 +1886,21 @@ struct tieddata {
 #define PM_ANONYMOUS    (1<<20) /* (function) anonymous function            */
 #define PM_LOCAL	(1<<21) /* this parameter will be made local        */
 #define PM_SPECIAL	(1<<22) /* special builtin parameter                */
-#define PM_DONTIMPORT	(1<<23)	/* do not import this variable              */
-#define PM_RESTRICTED	(1<<24) /* cannot be changed in restricted mode     */
-#define PM_UNSET	(1<<25)	/* has null value                           */
-#define PM_REMOVABLE	(1<<26)	/* special can be removed from paramtab     */
-#define PM_AUTOLOAD	(1<<27) /* autoloaded from module                   */
-#define PM_NORESTORE	(1<<28)	/* do not restore value of local special    */
-#define PM_AUTOALL	(1<<28) /* autoload all features in module
+#define PM_RO_BY_DESIGN (1<<23) /* to distinguish from specials that can be
+				   made read-only by the user               */
+#define PM_READONLY_SPECIAL (PM_SPECIAL|PM_READONLY|PM_RO_BY_DESIGN)
+#define PM_DONTIMPORT	(1<<24)	/* do not import this variable              */
+#define PM_RESTRICTED	(1<<25) /* cannot be changed in restricted mode     */
+#define PM_UNSET	(1<<26)	/* has null value                           */
+#define PM_REMOVABLE	(1<<27)	/* special can be removed from paramtab     */
+#define PM_AUTOLOAD	(1<<28) /* autoloaded from module                   */
+#define PM_NORESTORE	(1<<29)	/* do not restore value of local special    */
+#define PM_AUTOALL	(1<<29) /* autoload all features in module
 				 * when loading: valid only if PM_AUTOLOAD
 				 * is also present.
 				 */
-#define PM_HASHELEM     (1<<29) /* is a hash-element */
-#define PM_NAMEDDIR     (1<<30) /* has a corresponding nameddirtab entry    */
+#define PM_HASHELEM     (1<<30) /* is a hash-element */
+#define PM_NAMEDDIR     (1<<31) /* has a corresponding nameddirtab entry    */
 
 /* The option string corresponds to the first of the variables above */
 #define TYPESET_OPTSTR "aiEFALRZlurtxUhHTkz"
@@ -2138,6 +2141,8 @@ typedef groupset *Groupset;
 #define PRINT_INCLUDEVALUE	(1<<4)
 #define PRINT_TYPESET		(1<<5)
 #define PRINT_LINE	        (1<<6)
+#define PRINT_POSIX_EXPORT	(1<<7)
+#define PRINT_POSIX_READONLY	(1<<8)
 
 /* flags for printing for the whence builtin */
 #define PRINT_WHENCE_CSH	(1<<7)
diff --git a/Test/B02typeset.ztst b/Test/B02typeset.ztst
index b53f42f83..ac86e0ad1 100644
--- a/Test/B02typeset.ztst
+++ b/Test/B02typeset.ztst
@@ -20,6 +20,14 @@
 # Not yet tested:
 #  Assorted illegal flag combinations
 
+# For a few tests, we include a
+#    typeset -p param
+#    typeset -m param
+#    typeset +m param
+# to test the proper output of typeset for a number of different types
+# of variables. Note that we can't use a dedicated function to factorize
+# that code, as that would affect the scoping.
+
 %prep
   ## Do not remove the next line, it's used by V10private.ztst
   # test_zsh_param_private
@@ -37,6 +45,9 @@
     typeset -a array
     array=(l o c a l)
     print $scalar $array
+    typeset -p scalar array
+    typeset -m scalar array
+    typeset +m scalar array
   }
   scope01() {
     local scalar
@@ -44,6 +55,9 @@
     local -a array
     array=(l o c a l)
     print $scalar $array
+    typeset -p scalar array
+    typeset -m scalar array
+    typeset +m scalar array
   }
   scope02() {
     declare scalar
@@ -51,10 +65,16 @@
     declare -a array
     array=(l o c a l)
     print $scalar $array
+    typeset -p scalar array
+    typeset -m scalar array
+    typeset +m scalar array
   }
   scope10() {
     export outer=outer
     /bin/sh -fc 'echo $outer'
+    typeset -p outer
+    typeset -m outer
+    typeset +m outer
   }
   scope11() {
     typeset -x outer=outer
@@ -68,6 +88,9 @@
     local -xT OUTER outer
     outer=(i n n e r)
     /bin/sh -fc 'echo $OUTER'
+    typeset -p OUTER outer
+    typeset -m OUTER outer
+    typeset +m OUTER outer
   }
 
   # Bug?  `typeset -h' complains that ! # $ * - ? @ are not identifiers.
@@ -79,8 +102,14 @@
 
 %test
 
+ typeset -p scalar array
+ typeset -m scalar array
  typeset +m scalar array
-0:Report types of parameters with typeset +m
+0:Report types for global variables
+>typeset -g scalar=scalar
+>typeset -g -a array=( a r r a y )
+>scalar=scalar
+>array=( a r r a y )
 >scalar
 >array array
 
@@ -88,18 +117,36 @@
  print $scalar $array
 0:Simple local declarations
 >local l o c a l
+>typeset scalar=local
+>typeset -a array=( l o c a l )
+>scalar=local
+>array=( l o c a l )
+>local scalar
+>array local array
 >scalar a r r a y
 
  scope01
  print $scalar $array
 0:Equivalence of local and typeset in functions
 >local l o c a l
+>typeset scalar=local
+>typeset -a array=( l o c a l )
+>scalar=local
+>array=( l o c a l )
+>local scalar
+>array local array
 >scalar a r r a y
 
  scope02
  print $scalar $array
 0:Basic equivalence of declare and typeset
 >local l o c a l
+>typeset scalar=local
+>typeset -a array=( l o c a l )
+>scalar=local
+>array=( l o c a l )
+>local scalar
+>array local array
 >scalar a r r a y
 
  declare +m scalar
@@ -110,6 +157,9 @@
  print $outer
 0:Global export
 >outer
+>export outer=outer
+>outer=outer
+>outer
 >outer
 
  scope11
@@ -130,18 +180,30 @@
  print $f
  float -F f
  print $f
+ typeset -p f
+ typeset -m f
+ typeset +m f
 0:Floating point, adding a precision, and fixed point
 >float local f
 >3.14e+00
 >3.142
+>typeset -F f=3.142
+>f=3.142
+>float local f
 
  integer i=3.141
  typeset +m i
  integer -i2 i
  print $i
+ typeset -p i
+ typeset -m i
+ typeset +m i
 0:Integer and changing the base
 >integer local i
 >2#11
+>typeset -i2 i=3
+>i=3
+>integer 2 local i
 
  float -E3 f=3.141
  typeset +m f
@@ -174,16 +236,33 @@
 
  typeset -gU array
  print $array
+ typeset -p array
+ typeset -m array
+ typeset +m array
 0:Uniquified arrays and non-local scope
 >a r y
+>typeset -g -aU array=( a r y )
+>array=( a r y )
+>array unique array
 
  typeset -T SCALAR=l:o:c:a:l array
  print $array
  typeset -U SCALAR
  print $SCALAR $array
+ typeset -p SCALAR array
+ typeset -m SCALAR array
+ typeset +m SCALAR array
+ print ${(t)SCALAR} ${(t)array}
 0:Tied parameters and uniquified colon-arrays
 >l o c a l
 >l:o:c:a l o c a
+>typeset -UT SCALAR array=( l o c a )
+>typeset -aT SCALAR array=( l o c a )
+>SCALAR=l:o:c:a
+>array=( l o c a )
+>local unique tied array SCALAR
+>array local tied SCALAR array
+>scalar-local-tied-unique array-local-tied
 
  (setopt NO_multibyte cbases
  LC_ALL=C 2>/dev/null
@@ -209,9 +288,18 @@
  typeset -T SCALAR=$'l\000o\000c\000a\000l' array $'\000'
  typeset -U SCALAR
  print $array
+ typeset -p SCALAR array
+ typeset -m SCALAR array
+ typeset +m SCALAR array
  [[ $SCALAR == $'l\000o\000c\000a' ]]
 0:Tied parameters and uniquified arrays with NUL-character as separator
 >l o c a
+>typeset -UT SCALAR array=( l o c a ) ''
+>typeset -aT SCALAR array=( l o c a ) ''
+>SCALAR=$'l\C-@o\C-@c\C-@a'
+>array=( l o c a )
+>local unique tied array SCALAR
+>array local tied SCALAR array
 
  typeset -T SCALAR array
  typeset +T SCALAR
@@ -223,15 +311,30 @@
  print $OUTER
 0:Export of tied parameters
 >i:n:n:e:r
+>typeset -xT OUTER outer=( i n n e r )
+>typeset -aT OUTER outer=( i n n e r )
+>OUTER=i:n:n:e:r
+>outer=( i n n e r )
+>local exported tied outer OUTER
+>array local tied OUTER outer
 >outer
 
  typeset -TU MORESTUFF=here-we-go-go-again morestuff '-'
  print -l $morestuff
+ typeset -p MORESTUFF morestuff
+ typeset -m MORESTUFF morestuff
+ typeset +m MORESTUFF morestuff
 0:Tied arrays with separator specified
 >here
 >we
 >go
 >again
+>typeset -UT MORESTUFF morestuff=( here we go again ) -
+>typeset -aUT MORESTUFF morestuff=( here we go again ) -
+>MORESTUFF=here-we-go-again
+>morestuff=( here we go again )
+>local unique tied morestuff MORESTUFF
+>array local unique tied MORESTUFF morestuff
 
  typeset -T THIS will not work
 1:Tied array syntax
@@ -251,13 +354,25 @@
  local b=1 ;: to stomp assoc[1] if assoc[b] is broken
  typeset assoc[1]=a assoc[b]=2 assoc[3]=c
  print $assoc[1] $assoc[b] $assoc[3]
+ typeset -p assoc
+ typeset -m assoc
+ typeset +m assoc
 0:Legal local associative array element assignment
 >a 2 c
+>typeset -A assoc=( [1]=a [3]=c [b]=2 )
+>assoc=( [1]=a [3]=c [b]=2 )
+>association local assoc
 
  local scalar scalar[1]=a scalar[2]=b scalar[3]=c
  print $scalar
+ typeset -p scalar
+ typeset -m scalar
+ typeset +m scalar
 0:Local scalar subscript assignment
 >abc
+>typeset scalar=abc
+>scalar=abc
+>local scalar
 
  typeset -L 10 fools
  for fools in "   once" "twice"  "      thrice" "   oops too long here"; do
@@ -273,11 +388,17 @@
  for foolf in 1.3 4.6 -2.987 -4.91031; do
    print "'$foolf'"
  done
+ typeset -p foolf
+ typeset -m foolf
+ typeset +m foolf
 0:Left justification of floating point
 >'1.300     '
 >'4.600     '
 >'-2.987    '
 >'-4.910    '
+>typeset -FL10 foolf=-4.910
+>foolf=-4.910
+>float local left justified 10 foolf
 
  typeset -L 10 -Z foolzs
  for foolzs in 001.3 04.6 -2.987 -04.91231; do
@@ -293,10 +414,16 @@
  for foors in short longer even-longer; do
    print "'$foors'"
  done
+ typeset -p foors
+ typeset -m foors
+ typeset +m foors
 0:Right justification of scalars
 >'     short'
 >'    longer'
 >'ven-longer'
+>typeset -R10 foors=even-longer
+>foors=even-longer
+>local right justified 10 foors
 
  typeset -Z 10 foozs
  for foozs in 42 -42 " 43" " -43"; do
@@ -436,24 +563,36 @@
  print $case1
  upper="VALUE OF \$UPPER"
  print ${(P)case1}
+ typeset -p case1
+ typeset -m case1
+ typeset +m case1
 0:Upper case conversion, does not apply to values used internally
 >UPPER
 >VALUE OF $UPPER
+>typeset -u case1=upper
+>case1=upper
+>local uppercase case1
 
  local case2=LOWER
  typeset -l case2
  print $case2
  LOWER="value of \$lower"
  print ${(P)case2}
+ typeset -p case2
+ typeset -m case2
+ typeset +m case2
 0:Lower case conversion, does not apply to values used internally
 >lower
 >value of $lower
+>typeset -l case2=LOWER
+>case2=LOWER
+>local lowercase case2
 
  typeset -a array
  array=(foo bar)
  fn() { typeset -p array nonexistent; }
  fn
-1:declare -p shouldn't create scoped values
+1:typeset -p shouldn't create scoped values
 >typeset -g -a array=( foo bar )
 ?fn:typeset: no such variable: nonexistent
 
@@ -490,7 +629,7 @@
 ?0
 ?(eval):5: read-only variable: pbro
 ?(eval):6: read-only variable: pbro
-?typeset -g -r pbro
+?readonly pbro
 ?0
 ?(eval):10: read-only variable: pbro
 
@@ -820,7 +959,145 @@
 >  [three]=''
 >)
 
- (typeset -a -U foo=(bar bar)
- typeset -p foo)
-0:typeset -p of typeset -U
->typeset -aU foo=( bar )
+ (export PATH MANPATH
+ path=(/bin)
+ MANPATH=/
+ # read-only special params like zsh_eval_context are not output by typeset -p
+ specials=(path PATH manpath MANPATH zsh_eval_context ZSH_EVAL_CONTEXT)
+ typeset -p $specials
+ typeset -m $specials
+ typeset +m $specials
+ for var ($specials) print $var: ${(Pt)var}
+ )
+0:typeset output for some special tied parameters
+>typeset -g -aT PATH path=( /bin )
+>export -T PATH path=( /bin )
+>typeset -g -aT MANPATH manpath=( / )
+>export -T MANPATH manpath=( / )
+>path=( /bin )
+>PATH=/bin
+>manpath=( / )
+>MANPATH=/
+>zsh_eval_context=( toplevel shfunc shfunc shfunc eval )
+>ZSH_EVAL_CONTEXT=toplevel:shfunc:shfunc:shfunc:eval
+>array tied PATH path
+>tied path PATH
+>array tied MANPATH manpath
+>tied manpath MANPATH
+>array readonly tied ZSH_EVAL_CONTEXT zsh_eval_context
+>readonly tied zsh_eval_context ZSH_EVAL_CONTEXT
+>path: array-tied-special
+>PATH: scalar-tied-export-special
+>manpath: array-tied-special
+>MANPATH: scalar-tied-export-special
+>zsh_eval_context: array-readonly-tied-special
+>ZSH_EVAL_CONTEXT: scalar-readonly-tied-special
+
+ typeset -T VAR var=(a b a b)
+ typeset -UuT VAR var +
+ print $VAR
+0:redeclare a tied variable with different attributes
+>A+B
+
+ typeset -T VAR=a+b var
+ typeset -T VAR var +
+ print $var
+0:colonarray re-split when changing the join character
+>a b
+
+ readonly -T VAR var=(a b)
+ readonly -T VAR var +
+1:cannot change the join character on a readonly tied variable
+?(eval):1: read-only variable: var
+
+ typeset -T FOO manpath
+1:Can't tie a special tied array to a different variable
+?(eval):typeset:1: manpath special parameter can only be tied to special parameter MANPATH
+
+ typeset -T MANPATH foo
+1:Can't tie a special tied scalar to a different variable
+?(eval):typeset:1: MANPATH special parameter can only be tied to special parameter manpath
+
+ typeset -T MANPATH manpath +
+1:Can't change the join character of a special tied variable
+?(eval):typeset:1: cannot change the join character of special tied parameters
+
+ (){
+   typeset -h path
+   typeset -T PATH path=(x)
+ }
+ (){
+   typeset -h PATH
+   typeset -T PATH path=(x)
+ }
+1:reject attempt to tie special to downgraded peer
+?(anon):typeset:2: PATH special parameter can only be tied to special parameter path
+?(anon):typeset:2: path special parameter can only be tied to special parameter PATH
+
+ typeset MANPATH
+ manpath=(/ /)
+ typeset -UT MANPATH manpath
+ print $manpath
+0:OK to run typeset -T on tied specials as long as peer and joinchar are unchanged
+>/
+
+ typeset FOO=a:b
+ export FOO
+ typeset +x -T FOO foo
+ typeset -p FOO
+0:Make sure +x is honoured when tying a parameter
+>typeset -T FOO foo=( a b )
+
+ $ZTST_testdir/../Src/zsh --emulate sh -f -c '
+ PATH=/bin; export PATH; readonly PATH
+ export -p PATH
+ typeset -p PATH
+ readonly -p'
+0: readonly/export output for exported+readonly+special when started as sh
+>export PATH=/bin
+>export -r PATH=/bin
+>readonly PATH=/bin
+
+ function {
+ emulate -L sh
+ MANPATH=/bin; export MANPATH; readonly MANPATH
+ export -p MANPATH
+ typeset -p MANPATH
+ readonly -p
+ }
+0: readonly/export output for exported+readonly+tied+special after switching to sh emulation
+>export MANPATH=/bin
+>export -rT MANPATH manpath=( /bin )
+>readonly MANPATH=/bin
+
+ function {
+   local -rax zsh_exported_readonly_array=(2)
+   local -rAx zsh_exported_readonly_hash=(3 3)
+   local -rx zsh_exported_readonly_scalar=1
+   print zsh:
+   export -p | grep zsh_exported_readonly
+   readonly -p | grep zsh_exported_readonly
+   print sh:
+   emulate -L sh
+   export -p | grep zsh_exported_readonly
+   readonly -p | grep zsh_exported_readonly
+   print still asking for arrays:
+   export -ap | grep zsh_exported_readonly
+   readonly -ap | grep zsh_exported_readonly
+ }
+0: no array/hash in POSIX export/readonly -p
+>zsh:
+>typeset -arx zsh_exported_readonly_array=( 2 )
+>typeset -Arx zsh_exported_readonly_hash=( [3]=3 )
+>typeset -rx zsh_exported_readonly_scalar=1
+>typeset -arx zsh_exported_readonly_array=( 2 )
+>typeset -Arx zsh_exported_readonly_hash=( [3]=3 )
+>typeset -rx zsh_exported_readonly_scalar=1
+>sh:
+>export zsh_exported_readonly_scalar=1
+>readonly zsh_exported_readonly_scalar=1
+>still asking for arrays:
+>export zsh_exported_readonly_array=( 2 )
+>export zsh_exported_readonly_scalar=1
+>readonly zsh_exported_readonly_array=( 2 )
+>readonly zsh_exported_readonly_scalar=1

^ permalink raw reply	[relevance 5%]

* Re: [PATCH] [long] typeset doesn't report tied parameters (and related issues)
  @ 2018-10-08 15:54  3%             ` Bart Schaefer
  0 siblings, 0 replies; 200+ results
From: Bart Schaefer @ 2018-10-08 15:54 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: zsh-workers

On Mon, Oct 8, 2018 at 8:24 AM Peter Stephenson
<p.stephenson@samsung.com> wrote:
>
> Hmm, I'm guessing all the B02 failures are all down to being re-run from
> V10?

No, they're coming from the first pass through B02.  Succeeds for me
on MacOS, but on CentOS 6.10 it fails:

Test ./B02typeset.ztst failed: bad status 1, expected 0 from:
 function {
   local -rax zsh_exported_readonly_array=(2)
   local -rAx zsh_exported_readonly_hash=(3 3)
   local -rx zsh_exported_readonly_scalar=1
   print zsh:
   export -p | grep zsh_exported_readonly
   readonly -p | grep zsh_exported_readonly
   print sh:
   emulate -L sh
   export -p | grep zsh_exported_readonly
   readonly -p | grep zsh_exported_readonly
   print still asking for arrays:
   export -ap | grep zsh_exported_readonly
   readonly -ap | grep zsh_exported_readonly
 }
Was testing:  no array/hash in POSIX export/readonly -p

There is no other useful output, even with ZTST_VERBOSE.

^ permalink raw reply	[relevance 3%]

* local read-only variables (Was:[PATCH] [long] typeset doesn't report tied parameters (and related issues))
  2018-10-07 13:35  5% ` [PATCH] [long] typeset doesn't report tied parameters (and related issues) Stephane Chazelas
  @ 2018-10-09 10:59  3%   ` Stephane Chazelas
  1 sibling, 0 replies; 200+ results
From: Stephane Chazelas @ 2018-10-09 10:59 UTC (permalink / raw)
  To: Zsh hackers list

2018-10-07 14:35:46 +0100, Stephane Chazelas:
[...]
> -variable.  Note that in zsh (unlike other shells) it is still possible
                                ^^^^^^^^^^^^^^^^^^^
> -to create a local variable of the same name as this is considered a
> -different variable (though this variable, too, can be marked readonly).
> -)
> +variable.
> +
> +It is still possible to change other attributes of the variable though,
> +some of which like tt(-U) or tt(-Z) would affect the value. More generally,
> +the readonly attribute should not be relied on as a security mechanism.
> +
> +Note that in zsh (like in pdksh but unlike most other shells) it is
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> +still possible to create a local variable of the same name as this is
> +considered a different variable (though this variable, too, can be marked
> +readonly). Special variables that have been made readonly retain their value
> +and readonly attribute when made local.)
[...]

Actually also yash, we might as well remove that "(unlike other
shells)". Note that pdksh has quite a few derivatives (mksh,
posh, openbsd sh (I've not checked there)).

Those shells don't make a distinction for special variables.

Actually I'm not convinced it's useful to treat them
specially in that regard. For instance, if you do "readonly
IFS", you can no longer use library of functions that do some
"local IFS=:" If I add "readonly IFS" to .zshenv,
compaudit/compinit report errors and the shell exits with my
current ~/.zshrc.

See also related POSIX discussion at
http://austingroupbugs.net/view.php?id=767
http://austingroupbugs.net/view.php?id=767#c3735

-- 
Stephane

^ permalink raw reply	[relevance 3%]

* [RFC] Teach getopts to handle -o and +o separately
@ 2018-10-11  1:33  3% dana
  2018-10-11 20:26  0% ` Daniel Shahaf
  0 siblings, 1 reply; 200+ results
From: dana @ 2018-10-11  1:33 UTC (permalink / raw)
  To: Zsh hackers list

I really like that zsh's getopts handles both -o and +o option variants, but a
major limitation is that the two variants are linked by the one letter
specification — there's no way to tell zsh that you don't want one or the other,
and more importantly there's no way to specify that one variant should take an
argument and one should not.

I'd like to propose that a - or + following an option letter (or preceding it,
if that seems nicer) restrict that letter specification to the - or + variant
respectively. With this change, the following sort of thing becomes possible:

  % which testopts
  testopts () {
  local OPTARG OPTIND opt
    while getopts a-a:+bc opt
    do
      print -r - $opt${OPTARG:+:$OPTARG}
    done
  }

  % testopts -a +a
  a
  testopts:2: argument expected after +a option
  :

  % testopts -bab +bab
  b
  a
  b
  +b
  +a:b

Of course, this eliminates - and + as valid 'letters' in the optstring. But it
seems unlikely that anyone actually needs +-, -+, or ++ (-- is already
effectively unusable for obvious reasons). Also, POSIX says:

  >The use of other option characters that are not alphanumeric produces
  >unspecified results.

So we're fine there.

Below is a kind of silly-looking patch that implements the change. If the idea
is sound i can try to make it nicer (along with adding docs and tests obv), but
in any case i think it can be done without too many LOC and without touching the
scarier parts of that function.

Does this seem viable at all?

dana


diff --git a/Src/builtin.c b/Src/builtin.c
index 8dcdcc024..f099e3263 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -5512,7 +5512,8 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
     optbuf[lenoptbuf - 1] = opch;
 
     /* check for legality */
-    if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) {
+    if(opch == ':' || opch == '?' || opch == '-' || opch == '+'
+	|| !(p = memchr(optstr, opch, lenoptstr))) {
 	p = "?";
     err:
 	zsfree(zoptarg);
@@ -5528,6 +5529,19 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
 	return 0;
     }
 
+    scan:
+	if (!p) {
+	    p = "?";
+	    goto err;
+	}
+	if ((lenoptbuf == 1 && (p[1] == '+' || (p[1] == ':' && p[2] == '+')))
+	    || (lenoptbuf == 2 && (p[1] == '-' || (p[1] == ':' && p[2] == '-')))
+	    ) {
+	    p++;
+	    p = memchr(p, opch, strlen(p));
+	    goto scan;
+	}
+
     /* check for required argument */
     if(p[1] == ':') {
 	if(optcind == lenstr) {


^ permalink raw reply	[relevance 3%]

* Re: [RFC] Teach getopts to handle -o and +o separately
  2018-10-11  1:33  3% [RFC] Teach getopts to handle -o and +o separately dana
@ 2018-10-11 20:26  0% ` Daniel Shahaf
  0 siblings, 0 replies; 200+ results
From: Daniel Shahaf @ 2018-10-11 20:26 UTC (permalink / raw)
  To: zsh-workers

dana wrote on Wed, 10 Oct 2018 20:33 -0500:
> Of course, this eliminates - and + as valid 'letters' in the optstring. But it
> seems unlikely that anyone actually needs +-, -+, or ++ (-- is already
> effectively unusable for obvious reasons). Also, POSIX says:

FWIW, ezmlm-make(1) has a -+ flag.  Not saying it's a deal breaker; just a datapoint.

^ permalink raw reply	[relevance 0%]

* PATCH: git 2.19 completion update
@ 2018-10-16 23:00  4% Oliver Kiddle
  0 siblings, 0 replies; 200+ results
From: Oliver Kiddle @ 2018-10-16 23:00 UTC (permalink / raw)
  To: Zsh workers

This updates options for git 2.19 and handles the new range-diff
subcommand. For git grep, I've added more exclusions and made some
descriptions match _grep more closely.

With git help -c, config options can now be listed. This could perhaps
be used to augment the hard-coded list (which is incomplete).

Oliver

diff --git a/Completion/Unix/Command/_git b/Completion/Unix/Command/_git
index 195a7f6dc..c8760adc5 100644
--- a/Completion/Unix/Command/_git
+++ b/Completion/Unix/Command/_git
@@ -295,7 +295,7 @@ _git-branch () {
   declare l c m d e
 
   l='--color --no-color -r --remotes -a -v --verbose --abbrev --no-abbrev --list --points-at --sort'
-  c='-l --create-reflog -f --force -t --track --no-track -u --set-upstream --set-upstream-to --unset-upstream --contains --no-contains --merged --no-merged'
+  c='--create-reflog -f --force -t --track --no-track -u --set-upstream --set-upstream-to --unset-upstream --contains --no-contains --merged --no-merged'
   m='-c --copy -C -m --move -M --edit-description'
   d='-d --delete -D'
 
@@ -337,7 +337,7 @@ _git-branch () {
     "($c $m $d : -v -vv --verbose)"{-v,-vv,--verbose}'[show SHA1 and commit subject line for each head]' \
     "($c $m $d :)--abbrev=[set minimum SHA1 display-length]: :__git_guard_number length" \
     "($c $m $d :)--no-abbrev[don't abbreviate sha1s]" \
-    "($l $m $d)"{-l,--create-reflog}"[create the branch's reflog]" \
+    "($l $m $d)--create-reflog[create the branch's reflog]" \
     "($l $m $d -f --force)"{-f,--force}'[force the creation of a new branch]' \
     "($l $m $d -t --track)"{-t,--track}'[setup configuration so that pull merges from the start point]' \
     "($l $m $d)--no-track[override the branch.autosetupmerge configuration variable]" \
@@ -872,6 +872,7 @@ _git-fetch () {
     '(--all -m --multiple)'{-m,--multiple}'[fetch from multiple remotes]' \
     '(-P --prune-tags)'{-P,--prune-tags}'[prune local tags no longer on remote and clobber changed tags]' \
     \*{-o+,--server-option=}'[send specified string to the server when using protocol version 2]:option' \
+    '--negotiation-tip=[only report refs reachable from specified object to the server]:commit:__git_commits' \
     '--filter=[object filtering]:filter:_git_rev-list_filters' \
     '*:: :->repository-or-group-or-refspec' && ret=0
 
@@ -992,35 +993,37 @@ _git-grep () {
     '--untracked[search also in untracked files]' \
     '(-a --text)'{-a,--text}'[process binary files as if they were text]' \
     '(--textconv --no-textconv)--textconv[honor textconv filter settings]' \
-    '(--textconv --no-textconv)--no-textconv[do not honor textconv filter settings]' \
+    "(--textconv --no-textconv)--no-textconv[don't honor textconv filter settings]" \
     '(-i --ignore-case)'{-i,--ignore-case}'[ignore case when matching]' \
-    '-I[do not match pattern in binary files]' \
+    "-I[don't match pattern in binary files]" \
     '--max-depth=[descend at most given levels of directories]: :__git_guard_number depth' \
     '(-w --word-regexp)'{-w,--word-regexp}'[match only whole words]' \
     '(-v --invert-match)'{-v,--invert-match}'[select non-matching lines]' \
-    '(   -H)-h[supress output of filenames]' \
-    '(-h   )-H[show filenames]' \
+    '(-H)-h[suppress output of filenames]' \
+    '(-h -c --count)-H[show filenames]' \
     '--full-name[output paths relative to the project top directory]' \
-    '(-E --extended-regexp -G --basic-regexp -P --perl-regexp -F --fixed-strings)'{-E,--extended-regexp}'[use POSIX extended regexes]' \
-    '(-E --extended-regexp -G --basic-regexp -P --perl-regexp -F --fixed-strings)'{-G,--basic-regexp}'[use POSIX basic regexes]' \
+    '(-E --extended-regexp -G --basic-regexp -P --perl-regexp -F --fixed-strings)'{-E,--extended-regexp}'[use extended regular expressions]' \
+    '(-E --extended-regexp -G --basic-regexp -P --perl-regexp -F --fixed-strings)'{-G,--basic-regexp}'[use basic regular expressions]' \
     '(-E --extended-regexp -G --basic-regexp -P --perl-regexp -F --fixed-strings)'{-P,--perl-regexp}'[use perl-compatible regexes]' \
-    '(-E --extended-regexp -G --basic-regexp -P --perl-regexp -F --fixed-strings)'{-F,--fixed-strings}'[do not interpret pattern as a regex]' \
+    '(-E --extended-regexp -G --basic-regexp -P --perl-regexp -F --fixed-strings)'{-F,--fixed-strings}'[use literal strings]' \
     '(-n --line-number)'{-n,--line-number}'[prefix the line number to matching lines]' \
-    '(-l --files-with-matches -L --files-without-match --name-only)'{-l,--files-with-matches,--name-only}'[show only names of matching files]' \
-    '(-l --files-with-matches -L --files-without-match)'{-L,--files-without-match}'[show only names of non-matching files]' \
-    '(--cached -O --open-files-in-pager)'{-O+,--open-files-in-pager=}'-[open matching files in pager]:pager:_cmdstring' \
+    '(-c --count)--column[show column number of first match]' \
+    '(-c --count -l --files-with-matches --name-only -L --files-without-match -o --only-matching)'{-l,--files-with-matches,--name-only}'[show only names of matching files]' \
+    '(-c --count -l --files-with-matches --name-only -L --files-without-match -o --only-matching)'{-L,--files-without-match}'[show only names of non-matching files]' \
+    '(-c --count -o --only-matching -n --line-number --color --no-color --cached --heading -O --open-files-in-pager)'{-O,--open-files-in-pager=}'-[open matching files in pager]::pager:_cmdstring' \
     '(-z --null)'{-z,--null}'[output \0 after filenames]' \
-    '(-c --count)'{-c,--count}'[show number of matching lines in files]' \
-    '(        --no-color)--color=-[color matches]:: :__git_color_whens' \
-    '(--color           )--no-color[do not color matches]' \
-    '--break[prefix the line number to matching lines]' \
-    '--heading[show the filename above the matches]' \
-    '(-A --after-context)'{-A+,--after-context=}'[show <num> trailing lines, and separate groups of matches]: :__git_guard_number lines' \
-    '(-B --before-context)'{-B+,--before-context=}'[show <num> leading lines, and separate groups of matches]: :__git_guard_number lines' \
-    '(-A --after-context -B --before-context -C --context)'{-C+,--context=}'[show <num> leading and trailing lines, and separate groups of matches]: :__git_guard_number lines' \
+    '(-c --count -l --files-with-matches --name-only -L --files-without-match -o --only-matching)'{--only-matching,-o}'[show only matching part of line]' \
+    '(-h -c --count -l --files-with-matches --name-only -L --files-without-match -o --only-matching --color --break --heading -p --show-function -W --function-context)'{-c,--count}'[show number of matching lines in files]' \
+    '(--no-color -O --open-files-in-pager)--color=-[color matches]:: :__git_color_whens' \
+    "(--color -O --open-files-in-pager)--no-color[don't color matches]" \
+    '(-c --count -O --open-files-in-pager)--break[print an empty line between matches from different files]' \
+    '(-c --count -O --open-files-in-pager)--heading[show the filename above the matches]' \
+    '(-A --after-context)'{-A+,--after-context=}'[specify lines of trailing context]: :__git_guard_number lines' \
+    '(-B --before-context)'{-B+,--before-context=}'[specify lines of leading context]: :__git_guard_number lines' \
+    '(-A --after-context -B --before-context -C --context)'{-C+,--context=}'[specify lines of context]: :__git_guard_number lines' \
     '--threads=[use specified number of threads]:number of threads' \
-    '(-p --show-function)'{-p,--show-function}'[show preceding line containing function name of match]' \
-    '(-W --function-context)'{-W,--function-context}'[show whole function where a match was found]' \
+    '(-c --count -p --show-function)'{-p,--show-function}'[show preceding line containing function name of match]' \
+    '(-c --count -W --function-context)'{-W,--function-context}'[show whole function where a match was found]' \
     '(1)*-f+[read patterns from given file]:pattern file:_files' \
     '(1)*-e+[use the given pattern for matching]:pattern' \
     $pattern_operators \
@@ -1210,7 +1213,8 @@ _git-merge () {
 
   _arguments -S -s \
     $merge_options \
-    '-m+[set the commit message to be used for the merge commit]:merge message' \
+    \*{-m+,--message=}'[set the commit message to be used for the merge commit]:merge message' \
+    \*{-F+,--file=}'[read commit message from a file]:file' \
     '(--edit --no-edit)-e[open an editor to change the commit message]' \
     '(                    --no-rerere-autoupdate)--rerere-autoupdate[allow the rerere mechanism to update the index]' \
     '(--rerere-autoupdate                       )--no-rerere-autoupdate[do not allow the rerere mechanism to update the index]' \
@@ -1348,10 +1352,13 @@ _git-pull () {
 
   _arguments \
     $merge_options \
-    '(-r --rebase --no-rebase)'{-r=-,--rebase=-}'[perform a rebase after fetching]::rebase after fetching:((true\:"rebase after fetching"
-                                                                                                        false\:"merge after fetching"
-                                                                                                        preserve\:"rebase and preserve merges"
-													interactive\:"allow list of commits to be edited"))' \
+    '(-r --rebase --no-rebase)'{-r=-,--rebase=-}'[perform a rebase after fetching]::rebase after fetching:((
+      true\:"rebase after fetching"
+      false\:"merge after fetching"
+      merges\:"try to rebase merges instead of skipping them"
+      preserve\:"rebase and preserve merges"
+      interactive\:"allow list of commits to be edited"
+    ))' \
     '(-r --rebase            )--no-rebase[do not perform a rebase after fetching]' \
     '--autostash[automatically stash/stash pop before and after rebase]' \
     $fetch_options \
@@ -1423,6 +1430,20 @@ _git-push () {
   return ret
 }
 
+(( $+functions[_git-range-diff] )) ||
+_git-range-diff () {
+  local -a diff_options
+  __git_setup_diff_options
+
+  _arguments -s \
+    '--creation-factor=[specify weighting for creation]:weighting (percent)' \
+    '--no-dual-color[use simple diff colors]' \
+    $diff_options \
+    '1:range 1:__git_commit_ranges' \
+    '2:range 2:__git_commit_ranges' \
+    '3:revision 2:__git_commits'
+}
+
 (( $+functions[_git-rebase] )) ||
 _git-rebase () {
   local -a autosquash_opts
@@ -1457,6 +1478,7 @@ _git-rebase () {
     '(-i --interactive)--whitespace=-[detect a new or modified line that has whitespace errors]: :__git_apply_whitespace_strategies' \
     '(-i --interactive)--committer-date-is-author-date[use author date as committer date]' \
     '(-i --interactive --ignore-whitespace --whitespace --committer-date-is-author-date)'{-i,--interactive}'[make a list of commits to be rebased and open in $EDITOR]' \
+    '(-r --rebase-merges)'{-r-,--rebase-merges=-}'[try to rebase merges instead of skipping them]::option:(rebase-cousins no-rebase-cousins)' \
     '(-p --preserve-merges --interactive)'{-p,--preserve-merges}'[try to recreate merges instead of ignoring them]' \
     {-x+,--exec=}'[with -i\: append "exec <cmd>" after each line]:command:_command_names -e' \
     '(-k --keep-empty)'{-k,--keep-empty}'[keep empty commits in the result]' \
@@ -2589,9 +2611,13 @@ __git_config_option-or-value () {
     pull.rebase:'rebase branches on top of the fetched branch, instead of merging::->pull.rebase:false'
     pull.twohead:'default merge strategy to use when pulling a single branch::__git_merge_strategies'
     push.default:'action git push should take if no refspec is given::->push.default:simple'
+    push.followTags:'enable --follow-tags option by default::->bool:false'
+    push.gpgSign:'GPG-sign pushes::->bool:false'
+    push.recurseSubmodules:'ensure all submodule commits are available on a remote-tracking branch'
     rebase.stat:'show a diffstat of what changed upstream since last rebase::->bool:false'
-    rebase.autosquash:'autosquash by default::->bool:false'
-    rebase.autostash:'autostash by default::->bool:false'
+    rebase.autoSquash:'autosquash by default::->bool:false'
+    rebase.autoStash:'autostash by default::->bool:false'
+    rebase.missingCommitsCheck:'print a warning if some commits are removed'
     receive.autogc:'run git gc --auto after receiving data::->bool:true'
     receive.fsckObjects:'check all received objects::->bool:true'
     receive.hiderefs:'string(s) receive-pack uses to decide which refs to omit from its initial advertisement:hidden refs:->string'
@@ -2718,8 +2744,12 @@ __git_config_option-or-value () {
     'url.*.pushInsteadOf:string to start URLs to push to with:prefix:->string'
     user.email:'email address used for commits::_email_addresses -c'
     user.name:'full name used for commits:name:->string'
+    user.useConfigOnly:'avoid guessing defaults for user.email and user.name:->bool:true'
     user.signingkey:'default GPG key to use when creating signed tags::__git_gpg_secret_keys'
-    web.browser:'web browser to use::__git_browsers')
+    versionsort.suffix:'specify sort order of suffixes applied to tags:suffix'
+    web.browser:'web browser to use::__git_browsers'
+    worktree.guessRemote:'with add, if branch matches remote track it::->bool:true'
+  )
 
   declare -a git_present_options # 'present' is an adjective
   git_present_options=(
@@ -2962,8 +2992,11 @@ __git_config_option-or-value () {
           url:'URL prefixes'
           user:'options controlling user identity'
           web:'web options'
+          versionsort:'tag sorting options'
+          worktree:'git worktree options'
           svn:'git svn options'
-          svn-remote:'git svn remotes')
+          svn-remote:'git svn remotes'
+        )
         () {
           local i
           for i in ${(u)git_present_options%%.*}; do
@@ -3689,7 +3722,8 @@ _git-repack () {
     '--depth=[maximum delta depth]:maximum delta depth' \
     '--threads=[limit maximum number of threads]:threads' \
     '--max-pack-size=-[maximum size of each output packfile]: : __git_guard_bytes "maximum pack size"' \
-    '--pack-kept-objects[repack objects in packs marked with .keep]'
+    '--pack-kept-objects[repack objects in packs marked with .keep]' \
+    '--keep-pack=[ignore named pack]:pack'
 }
 
 (( $+functions[_git-replace] )) ||
@@ -3835,11 +3869,12 @@ _git-get-tar-commit-id () {
 (( $+functions[_git-help] )) ||
 _git-help () {
   _arguments -S -s \
-    '(         -g --guides -i --info -m --man -w --web)'{-a,--all}'[show all available commands]' \
-    '(-a --all -g --guides           -m --man -w --web)'{-i,--info}'[display manual for the command in info format]' \
-    '(-a --all -g --guides -i --info          -w --web)'{-m,--man}'[display manual for the command in man format]' \
-    '(-a --all -g --guides -i --info -m --man         )'{-w,--web}'[display manual for the command in HTML format]' \
-    '(-g --guides)'{-g,--guides}'[prints a list of useful guides on the standard output]' \
+    '(-c --config -i --info -m --man -w --web)'{-a,--all}'[show all available commands]' \
+    '(-)'{-c,--config}'[print all configuration variable names]' \
+    '(-a --all -g --guides -c --config -m --man -w --web)'{-i,--info}'[display manual for the command in info format]' \
+    '(-a --all -g --guides -c --config -i --info -w --web)'{-m,--man}'[display manual for the command in man format]' \
+    '(-a --all -g --guides -c --config -i --info -m --man)'{-w,--web}'[display manual for the command in HTML format]' \
+    '(-g --guides -c --config -i --info -m --man -w --web)'{-g,--guides}'[prints a list of useful guides on the standard output]' \
     '(-v --verbose)'{-v,--verbose}'[print command descriptions]' \
     ': : _alternative commands:command:_git_commands "guides:git guides:(attributes glossary ignore modules revisions tutorial workflows)"'
 }
@@ -4886,6 +4921,7 @@ _git-cat-file () {
     '(--batch)--batch-check=-[print SHA1, type and size (or in specified format)]::format' \
     '--follow-symlinks[follow in-tree symlinks (used with --batch or --batch-check)]' \
     '--batch-all-objects[show all objects with --batch or --batch-check]' \
+    "--unordered[don't order --batch-all-objects output]" \
     '--buffer[disable flushing of output after each object]'
 }
 
@@ -5698,6 +5734,7 @@ _git_commands () {
     notes:'add or inspect object notes'
     pull:'fetch from and merge with another repository or local branch'
     push:'update remote refs along with associated objects'
+    range-diff:'compare two commit ranges'
     rebase:'forward-port local commits to the updated upstream head'
     reset:'reset current HEAD to specified state'
     revert:'revert existing commits'

^ permalink raw reply	[relevance 4%]

* [PATCH] Completion: Change plural descriptions to singular
@ 2018-10-21 22:22  5% dana
  0 siblings, 0 replies; 200+ results
From: dana @ 2018-10-21 22:22 UTC (permalink / raw)
  To: Zsh hackers list

From the completion style guide:

>Group descriptions should be singular because only one thing is being
>completed even though many may be listed. This applies even where you
>complete a list of the things.

Despite this, many functions use plural descriptions. The most noticeable for me
have always been the ones like ls and grep that override the default set by
_files. This patch fixes many of those; there are still a bunch left, but i
thought i'd limit myself

(Also, a few of the ones i changed weren't actually being applied because
they're overridden by a ->state description or because the type function they
call ignores the description set by _arguments. But they're still either wrong
or misleading, so i changed them anyway)

dana


diff --git a/Completion/BSD/Command/_fstat b/Completion/BSD/Command/_fstat
index 5487e6c0f..153378441 100644
--- a/Completion/BSD/Command/_fstat
+++ b/Completion/BSD/Command/_fstat
@@ -9,4 +9,4 @@ _arguments -s \
 '-p+[report all files open by the specified process]:process id:_pids' \
 '-u+[report all files open by the specified user]:user:_users' \
 '-v[verbose mode]' \
-'*:files:_files'
+'*: :_files'

diff --git a/Completion/Unix/Command/_awk b/Completion/Unix/Command/_awk
index 4063743e5..b23ecdf9d 100644
--- a/Completion/Unix/Command/_awk
+++ b/Completion/Unix/Command/_awk
@@ -21,7 +21,7 @@ args=(
   '*'{-v+,--assign}'[assign values to variables]:assignment:'
   '(1)*'{-f+,--file}'[read program file]:program file:->script'
   '1: :_guard "^-*" "program text"'
-  '*:input files:_files'
+  '*:input file:_files'
 )
 
 case $variant in

diff --git a/Completion/Unix/Command/_cat b/Completion/Unix/Command/_cat
index e383f8c6b..c78dc4cd9 100644
--- a/Completion/Unix/Command/_cat
+++ b/Completion/Unix/Command/_cat
@@ -16,7 +16,7 @@ if _pick_variant gnu=GNU unix --version; then
     '(-v --show-nonprinting)'{-v,--show-nonprinting}'[use ^ and M- notation, except for LFD and TAB]'
     '(- : *)--help[display help and exit]'
     '(- : *)--version[output version information and exit]'
-    '*:files:_files'
+    '*: :_files'
   )
 
 elif [[ "$OSTYPE" == (*bsd|dragonfly|darwin)* ]]; then
@@ -29,7 +29,7 @@ elif [[ "$OSTYPE" == (*bsd|dragonfly|darwin)* ]]; then
     '(-v)-t[display tab as ^I (implies -v)]'
     '-u[do not buffer output]'
     '-v[display non-printing chars as ^X or M-a]'
-    '*:files:_files'
+    '*: :_files'
   )
   [[ $OSTYPE = (free|net)bsd* ]] && args+=(
     '-l[set a lock on the stdout file descriptor]'
@@ -48,14 +48,14 @@ elif [[ $OSTYPE = solaris* ]]; then
     '-v[display non-printing chars as ^X or M-a]'
     '-e[display $ at the end of each line (requires -v)]'
     '-t[display tab as ^I and formfeeds and ^L (requires -v)]'
-    '*:files:_files'
+    '*: :_files'
   )
 else
   # POSIX reqires '-u', and most OSes may support '-n'
   args=(
     '-n[number all output lines]'
     '-u[do not buffer output]'
-    '*:files:_files'
+    '*: :_files'
   )
 fi
 

diff --git a/Completion/Unix/Command/_chown b/Completion/Unix/Command/_chown
index 2c63a399a..a97a0e0bf 100644
--- a/Completion/Unix/Command/_chown
+++ b/Completion/Unix/Command/_chown
@@ -40,7 +40,7 @@ else
 fi
 
 (( $+words[(r)--reference*] )) || args+=( '(--reference)1: :->owner' )
-_arguments -C -s "$args[@]" '*:files:->files' && ret=0
+_arguments -C -s "$args[@]" '*: :->files' && ret=0
 
 case $state in
   owner)

diff --git a/Completion/Unix/Command/_df b/Completion/Unix/Command/_df
index a31145cd4..21abff105 100644
--- a/Completion/Unix/Command/_df
+++ b/Completion/Unix/Command/_df
@@ -19,7 +19,7 @@ if _pick_variant gnu=GNU unix --version; then
     '!-v'
     '(- : *)--help[display help and exit]'
     '(- : *)--version[output version information and exit]'
-    '*:files:_umountable'
+    '*: :_umountable'
     - '(format)'
     {-B+,--block-size=}'[specify block size]:size (bytes)'
     '-k[like --block-size=1K]'
@@ -36,7 +36,7 @@ elif [[ "$OSTYPE" == (darwin|dragonfly|freebsd|netbsd*|openbsd)* ]]; then
     '(-G -i -P)-i[include inode usage statistics (default)]'
     '-l[only display locally-mounted file systems]'
     '-n[use previously obtained statistics]'
-    '*:files:_umountable'
+    '*: :_umountable'
   )
   spec='[only display file systems of specified types]:file system type:->fslist'
   case "$OSTYPE" in
@@ -93,7 +93,7 @@ else
     '-k[use 1024-byte blocks]'
     '-P[POSIX compliant output]'
     '-t[include total allocated-space figures in the output]'
-    '*:files:_umountable'
+    '*: :_umountable'
   )
 fi
 

diff --git a/Completion/Unix/Command/_grep b/Completion/Unix/Command/_grep
index 3bc8d3fe0..d3e1b1a6e 100644
--- a/Completion/Unix/Command/_grep
+++ b/Completion/Unix/Command/_grep
@@ -10,11 +10,11 @@ if [[ $service = *GREP_OPT* ]]; then
 else
   arguments=( '(-e --regexp -f --file)1: :_guard "^-*" pattern' )
   if [[ $service = z* ]]; then
-    arguments+=( '*:files:_files -g "*.gz(-.)"' )
+    arguments+=( '*: :_files -g "*.gz(-.)"' )
   elif [[ $service = bz* ]]; then
-    arguments+=( '*:files:_files -g "*.bz2(-.)"' )
+    arguments+=( '*: :_files -g "*.bz2(-.)"' )
   else
-    arguments+=( '*:files:_files' )
+    arguments+=( '*: :_files' )
   fi
   command="$words[1]"
 fi

diff --git a/Completion/Unix/Command/_ls b/Completion/Unix/Command/_ls
index ea96de6ff..cedea1de9 100644
--- a/Completion/Unix/Command/_ls
+++ b/Completion/Unix/Command/_ls
@@ -37,7 +37,7 @@ if ! _pick_variant gnu=gnu unix --help; then
 
     '(-B -b -w -q)-q[hide control chars]'
 
-    '*:files:_files'
+    '*: :_files'
   )
   if [[ "$OSTYPE" = (netbsd*|dragonfly*|freebsd*|openbsd*|darwin*) ]]; then
     arguments+=(

diff --git a/Completion/Unix/Command/_od b/Completion/Unix/Command/_od
index aba400660..046018131 100644
--- a/Completion/Unix/Command/_od
+++ b/Completion/Unix/Command/_od
@@ -61,7 +61,7 @@ else
   esac
 fi
 
-_arguments -C -s -S : "$args[@]" '*:files:_files' && return 0
+_arguments -C -s -S : "$args[@]" '*: :_files' && return 0
 
 case "$state" in
   (format)

diff --git a/Completion/Unix/Command/_pax b/Completion/Unix/Command/_pax
index 7ebaa6aa5..0ae58423c 100644
--- a/Completion/Unix/Command/_pax
+++ b/Completion/Unix/Command/_pax
@@ -44,4 +44,4 @@ _arguments -s \
   '-X[do not descend into directories that have a different device ID]' \
   '-Y[ignore older files by ctime after file name mods]' \
   '-Z[ignore older files after file name mods]' \
-  '*:files:_files'
+  '*: :_files'

diff --git a/Completion/Unix/Command/_rar b/Completion/Unix/Command/_rar
index 906e236fd..68982be60 100644
--- a/Completion/Unix/Command/_rar
+++ b/Completion/Unix/Command/_rar
@@ -58,7 +58,7 @@ case $service in
 	'-n+:file to include:_files' \
 	'-n@+:file of files to include:_files' \
         "$common[@]" \
-        '*:RAR files:_files -g \*.rar\(-.\)'
+        '*:RAR file:_files -g \*.rar\(-.\)'
     fi
   ;;
   rar)
@@ -126,7 +126,7 @@ case $service in
 	'-w+[assign work directory]:work directory:_files -/' \
 	"$common[@]" \
 	'-z+[read archive comment from file]:comment file:_files' \
-	'*:files:_files'
+	'*: :_files'
     fi
   ;;
 esac

diff --git a/Completion/Unix/Command/_rm b/Completion/Unix/Command/_rm
index 912b5eadf..aa24a3fd6 100644
--- a/Completion/Unix/Command/_rm
+++ b/Completion/Unix/Command/_rm
@@ -5,7 +5,7 @@ args=(
   '(-f --force)'{-f,--force}'[ignore nonexistent files, never prompt]'
   '(-I --interactive)-i[prompt before every removal]'
   '(-r -R --recursive)'{-r,-R,--recursive}'[remove directories and their contents recursively]'
-  '*::files:->file'
+  '*:: :->file'
 )
 if _pick_variant gnu=gnu unix --help; then
   opts+=(-S)

diff --git a/Completion/Unix/Command/_xxd b/Completion/Unix/Command/_xxd
index 0034be519..3a8efd664 100644
--- a/Completion/Unix/Command/_xxd
+++ b/Completion/Unix/Command/_xxd
@@ -41,7 +41,7 @@ arguments=(
   {-o+,-offset}'[add specified offset to displayed file position]:offset'
   {-s,-skip,-seek}'[specify file offset to dump from]: :_guard "[0-9]#" "file offset to dump from (absolute or relative)"'
 
-  ':files:_files'
+  ': :_files'
 )
 
 _arguments -S $arguments


^ permalink raw reply	[relevance 5%]

* Re: [PATCH] Add nanosecond support to strftime (zsh/datetime)
  @ 2018-11-07 22:26  3% ` Bart Schaefer
  2018-11-07 22:40  3%   ` dana
  0 siblings, 1 reply; 200+ results
From: Bart Schaefer @ 2018-11-07 22:26 UTC (permalink / raw)
  To: dana; +Cc: zsh-workers

On Wed, Nov 7, 2018 at 2:19 PM dana <dana@dana.is> wrote:
>
> This is a follow-up to workers/43075, which added nanosecond support to (AFAIK)
> the rest of the shell.

I'm having flashbacks that a new format specifier for this may have
been discussed on the austin-group (POSIX standards) mailing list.  If
so, it would be preferable to match here whatever the (still
forthcoming?  I've lost track of austin-group recently) standard is
going to be, rather than invent something of our own.

^ permalink raw reply	[relevance 3%]

* Re: [PATCH] Add nanosecond support to strftime (zsh/datetime)
  2018-11-07 22:26  3% ` Bart Schaefer
@ 2018-11-07 22:40  3%   ` dana
  0 siblings, 0 replies; 200+ results
From: dana @ 2018-11-07 22:40 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: zsh-workers

On 7 Nov 2018, at 16:26, Bart Schaefer <schaefer@brasslantern.com> wrote:
>I'm having flashbacks that a new format specifier for this may have
>been discussed on the austin-group (POSIX standards) mailing list.  If
>so, it would be preferable to match here whatever the (still
>forthcoming?  I've lost track of austin-group recently) standard is
>going to be, rather than invent something of our own.

I don't follow the mailing list, but i couldn't find any mention of it on their
bug tracker.

In any case, this change doesn't add any format specifiers, it just allows
strftime to pass nanoseconds to ztrftime(). The previous change extended the
existing %#. specifier so that you could do %9., and then as an alias for that
it also added %N, which is what GNU date (and anything else using Gnulib's
nstrftime()) uses. If there is something different that might be coming to
POSIX, i agree that it would be nice to support that too/instead. (Though the
previous change was already released, so if we replaced it we might be
breaking stuff...)

dana


^ permalink raw reply	[relevance 3%]

* EXIT trap not executed on error
@ 2018-12-08 20:08  3% Martijn Dekker
  0 siblings, 0 replies; 200+ results
From: Martijn Dekker @ 2018-12-08 20:08 UTC (permalink / raw)
  To: Zsh hackers list

When zsh exits due to an error in a special builtin (e.g. 'set -o 
bad@option') or in an arithmetic expression in $((...)), the EXIT trap 
is not executed.

Is this a bug? I'm not sure -- I can't find anything relevant in the 
POSIX spec. But it seems to me like the trap should be executed. All the 
other shells execute the trap.

Emulation modes don't seem to make a difference to this behaviour.

$ zsh -c 'trap "echo OK" EXIT; : $((1\\2)); echo OOPS'
zsh:1: bad math expression: illegal character: \

(output I would expect: that error message followed by 'OK')

- M.

^ permalink raw reply	[relevance 3%]

* [PATCH] zshmisc(1): document implicit append of `term` when `in word` is omitted
@ 2018-12-15 20:26  3% Joey Pabalinas
       [not found]     ` <1544925190.621790.1610325216.065C6F48@webmail.messagingengine.com>
  0 siblings, 1 reply; 200+ results
From: Joey Pabalinas @ 2018-12-15 20:26 UTC (permalink / raw)
  To: Zsh Workers Mailing List; +Cc: Joey Pabalinas

[-- Attachment #1: Type: text/plain, Size: 1472 bytes --]

In code such as:

> hobbes% () { for arg do print -r $arg; done; } 1 2 3
> 1
> 2
> 3

the implicit `in "$@"` added when the `in word ...` list is omitted
also implicitly appends the separator term, making the above
code valid.

This is fairly innocuous behavior and is POSIX-compliant, so document
this odd edge case rather than risk regressions attempting to change
the lexer/parser code.
---
 Doc/Zsh/grammar.yo | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Doc/Zsh/grammar.yo b/Doc/Zsh/grammar.yo
index d2c7cd29c289287066..33aa3a4c5965771971 100644
--- a/Doc/Zsh/grammar.yo
+++ b/Doc/Zsh/grammar.yo
@@ -185,11 +185,12 @@ cindex(loops, for)
 item(tt(for) var(name) ... [ tt(in) var(word) ... ] var(term) tt(do) var(list) tt(done))(
 where var(term) is at least one newline or tt(;).
 Expand the list of var(word)s, and set the parameter
 var(name) to each of them in turn, executing
 var(list) each time.  If the tt(in) var(word) is omitted,
-use the positional parameters instead of the var(word)s.
+use the positional parameters with a var(term) implicitly
+appended instead of the var(word)s.
 
 More than one parameter var(name) can appear before the list of
 var(word)s.  If var(N) var(name)s are given, then on each execution of the
 loop the next var(N) var(word)s are assigned to the corresponding
 parameters.  If there are more var(name)s than remaining var(word)s, the
-- 
Cheers,
Joey Pabalinas

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[relevance 3%]

* Re: [PATCH] zshmisc(1): document implicit append of `term` when `in word` is omitted
       [not found]     ` <1544925190.621790.1610325216.065C6F48@webmail.messagingengine.com>
@ 2018-12-16  2:01  0%   ` Joey Pabalinas
  2018-12-16 11:54  0%     ` Daniel Shahaf
  0 siblings, 1 reply; 200+ results
From: Joey Pabalinas @ 2018-12-16  2:01 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Zsh Workers Mailing List, Joey Pabalinas

[-- Attachment #1: Type: text/plain, Size: 785 bytes --]

On Sun, Dec 16, 2018 at 01:53:10AM +0000, Daniel Shahaf wrote:
> Joey Pabalinas wrote on Sat, 15 Dec 2018 10:26 -1000:
> > This is fairly innocuous behavior and is POSIX-compliant, so document
> > this odd edge case rather than risk regressions attempting to change
> > the lexer/parser code.
> 
> If preventing regressions is your goal, why not add a regression test using this syntax?

Sorry, maybe my commit message was a bit unclear. I meant instead of
trying to poke at the parser/lexer code to make the behavior match the
documentation, I figured it's easier to just do it the other way and
document the weird edge case.

Sorry for the ambiguity (and the double send, forgot to CC the list),
should I revise the commit description?

-- 
Cheers,
Joey Pabalinas

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[relevance 0%]

* Re: [PATCH] zshmisc(1): document implicit append of `term` when `in word` is omitted
  2018-12-16  2:01  0%   ` Joey Pabalinas
@ 2018-12-16 11:54  0%     ` Daniel Shahaf
  0 siblings, 0 replies; 200+ results
From: Daniel Shahaf @ 2018-12-16 11:54 UTC (permalink / raw)
  To: Joey Pabalinas; +Cc: Zsh Workers Mailing List

Joey Pabalinas wrote on Sat, Dec 15, 2018 at 16:01:27 -1000:
> On Sun, Dec 16, 2018 at 01:53:10AM +0000, Daniel Shahaf wrote:
> > Joey Pabalinas wrote on Sat, 15 Dec 2018 10:26 -1000:
> > > This is fairly innocuous behavior and is POSIX-compliant, so document
> > > this odd edge case rather than risk regressions attempting to change
> > > the lexer/parser code.
> > 
> > If preventing regressions is your goal, why not add a regression test using this syntax?
> 
> Sorry, maybe my commit message was a bit unclear. I meant instead of
> trying to poke at the parser/lexer code to make the behavior match the
> documentation, I figured it's easier to just do it the other way and
> document the weird edge case.
> 
> Sorry for the ambiguity (and the double send, forgot to CC the list),

The double send is my fault, not yours.  It is I who replied off-list in
the first place.

> should I revise the commit description?

Let me begin by saying I'm not familiar enough with the parser to have
an opinion on whether it would be better to have change the docs to
match the code, or change the code to match the docs, or leave this as
an implementation detail that's subject to change.

All that said, I'm not too bothered by the grammar of the log message
(which is now explained in this thread anyway).  I'd sooner suggest
changes to the new text:

> +++ b/Doc/Zsh/grammar.yo
> @@ -185,11 +185,12 @@ cindex(loops, for)
>  item(tt(for) var(name) ... [ tt(in) var(word) ... ] var(term) tt(do) var(list) tt(done))(
>  where var(term) is at least one newline or tt(;).
>  Expand the list of var(word)s, and set the parameter
>  var(name) to each of them in turn, executing
>  var(list) each time.  If the tt(in) var(word) is omitted,
> -use the positional parameters instead of the var(word)s.
> +use the positional parameters with a var(term) implicitly
> +appended instead of the var(word)s.
>  

Two issues here:

1. The docs of var(term) are spread across the first and last sentence.

2. Adding a side remark about var(term) to the last sentence may obscure
that sentence's primary point about the fallback to positional
parameters.

So, perhaps something like this (relative to master):

-where var(term) is at least one newline or tt(;).
 Expand the list of var(word)s, and set the parameter
 var(name) to each of them in turn, executing
-var(list) each time.  If the tt(in) var(word) is omitted,
+var(list) each time.  If the `tt(in) var(word)' is omitted,
 use the positional parameters instead of the var(word)s.
+
+var(term) should be one or more newline or tt(;), and is optional if
+the `tt(in) var(word)' is omitted.

But let's wait for someone familiar with the parser to opine on the
proposed strategic direction ("document the parser's behaviour") before
we spend too much time on implementing that.

>  More than one parameter var(name) can appear before the list of
>  var(word)s.  If var(N) var(name)s are given, then on each execution of the
>  loop the next var(N) var(word)s are assigned to the corresponding
>  parameters.  If there are more var(name)s than remaining var(word)s, the

Cheers,

Daniel


^ permalink raw reply	[relevance 0%]

* The big kre zsh bug report
       [not found]     ` <b8851c3a50bd8bceba1961f2f764e1a6869481ac.camel@ntlworld.com>
@ 2018-12-20 22:25  5%   ` Martijn Dekker
  2018-12-21  7:53  3%     ` Bart Schaefer
  2018-12-21 11:30  2%     ` The big kre zsh bug report Robert Elz
  2018-12-21  2:28  5%   ` Robert Elz
  1 sibling, 2 replies; 200+ results
From: Martijn Dekker @ 2018-12-20 22:25 UTC (permalink / raw)
  To: zsh-workers; +Cc: Robert Elz

Robert Elz a.k.a. kre (author of NetBSD sh) ran "zsh-5.6.2 --emulate sh" 
against his NetBSD sh test suite and came up with a laundry list of test 
failures which he wanted to filter through me. Here are the ones I could 
reproduce in zsh-5.6.2-test-2 and which seemed like bugs to me.

I've ommitted test failures that clearly represent legit zsh extensions, 
such as empty command lists, arithmetic expressions in lieu of numeric 
arguments to builtins, or zsh-specific parameter expansions. At least I 
don't *think* it is the aim of sh/ksh/bash emulation mode to disable all 
zsh extensions -- just those that would cause incompatibilities.

_____________________
First, kre noted that things like $(( x )), $(( x+=1 )), and the like 
don't error out if 'set -u' (-o nounset) is active. I don't think that's 
technically a bug, because 'set -u' is specified to apply to parameter 
expansion and not arithmetic expansion. (Note that $(( $x )) contains a 
parameter expansion and 'set -u' works as expected.)

However, I think having 'set -u' apply to $(( x )) is "obvious" and 
useful behaviour. Bash, ksh93, dash, FreeBSD sh and NetBSD sh already do 
it (mksh and yash don't). I can't think of any downside so this 
behaviour. So perhaps this could be considered a feature request.

All further quoted text below is from kre... (Note his test suite output 
doesn't quote the '-c' option argument, but the tests are executed as if 
it were quoted with single quotes.)

Op 07-12-18 om 11:39 schreef Robert Elz:
> Next, for something different:
> 
> tc-so:Executing command [ zsh --emulate sh -c sleep 1 & P=$!; jobs; wait ]
> tc-se:Fail: regexp Running not in stdout
> tc-se:[1]  + running    sleep 1
> 
> Posix says of the "jobs" command that the status is Running (with a capital R)
> not "running" with a lower case 'r'.    (Same with Done, ...)

Confirmed. (Note this applies to the POSIX locale.)
Ref.: 
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/jobs.html#tag_20_62_10

_____________________
> Next:
> 
> tc-so:Executing command [ zsh --emulate sh -c for op in + - \* / %
> tc-so:            do
> tc-so:                echo $(( $( echo 9 ) $( echo "${op}" ) $( echo 2 ) ))
> tc-so:            done ]
> tc-se:Fail: incorrect exit status: 1, expected: 0
> tc-se:stdout:
> tc-se:11
> tc-se:
> tc-se:stderr:
> tc-se:zsh:3: bad math expression: operator expected at `2 '
> tc-se:
> 
> I am not sure what happened with that one.   zsh did do...
> 
> tc-so:Executing command [ zsh --emulate sh -c echo $(( $( echo 3 ) $( echo + ) $
> ( echo 4 ) )) ]
> 
> correctly, so it can handle expansions inside a $(( )) - perhaps the "${op}"
> being quoted confused it, but those quotes are inside $( ) and will not survive
> the result of that expansion (they are removed before echo runs), so should
> be invisible to the arith code.   They are needed so the '*' is not subject to
> filename expansion in the case when op=\*

Yes, I don't think the "${op}" being quoted has anything to do with it. 
It fails only at the second loop iteration, so with '-' as the operator.

So it doesn't like this:

$ zsh --emulate sh -c 'echo $(( $(echo 9) $(echo -) $(echo 2) ))'
zsh:1: bad math expression: operator expected at `2 '
$ zsh --emulate sh -c 'echo $(( 9 `echo -` 2 ))'
zsh:1: bad math expression: operator expected at `2 '

Whereas these works fine:

$ zsh --emulate sh -c 'echo $(( $(echo 9) - $(echo 2) ))'
7
$ zsh --emulate sh -c 'echo $(( 9 $(echo +) 2 ))'
11
$ zsh --emulate sh -c 'echo $(( 9 $(echo \*) 2 ))'
18
$ zsh --emulate sh -c 'echo $(( 9 `echo /` 2 ))'
4

So yes, that's a bug; the '-' operator is somehow handled differently 
from the rest.

_____________________
> This next one is (smilar to) the one you (Martijn) reported wrt
> the NetBSD shell just recently (one of the new tests I added
> when I fixed it...)
> 
> tc-so:Executing command [ zsh --emulate sh -c . ./h-f3; X=1; set -- ; delim_argv "${X+$@}" ]
> tc-se:Fail: stdout does not match expected value
> tc-se:--- /tmp/inline.Ph8gBl    2018-12-07 05:42:15.944365210 +0000
> tc-se:+++ /tmp/check.JvvNwL/stdout      2018-12-07 05:42:15.944098585 +0000
> tc-se:@@ -1 +1 @@
> tc-se:-
> tc-se:+><       
> 
> "h-f3" just contains the definition of the delim_argv function, which
> is simply...
> 
> delim_argv() {
>         str=
>         for Arg; do
>                 str="${str:+${str} }>${Arg}<"
>         done
>         printf '%s\n' "${str}"
> } 
> 
> ie: stick ><  (angle quotes, except in the backwards
> order, so they are more obvious) around each arg.
> 
> This indicates that zsh is parsing that the same way we did I
> think, and not producing nothing when there are no args from
> the (enclosed) quoted $@
> 
> The output is a diff -u with the first arg ('-' lines in output) being what
> was expected (the file with "inline" in its name) and the 2nd arg ('+'
> lines in the output) being the output from the command.

Hmm... to make this a little easier to understand and reproduce:

zsh --emulate sh -c 'delim_argv() { str=;
		for Arg do str="${str:+${str} }>${Arg}<"; done;
		printf "%s\n" "${str}"; };
	X=1; set -- ; delim_argv "${X+$@}"'

zsh, bash, dash, ksh93, mksh >= R50e, FreeBSD sh output:
 ><

yash, mksh <= R50 output nothing.

So the issue is that "${X+$@}" should be removed completely and not 
leave an empty quoted string if X is set, but there are no positional 
parameters.

Looks logical to me: in that the ${X+$@} parameter substitution 
substitutes $@, within quotes, leaving "$@", which is definitely removed 
completely if there are no positional parameters.

But if this is a bug, it's certainly a widespread one!

_____________________
> tc-se:dollar_hash[7]: Test of 'set -- a b c; echo ${\#}' failed.
> tc-se:[7] expected exit code 2, got 0
> tc-se:[7] Expected messages on stderr, nothing produced
> tc-se:[7] Expected output '', received '3' 
> tc-se:[7] Full command: <<set -- a b c; echo ${\#}>>
> 
> The \ is not removed before var expansion, ${\#} is not ${#}
> and \# is not a valid var name, nor is \ if this is being parsed
> as a substring match on ${\}  
> so this should be a syntax error
> (at least in sh emulation mode).
> 
> There is another test which fails in a similar way for the same
> reason ...
> 
> tc-se:dollar_hash[8]: Test of 'set -- a b c; echo ${\#:-foo}' failed.

Confirmed. ${\#} or ${\#:-foo} should be syntax errors, but aren't.

Same with other special parameters: ${\?}, ${\!}, etc. and even normal 
variables: ${\foo}.

_____________________
> And something different, but related, perhaps:
> 
> tc-se:dollar_hash[48]: Test of 'x=hello; set -- a b c; echo ${#x:-1}' failed.
> tc-se:[48] expected exit code 2, got 0
> tc-se:[48] Expected messages on stderr, nothing produced
> tc-se:[48] Expected output '', received '5'
> tc-se:[48] Full command: <<x=hello; set -- a b c; echo ${#x:-1}>>
> 
> Combining length and set/not set operators is not defined in sh,
> and makes no sense anyway, as ${#x} is always set (it is a number,
> 0 .. whatever, so cannot be unset or null - if 'x' is unset and -u
> is on, there might be an error, but returning the length of $x with
> this nonsense syntax is always incorrect).
> 
> And more like that one ...
> 
> tc-se:dollar_hash[49]: Test of 'x=hello; set -- a b c; echo ${#x-1}' failed.
> tc-se:dollar_hash[50]: Test of 'x=hello; set -- a b c; echo ${#x:+1}' failed.
> tc-se:dollar_hash[51]: Test of 'x=hello; set -- a b c; echo ${#x+1}' failed.
> tc-se:dollar_hash[52]: Test of 'x=hello; set -- a b c; echo ${#x+1}' failed.
> tc-se:dollar_hash[53]: Test of 'x=hello; set -- a b c; echo ${#x:?msg}' failed.
> tc-se:dollar_hash[54]: Test of 'x=hello; set -- a b c; echo ${#x?msg}' failed.
> tc-se:dollar_hash[55]: Test of 'x=hello; set -- a b c; echo ${#x:=val}' failed.
> tc-se:dollar_hash[56]: Test of 'x=hello; set -- a b c; echo ${#x=val}' failed.
> tc-se:dollar_hash[57]: Test of 'x=hello; set -- a b c; echo ${#x#h}' failed.
> tc-se:dollar_hash[58]: Test of 'x=hello; set -- a b c; echo ${#x#*l}' failed.
> tc-se:dollar_hash[59]: Test of 'x=hello; set -- a b c; echo ${#x##*l}' failed.
> tc-se:dollar_hash[60]: Test of 'x=hello; set -- a b c; echo ${#x%o}' failed.
> tc-se:dollar_hash[61]: Test of 'x=hello; set -- a b c; echo ${#x%l*}' failed.
> tc-se:dollar_hash[62]: Test of 'x=hello; set -- a b c; echo ${#x%%l*}' failed.

I'll leave this for the zsh developers to consider, but personally I 
think we can legitimately consider this an artefact of a zsh extension.

Zsh has always allowed combining things in parameter substitutions that 
other shells don't, and allowing nonsensical combinations is simply a 
side effect of allowing other, sensible ones. Zsh accepting something 
other POSIX shells don't, even in emulation mode, will not cause any 
compatibility problems as long as POSIX syntax is followed.

As far as I know, no shell except yash aims to prohibit everything not 
allowed by POSIX in its POSIX compatibility mode. (IOW, for a strict 
POSIX compatibility test, I'd highly recommend 'yash -o posix'!)

_____________________
> Next we get similar tests, but this time we are testing $# rather than the
> length of a var operator...
> 
> tc-se:dollar_hash[79]: Test of 'set -- a b c; echo ${#:-99}' failed.
> tc-se:[79] Expected output '3', received '2'
> tc-se:[79] Full command: <<set -- a b c; echo ${#:-99}>>
> 
> For $# I am not sure posix requires handling the tests for set/unset
> (as $# is always set) so I would understand an error here, but not
> the wrong answer, there are 3 args $# should be 3, it is never unset
> or null, so the :-99 part should just be noise (or an error).

Confirmed:

$ zsh --emulate sh -c 'set -- a b c; echo ${#:-99}'
2

That should be 3, so this is a bug.

Hmmm...

$ zsh --emulate sh -c 'set -- a b c; echo ${#}'
3
$ zsh --emulate sh -c 'set -- a b c; echo ${#:-}'
0
$ zsh --emulate sh -c 'set -- a b c; echo ${#:-3}'
1
$ zsh --emulate sh -c 'set -- a b c; echo ${#:-3000}'
4
$ zsh --emulate sh -c 'set -- a b c; echo ${#:-10chstring}'
10
$ zsh --emulate sh -c 'bar=10chstring; set -- a b c; echo ${#:-$bar}'
10

So what happens is that ${#:-foo} measures the length of whatever 
follows ':-'. Interesting behaviour, but not correct, at least not for 
POSIX mode.

_____________________
> This next one does generate an error from zsh, which is not
> unreasonable:
> 
> tc-se:dollar_hash[80]: Test of 'set -- a b c; echo ${#-99}' failed.
> tc-se:[80] expected exit code 0, got 1
> tc-se:[80] Messages produced on stderr unexpected...
> tc-se:zsh:1: bad substitution
> tc-se:[80] Expected output '3', received ''
> tc-se:[80] Full command: <<set -- a b c; echo ${#-99}>>
> 
> so why it acted differently when the ':' was there is hard to explain.

I think it's a (minor) bug. Since $# is always set, ${#-foo} should be 
accepted as equivalent to $#.

> I see them same (not really incorrect, this is in "unspecified" territory
> I think ... our tests test NetBSD sh behaviour)...
> 
> tc-se:dollar_hash[84]: Test of 'set -- a b c; echo ${#?bogus}' failed.
> tc-se:[84] expected exit code 0, got 1
> tc-se:[84] Messages produced on stderr unexpected...
> tc-se:zsh:1: bad substitution
> tc-se:[84] Expected output '3', received ''
> tc-se:[84] Full command: <<set -- a b c; echo ${#?bogus}>>

IMHO, same as above: $# is always set, so ${#?bogus} should be accepted 
as equivalent to $#.

_____________________
> This next one is an obvious, and common, problem ...
> 
> tc-se:shell_params[13]: Test of 'set -- a b c d; echo ${4294967297}' failed.
> tc-se:[13] Expected output '', received 'a'
> tc-se:[13] Full command: <<set -- a b c d; echo ${4294967297}>>
> 
> (4294967297 & 0xFFFFFFFF) == 1
> 
> This indicates that 32 bit arith overflow occurred, and wasn't detected.

Confirmed (also on ksh93 and dash).

_____________________
> This next one indicates that \newline line joining is not working correctly
> I think ...
> 
> tc-so:Executing command [ zsh --emulate sh -c line='#define bindir "/usr/bin" /* comment */'; echo :"$\
> tc-so:{li\
> tc-so:ne%\
> tc-so:'/*'*}": ] 
> tc-se:Fail: stdout does not match expected value
> tc-se:--- /tmp/inline.FL7wVx    2018-12-07 05:42:17.365040418 +0000
> tc-se:+++ /tmp/check.5PcEmx/stdout      2018-12-07 05:42:17.364785460 +0000
> tc-se:@@ -1 +1 @@
> tc-se:-:#define bindir "/usr/bin" :
> tc-se:+:{line%'/*'*}:   
> 
> Since the previous test ...
> 
> tc-so:Executing command [ zsh --emulate sh -c line='#define bindir "/usr/bin" /* comment */'; echo :${line\
> tc-so:%\
> tc-so:'/*'*}: ]  
> 
> does work, and it should end up being the exact same thing, my guess is
> that the ${ with a \newline between the $ and { is not working as it should.

Confirmed. A bit more experimenting shows that it breaks between '$' and 
'{' and nowhere else. Very unlikely for line continuation to be used 
there in real-life scripts, but still a bug.

$ zsh --emulate sh -c 'foo=bar; echo $\
foo'
bar
$ zsh --emulate sh -c 'foo=bar; echo $\
{foo}'
{foo}
$ zsh -c 'line='\''#define bindir "/usr/bin" /* comment */'\''; echo 
:"${\
l\
i\
n\
e\
%\
'\''\
/\
*\
'\''\
*\
}\
"\
:'
:#define bindir "/usr/bin" :

_____________________
> Next ...
> 
> 	check 't=" x";     IFS=" x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '1'
> 
> The first arg to "check" is evaluated using ${SHELL} -c the 2nd arg is the
> expected output....    This one is test 2 of the IFS tests (test 1 
> passed).
> 
> tc-end: 1544161337.630429, ifs, failed, Test 2 't=" x";     ...': expected [1], found [2 :]
> 
> (unfortunately here, when it fails, the "check" in question truncates the
> command in the message, it is expected you will look at the test source
> to see what it really was.... so I am including the ones that fail.   I should
> improve the output from this "check" function - this was an early version...
> 
> The data enclosed in [] is what should have been output. and what
> actually was.
> 
> I think this means that zsh is not treating non-whitespace IFS characters
> as field terminators, but as field separators, which is not the way it is
> supposed to be.  A later failure is because of the same thing I believe.

Confirmed. Modernish identifies this as a shell quirk (QRK_IFSFINAL) 
because the POSIX spec is quite ambiguous on this, so I was unable to 
confirm that it is actually a bug and not a legitimate behaviour variant.

However, yash's author, magicant, has since decided it's a bug, and 
fixed it, adding a shell option to restore the old behaviour. See 
discussion here:
https://osdn.net/projects/yash/ticket/35283

He refers to this clarification:
http://www.open-std.org/JTC1/SC22/WG15/docs/rr/9945-2/9945-2-98.html

So this should probably be fixed, at least for sh emulation mode.

_____________________
> Next, with IFS containing a digit...
> 
>         check 'IFS=5; echo $(( 123456789 ))'    '1234 6789'
> 
> tc-end: 1544161337.819973, split_arith, failed, Test 1 'IFS=5; echo ...': expect
> ed [1234 6789], found [123456789]

This has come up before on zsh-workers. It's known that artihmetic 
expansion is not subject to field splitting in zsh and they've declined 
to fix it on the grounds that field splitting arithmetic expansions is 
nonsensical. I would prefer consistent field splitting of all dollar 
expansions as the standard requires, but was outgunned by Stéphane 
Chazelas. (IMHO, a more sensible use case would be to split on the 
decimal point.)

_____________________
> The next test is also perhaps dubious, and bizarre, but ...
> 
>         check 'cat <<  echo'"${nl}"'\'"${nl}echo${nl}echo${nl}" 'echo' 0
> 
> ($nl is a single newline character).
> 
> tc-se:lineends[2]: test of 'cat <<  echo
> tc-se:\
> tc-se:echo
> tc-se:echo
> tc-se:' failed
> 
> The point here is that the here doc delimiter
> is unqoted "echo", then in the here doc, the \
> on the first line joins the 2nd line to it, which
> as I read the spec, means it cannot be the
> terminator of the here doc, no matter what it
> ends up containing, as that is required to be
> on a line containing nothing else (after leading
> tabs are stripped if appropriate, which isn't the
> case here, and there aren't any anyway).  If
> a line has been joined to a previous line, I interpret
> that as meaning it cannot be the terminator (and
> allowing it to be adds nothing useful to the shell,
> other than the potential for errors).
> 
> Hence, I believe in this that "echo" is the correct
> output.    But if the first "echo" terminates, we get
> no output (as trailing newlines get removed by
> the command substitution that "check" uses to
> capture the results, so the missing \n from the
> echo command that would follow makes sense).
> 
> But this is a view of how here docs work that is
> not necessarily agreed by all...

Your interpretation looks correct to me, but few real-life shells follow 
it (dash, recent yash, and ksh93 are the ones I can quickly identify).

_____________________
> Next, zsh apparently does not implement the posix
> required -h option.   Nor do we, but we at least allow
> it to be set and cleared ....

I'm not sure that allowing it to be set and then not doing what it 
promises is better than not allowing it to be set at all.

_____________________
> Next onto pattern tests ...
> 
> tc-se:case_matching[51]: Test of 'case "[a-c]" in ([a-c\]) printf M;; (*) printf X;; esac' failed.
> tc-se:[51] Messages produced on stderr unexpected...
> tc-se:zsh:1: bad pattern: [a-c]
> tc-se:[51] Expected output 'M', received ''
> tc-se:[51] Full command: <<case "[a-c]" in ([a-c\]) printf M;; (*) printf X;; esac>>
> 
> There is no such thing as a "bad pattern" in sh - there is no
> [xyz] expression there, because of the quoted ']' (so there is
> no terminator) which means the '[' is just a character, so this
> pattern is effectively just "[a-c]" which should match the word
> which is the same thing.
> 
> It works when the [ is quoted ( \[a-c] ), or when the whole thing is ( "[a-c]" ).

[...and, moved up here from later in the original email...]
> tc-se:var_substring_matching[94]: Test of 'var='[a-c]d-f';printf '%s\n' ${var#[a-c\]}' failed.
> tc-se:[94] expected exit code 0, got 1
> tc-se:[94] Messages produced on stderr unexpected...
> tc-se:zsh:1: bad pattern: [a-c]
> tc-se:[94] Expected output 'd-f', received ''
> tc-se:[94] Full command: <<var='[a-c]d-f';printf '%s\n' ${var#[a-c\]}>>
> 
> This is the same issue as the earlier 'case' matching problem.
> There are no bad patterns in sh.   Ever.   The literal string
> '[a-c]' should be removed from the start of the value of var.

Properly quoted test cases:

$ zsh --emulate sh -c 'case "[a-c]" in ([a-c\]) printf M;;
	(*) printf X;; esac'
$ zsh --emulate sh -c "var='[a-c]d-f';printf '%s\\n' \${var#[a-c\\]}"

Confirmed. The first test case works on all shells except zsh. For the 
second, it works on all except zsh, ksh93 and mksh; the latter two don't 
throw an error, but fail to remove the '[a-c]'.

_____________________
> tc-se:case_matching[90]: Test of 'case a in ([[\:alpha:]]) printf M;; (*) printf X;; esac' failed.
> tc-se:[90] Expected output 'X', received 'M'
> tc-se:[90] Full command: <<case a in ([[\:alpha:]]) printf M;; (*) printf X;; esac>>
> 
> Here the quoted ':' means there is no char-class, so what is
> left is a simple [xyz] expression, where the xyz part is "[\:alpha:"
> the following ']' terminates that expression, and the ']' that
> follows it is just a character.   The 'a' matches the [...] (which
> contains an 'a') but there is nothing in the word to match that
> extra ']', so the match should fail.
> 
> tc-se:case_matching[96]: Test of 'var=":alpha:"; case B in ([["$var"]]) printf M;; (*) printf X;; esac' failed.
> tc-se:[96] Expected output 'X', received 'M'
> tc-se:[96] Full command: <<var=":alpha:"; case B in ([["$var"]]) printf M;; (*) printf X;; esac>>
> 
> This is similar. the : is quoted (via quoted $var) so there is no
> char class, 
> just a [] expression followed by a stray ']'
> 
> tc-se:case_matching[98]: Test of 'var=":alpha:"; case "[]" in ([["$var"]]) printf M;; (*) printf X;; esac' failed.
> tc-se:[98] Expected output 'M', received 'X'
> tc-se:[98] Full command: <<var=":alpha:"; case "[]" in ([["$var"]]) printf M;; (*) printf X;; esac>>
> 
> This is the same thing, buit in a "should match" scenario, the [
> in the word is matched by the [xyz] expression, which contains a '['
> (for the same reason as the last two) and the ] in the word matches
> the trailing ] that comes after the [xyz] expression, and we have a match.

Yes, that looks like three ways of testing the same thing.

Shells differ here. bash, dash, mksh<=R55 print X on the first and 
second, M on the third; yash, ksh93, mksh>=R56c, zsh print M on the 
first and second, X on the third.

_____________________
> tc-se:case_matching[147]: Test of 'var='\z'; case ${var} in (${var}) printf M;; (*) printf X;; esac' failed.
> tc-se:[147] Expected output 'X', received 'M'
> tc-se:[147] Full command: <<var='\z'; case ${var} in (${var}) printf M;; (*) printf X;; esac>>
> 
> The word to match is two chars, backslash and z, the pattern is
> a quoted 'z' (the backslash becomes a quoting character).   After
> that happens, there is nothing magic in the pattern, just one literal
> char, 2 chars can never match 1, so it should fail.

Properly quoted test case:

$ zsh --emulate sh -c 'var='\''\z'\'';
	case ${var} in (${var}) printf M;; (*) printf X;; esac'

This outputs X on bash, dash, ksh93, and M on yash, mksh, zsh.

This issue caused me headaches when implementing match() on modernish. I 
was given the impression that passing backslash-quoted characters 
through variables for use in 'case' patterns was one of those things 
that POSIX doesn't really specify one way or another. Of course I would 
much prefer the behaviour of bash, dash, and NetBSD sh.

_____________________
> tc-se:case_matching[265]: Test of 'var='\'; case '\' in ($var) printf M;; (*) printf X;; esac' failed.
> tc-se:[265] Expected output 'X', received 'M'
> tc-se:[265] Full command: <<var='\'; case '\' in ($var) printf M;; (*) printf X;; esac>>
> 
> This one is excusable, a \ followed by nothing in a pattern is an unspecified
> case, so anything is OK, and treating it as a literal \ is reasonable (it is
> just not what we do ... it is also not what bash does, so as this one also 
> fails in bash emulation, there probably is something that needs fixing.
> 
> tc-se:case_matching[267]: Test of 'set -- \\ \\; case $1 in ($2) printf M;; (*) printf X;; esac' failed.
> tc-se:[267] Expected output 'X', received 'M'
> tc-se:[267] Full command: <<set -- \\ \\; case $1 in ($2) printf M;; (*) printf X;; esac>>
> 
> This one is the same, both pattern and word are '\'  just created
> in a different way.
> 
> tc-se:case_matching[271]: Test of 'case '\' in ($( echo '\' )) printf M;; (*) printf X;; esac' failed.
> tc-se:[271] Expected output 'X', received 'M'
> tc-se:[271] Full command: <<case '\' in ($( echo '\' )) printf M;; (*) printf X;; esac>>
> 
> That one too.   Note all 3 of them work the same in bash as we expect,
> and all 3 still fail with zsh --emulate bash

I'm not sure the zsh authors aim to make emulation modes quite that 
exact, but I'll just leave this here for their consideration.

_____________________
> tc-se:var_substring_matching[47]: Test of 'var='abc';printf '%s\n' ${var%*}' failed.
> tc-se:[47] Expected output 'abc', received 'ab'
> tc-se:[47] Full command: <<var='abc';printf '%s\n' ${var%*}>>
> 
> This one is easy, * should start out matching nothing, that matches, so
> nothing should be removed from the value.   zsh is removing 'c'.
> (Yes, it is a stupid thing to do, or to expect to work, but it is how it
> is supposed to function.)
> 
> tc-se:var_substring_matching[48]: Test of 'var='abc';printf '%s\n' "${var%*}"' failed.
> tc-se:[48] Expected output 'abc', received 'ab'
> tc-se:[48] Full command: <<var='abc';printf '%s\n' "${var%*}">>
> 
> That is the same, just in a quoted expansion (should change nothing).

Properly quoted test cases:

$ zsh --emulate sh -c "var='abc';printf '%s\\n' \${var%*}"
$ zsh --emulate sh -c "var='abc';printf '%s\\n' \"\${var%*}\""

Bug confirmed; every shell except zsh prints 'abc'.

_____________________
Various other tests showed that zsh cannot handle file descriptors >9. 
Not really a bug as POSIX permits that limitation, but:

> And again, this test fails the same way with --emulate bash
> and bash certainly permits fds > 9!

So you could consider this a feature request.

_____________________
> tc-so:Executing command [ zsh --emulate sh -c  set -- a b c d; shift 1 1 ; echo FAILED  ]
> tc-se:Fail: incorrect exit status: 0, expected: anything else
> tc-se:stdout:
> tc-se:FAILED
> tc-se:
> tc-se:stderr:
> tc-se:
> 
> shift should only take 1 arg, not two ... but most shells do not check that,
> so this one is perhaps excusable.

I think builtins should always fail on excess arguments since, outside 
of test cases, that is a clear indication something has gone awry in the 
script.

_____________________
> In this one zsh is generating a syntax error, when there is none...
> 
> tc-so:Executing command [ zsh --emulate sh -c echo hello; true&false&:&cat</dev/null>/dev/null&x=3& echo world ]
> tc-se:Fail: incorrect exit status: 1, expected: 0
> tc-se:stdout:
> tc-se:
> tc-se:stderr:
> tc-se:zsh:1: parse error near `&'
> tc-se:
> 
> Which of the numerous '&' chars it does not like I have no idea.

Looks like zsh doesn't like a bare shell assignment as a background job.

$ zsh --emulate sh -c 'x=3 &'
zsh:1: parse error near `&'

Pointless or not, this is a bug. All other shells accept this.

_____________________
> tc-so:Executing command [ zsh --emulate sh -c case in in (esac|cat ]
> tc-se:Fail: incorrect exit status: 0, expected: anything else
> tc-se:stdout:
> tc-se:
> tc-se:stderr:
> tc-se:
> 
> I cannot even begin to imagine what that nonsense parsed as...
> (but once a case pattern is started with the optional '(' it requires
> the following ')' to complete it, always.

Probably related to zsh accepting empty command lists in all sorts of 
contexts where other shells don't. But it still seems strange that it 
accepts this:

$ zsh --emulate sh -c 'case in in (esac'
(no syntax error produced)

_____________________
> And another one that zsh cannot parse, though again, this one
> is legal...
> 
> tc-so:Executing command [ zsh --emulate sh -c if if :;then :;fi then :;fi ]
> tc-se:Fail: incorrect exit status: 1, expected: 0
> tc-se:stdout:
> tc-se:
> tc-se:stderr:
> tc-se:zsh:1: parse error near `then'
> tc-se:
> 
> I would guess it is the second "then" that follows "fi" with no ';'
> that is the problem, but that is OK syntax.

I would not have thought that to be correct syntax, but sure enough, 
every shell except zsh accepts it.

A similar case (with another reserved word, 'esac', directly preceding 
'then') works fine:

$ zsh --emulate sh -c 'if case a in a) ;; esac then :; fi'
(no syntax error produced)

However, this one fails on zsh and works on all other shells:

$ zsh --emulate sh -c 'if until :; do :; done then :; fi'
zsh:1: parse error near `then'

_____________________
> tc-so:Executing command [ zsh --emulate sh -c case x in (|| | ||) ;; esac ]
> tc-se:Fail: incorrect exit status: 0, expected: anything else
> tc-se:stdout:
> tc-se:
> tc-se:stderr:
> tc-se:
> 
> Not sure what I should say about that one... (but patterns are not
> allowed to be completely missing, there must be a word, and lots
> of alternative illegal missing patterns does not make it legal).

This was a syntax error in zsh until 5.4.1; 5.4.2 starts accepting it.

Strangely, dash accepts this.

_____________________
> There is a failure of a test testing %n notation for jobs in wait commands,
> but I need to check that one more carefully before complaining about it
> failing, it may be that the test is technically invalid (ie: that zsh is
> permitted to act as it does) but this next one ...
> 
> tc-so:Executing command [ zsh --emulate sh -c wait 1 ]
> tc-se:Fail: incorrect exit status: 1, expected: 127
> tc-se:stdout:
> tc-se:
> tc-se:stderr:
> tc-se:zsh:wait:1: pid 1 is not a child of this shell
> tc-se:
> 
> is clearly wrong.  The error message is maybe OK (it is
> correct, though I don't think "wait" is supposed to issue it)
> but the exit status is clearly wrong, wait is required to return
> status 127 when the given pid is not a child, not 1.

Bug confirmed:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/wait.html#tag_20_153_14

ksh93 returns status 0, which is even more wrong! All other shells 
except zsh return status 127.

_____________________

That's it. Hope this is useful!

- M.

^ permalink raw reply	[relevance 5%]

* Re: The big kre zsh bug report
       [not found]     ` <b8851c3a50bd8bceba1961f2f764e1a6869481ac.camel@ntlworld.com>
  2018-12-20 22:25  5%   ` The big kre zsh bug report Martijn Dekker
@ 2018-12-21  2:28  5%   ` Robert Elz
  1 sibling, 0 replies; 200+ results
From: Robert Elz @ 2018-12-21  2:28 UTC (permalink / raw)
  To: Martijn Dekker; +Cc: zsh-workers

    Date:        Thu, 20 Dec 2018 22:25:12 +0000
    From:        Martijn Dekker <martijn@inlv.org>
    Message-ID:  <18f684a8-2fec-4ebe-a63e-cf6688ae519f@inlv.org>

  | Robert Elz a.k.a. kre (author of NetBSD sh)

definitely not author, but I am doing most of the current maintenance
(and a few upgrades).

  | All further quoted text below is from kre... (Note his test suite output 
  | doesn't quote the '-c' option argument, but the tests are executed as if 
  | it were quoted with single quotes.)

Yes, sorry about that, the tests are run by code that almost everyone would
like to replace, and which is mostly not maintained (but, aside from mostly
cosmetic issues like this, does work)...   That's where that output comes from.


  | Yes, I don't think the "${op}" being quoted has anything to do with it. 
  | It fails only at the second loop iteration, so with '-' as the operator.
  |
  | So it doesn't like this:
  |
  | $ zsh --emulate sh -c 'echo $(( $(echo 9) $(echo -) $(echo 2) ))'
  | zsh:1: bad math expression: operator expected at `2 '
  | $ zsh --emulate sh -c 'echo $(( 9 `echo -` 2 ))'
  | zsh:1: bad math expression: operator expected at `2 '

I thought I corrected this one, either later in the big message, or a 
subsequent one.   The issue there is the use of echo where the
first arg starts '-' which is definitely an undefined area (the AT&T
idiots who broke the original Bell Labs echo have a lot to answer
for!)    I am going to change my tests to use printf instead.  There
is no issue here with zsh.

  | $ zsh --emulate sh -c 'set -- a b c; echo ${#:-99}'
  | 2
  |
  | That should be 3, so this is a bug.

Dealing with # in parameter expansions is painful, as it has
so many possible meanings, which need to be figured out
by context (not all of which have defined results).

But where it can reasonably be ${#} (with perhaps some
extra modifiers) it really ought to be.

  | Confirmed. Modernish identifies this as a shell quirk (QRK_IFSFINAL) 
  | because the POSIX spec is quite ambiguous on this, so I was unable to 
  | confirm that it is actually a bug and not a legitimate behaviour variant.

It is not supposed to be ambiguous any more - it used to be once.

In XCU 2.6.5:

The shell shall treat each character of the IFS as a delimiter and use the delimiters as field
terminators to split the results of parameter expansion, command substitution, and arithmetic |
expansion into fields.
[...]
b. Each occurrence in the input of an IFS character that is not IFS white space, along
     with any adjacent IFS white space, shall delimit a field, as described previously.

The std (consistently) uses "delimit" to mean "terminate" not "separate" but 
some readers misunderstand, and assume it means "separate" (as in, occurs 
between)

The intent is that when something is being collected (a field here, a
token in the lexer, ...) and the text says the next char "delimits the xxx"
it means that whatever was being collected is finished, what follows
depends upon what appears (if anything) - the delimiting character
does not, by itself, create anything following.

This is particularly confusing with field splitting, as "IFS" is used, and
the 'S' in that is "separator" which is preisely what it is not.   But the
name is historic.


  | _____________________
  | > Next, zsh apparently does not implement the posix
  | > required -h option.   Nor do we, but we at least allow
  | > it to be set and cleared ....
  |
  | I'm not sure that allowing it to be set and then not doing what it 
  | promises is better than not allowing it to be set at all.

If anything actually differs when that nonsense (implemented as
specified, which I am not sure anything does) is turned on, it would
be something of a surprise, and most likely break everything.

It is just that posix says that the option is supposed to exist, so
some script might turn it on.   Why I could not guess...


  | This issue caused me headaches when implementing match() on modernish. I 
  | was given the impression that passing backslash-quoted characters 
  | through variables for use in 'case' patterns was one of those things 
  | that POSIX doesn't really specify one way or another. Of course I would 
  | much prefer the behaviour of bash, dash, and NetBSD sh.

The pattern matching part of posix is due a rewrite (there is an open issue
with some potential fixes) - the intent is that it should work as you describe,
but it is hard (but not impossible) to read the current text to mean that.

  | _____________________
  | Various other tests showed that zsh cannot handle file descriptors >9. 
  | Not really a bug as POSIX permits that limitation, but:
  |
  | > And again, this test fails the same way with --emulate bash
  | > and bash certainly permits fds > 9!
  |
  | So you could consider this a feature request.

The '9' limit was created when the per-process file descriptor limit
was 16...   The shell needs a few fds for itself, so ...   These days
programs have access to lots more fds, so should sh scripts.


kre


^ permalink raw reply	[relevance 5%]

* Re: The big kre zsh bug report
  2018-12-20 22:25  5%   ` The big kre zsh bug report Martijn Dekker
@ 2018-12-21  7:53  3%     ` Bart Schaefer
  2018-12-25 20:44  8%       ` 'wait' exit status and warnings [was: The big kre zsh bug report] Martijn Dekker
  2018-12-21 11:30  2%     ` The big kre zsh bug report Robert Elz
  1 sibling, 1 reply; 200+ results
From: Bart Schaefer @ 2018-12-21  7:53 UTC (permalink / raw)
  To: Martijn Dekker; +Cc: zsh-workers, Robert Elz

On Thu, Dec 20, 2018 at 2:48 PM Martijn Dekker <martijn@inlv.org> wrote:
>
> However, I think having 'set -u' apply to $(( x )) is "obvious" and
> useful behaviour.

Reasonable.

> > Posix says of the "jobs" command that the status is Running (with a capital R)
> > not "running" with a lower case 'r'.    (Same with Done, ...)

In this instance, I really don't give a damn what Posix says about it.
Zsh follows csh here, and this seems way too trivial to special-case.

> $ zsh --emulate sh -c 'echo $(( $(echo 9) $(echo -) $(echo 2) ))'
> zsh:1: bad math expression: operator expected at `2 '

"echo -" is a special case handled like "echo --" so it echoes nothing
and the operator disappears.  This has nothing to do with math or the
"-" operator.  (Elz has clarified this in his own reply.)

> > tc-so:Executing command [ zsh --emulate sh -c . ./h-f3; X=1; set -- ; delim_argv "${X+$@}" ]
>
> So the issue is that "${X+$@}" should be removed completely and not
> leave an empty quoted string if X is set, but there are no positional
> parameters.
>
> Looks logical to me: in that the ${X+$@} parameter substitution
> substitutes $@, within quotes, leaving "$@", which is definitely removed
> completely if there are no positional parameters.
>
> But if this is a bug, it's certainly a widespread one!

I'd prefer that this continue to act like bash and ksh than to follow
the abstract spec.

> > The \ is not removed before var expansion, ${\#} is not ${#}
> > and \# is not a valid var name, nor is \ if this is being parsed
> > as a substring match on ${\}
> > so this should be a syntax error
> > (at least in sh emulation mode).

This one surprised me.  Seems to come down to this code in params.c:

2341        } else if (inbrace && inull(*s)) {
2342            /*
2343             * Handles things like ${(f)"$(<file)"} by skipping
2344             * the double quotes.  We don't need to know what was
2345             * actually there; the presence of a String or Qstring
2346             * is good enough.
2347             */
2348            s++;

inull() is expected to match a quote there, but it happens to also
match backslash.  Fix in another thread.

> > Combining length and set/not set operators is not defined in sh,
> > and makes no sense anyway, as ${#x} is always set
>
> I'll leave this for the zsh developers to consider, but personally I
> think we can legitimately consider this an artefact of a zsh extension.

Agree, I'm strongly inclined to ignore this.

> > Next we get similar tests, but this time we are testing $# rather than the
> > length of a var operator...
> >
> > tc-se:dollar_hash[79]: Test of 'set -- a b c; echo ${#:-99}' failed.
> > tc-se:[79] Expected output '3', received '2'
> > tc-se:[79] Full command: <<set -- a b c; echo ${#:-99}>>
> >
> > For $# I am not sure posix requires handling the tests for set/unset
> > (as $# is always set) so I would understand an error here, but not
> > the wrong answer, there are 3 args $# should be 3, it is never unset
> > or null, so the :-99 part should just be noise (or an error).
>
> So what happens is that ${#:-foo} measures the length of whatever
> follows ':-'. Interesting behaviour, but not correct, at least not for
> POSIX mode.

Zsh allows an empty parameter name before ":-" and it is handling that
case before doing the length calculation.

That also explains this one:

> > tc-se:dollar_hash[80]: Test of 'set -- a b c; echo ${#-99}' failed.
> > tc-se:[80] expected exit code 0, got 1

% echo ${-99}
zsh: bad substitution

There was quite a lot of discussion of this on zsh-workers a while
back, as I recall, and it was decided at that time that ${#anything}
always means ${#${anything}} never ${${#}anything}.

I'm not expecting this to be up for debate again now.

Same for this one:

> > tc-se:dollar_hash[84]: Test of 'set -- a b c; echo ${#?bogus}' failed.
> > tc-se:[84] expected exit code 0, got 1

This is being treated as ${#${?bogus}}.

> > tc-se:shell_params[13]: Test of 'set -- a b c d; echo ${4294967297}' failed.
> > tc-se:[13] Expected output '', received 'a'
> >
> > This indicates that 32 bit arith overflow occurred, and wasn't detected.
>
> Confirmed (also on ksh93 and dash).

Checking for overflow here seems like a lot of computational expense
for a case that probably only happens in test suites.  Since zsh
implements arrays as actual non-sparse C arrays, memory is going to
explode long before anything manages to assign that many positional
parameters.

> > [...] my guess is
> > that the ${ with a \newline between the $ and { is not working as it should.
>
> Confirmed. A bit more experimenting shows that it breaks between '$' and
> '{' and nowhere else. Very unlikely for line continuation to be used
> there in real-life scripts, but still a bug.

Not going to argue with that one.  I suspect it's because the parser
is treating "{" as beginning a brace expansion (e.g., {a,b,c}) at that
point and so encodes it differently.

> > Next ...
> >
> >       check 't=" x";     IFS=" x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r' '1'
> >
> > I think this means that zsh is not treating non-whitespace IFS characters
> > as field terminators, but as field separators, which is not the way it is
> > supposed to be.  A later failure is because of the same thing I believe.
>
> Confirmed. Modernish identifies this as a shell quirk (QRK_IFSFINAL)
> because the POSIX spec is quite ambiguous on this, so I was unable to
> confirm that it is actually a bug and not a legitimate behaviour variant.

It seems to me that changing this for $=t in zsh native mode might
break a lot of things, so I'll leave it open for discussion as to
whether it's feasible to change it only for emulation mode.  However,
it does differ from the most recent version of bash I have handy.

> > Next, zsh apparently does not implement the posix
> > required -h option.   Nor do we, but we at least allow
> > it to be set and cleared ....

For the lazy or very busy reader:

-h  Locate and remember utilities invoked by functions as those
functions are defined (the utilities are normally located when the
function is executed).

This basically means doing path search at function definition time;
there's some dependence here on the formal definition of "utilities",
as I recall there are other cases where zsh differs from posix on
using builtins or functions to replace utilities.

> > There are no bad patterns in sh.   Ever.   The literal string
> > '[a-c]' should be removed from the start of the value of var.

This one is going to be very tricky to deal with.  Zsh does not
convert "invalid" patterns into plain strings and then match against
the plain strings; a pattern is either valid, or it is never used for
matching in the first place.  Thus in globbing:

% zsh --emulate sh
$ touch "[a-c]"
$ echo *[a-c\]
*[a-c]
$ bash
bash-4.1$ echo *[a-c\]
[a-c]

I think all the related cases cascade from this.

> > tc-se:case_matching[147]: Test of 'var='\z'; case ${var} in (${var}) printf M;; (*) printf X;; esac' failed.
> >
> > The word to match is two chars, backslash and z, the pattern is
> > a quoted 'z' (the backslash becomes a quoting character).

I don't think this is going to get fixed.  I went looking for this
test case but didn't find it:

var='"z"'; case ${var} in (${var}) printf M;; (*) printf X;; esac

This also prints M.  If backslash-z should become a quoted z,
shouldn't the above case also become a quoted z?  So that means in
case statements all variable references have to be treated as if they
were ${(Q)var} (to use zsh-speke)?  What if the quotes aren't
balanced?

> > This one is excusable, a \ followed by nothing in a pattern is an unspecified
> > case [...]
> > That one too.   Note all 3 of them work the same in bash as we expect,
> > and all 3 still fail with zsh --emulate bash
>
> I'm not sure the zsh authors aim to make emulation modes quite that
> exact, but I'll just leave this here for their consideration.

There isn't any --emulate bash, really, it's merly a synonym for
--emulate sh.  And therefore no, it's not intended to be perfect.
(Neither is --emulate ksh, although that has some distinctions from
sh.)

> > tc-se:var_substring_matching[47]: Test of 'var='abc';printf '%s\n' ${var%*}' failed.
> > tc-se:[47] Expected output 'abc', received 'ab'

This one has crept in along the way somewhere, zsh 3.0 and zsh 4.2
both work correctly, zsh 5.3 does not (I don't presently have access
to anything earlier in 5.x to try).

> Various other tests showed that zsh cannot handle file descriptors >9.

That's not true, it just doesn't handle redirection to descriptors >9
without the use of variant syntax.

> > tc-so:Executing command [ zsh --emulate sh -c  set -- a b c d; shift 1 1 ; echo FAILED  ]
> > tc-se:Fail: incorrect exit status: 0, expected: anything else
> > tc-se:stdout:
> > tc-se:FAILED
> > tc-se:
> > tc-se:stderr:
> > tc-se:
> >
> > shift should only take 1 arg, not two ... but most shells do not check that,
> > so this one is perhaps excusable.
>
> I think builtins should always fail on excess arguments since, outside
> of test cases, that is a clear indication something has gone awry in the
> script.

This isn't a bug.  "shift 1 1" means to shift the array $1 by one
position.  Zsh "shift" can even work on multiple arrays at once,
"shift 3 foo bar" means to shift both foo and bar by three.

You could argue that "shift thingthatisnotanarray" should complain,
but it's not about the number of arguments.

> Looks like zsh doesn't like a bare shell assignment as a background job.

This is a bug in native mode too.  Been that way forever.

> > tc-so:Executing command [ zsh --emulate sh -c case in in (esac|cat ]
> >
> > I cannot even begin to imagine what that nonsense parsed as...
> > (but once a case pattern is started with the optional '(' it requires
> > the following ')' to complete it, always.

This is an issue with "-c" and maybe also with a script file input.
If passed to an interactive shell, it's an incomplete parse (the PS2
prompt is printed and the shell waits for more input to finish the
case statement).  So it's not parsing as nonsense, it's parsing as
what it should, and then exiting zero when it hits end-of-file even
though it's still in the middle of a statement.  If I EOF the
interactive shell:

% case in in (esac|cat
case> zsh: parse error near `(esac|cat'

So this has apparently been special-cased to be silent when the shell
is not interactive.

> > tc-so:Executing command [ zsh --emulate sh -c if if :;then :;fi then :;fi ]

Another one that works up through 4.2 but breaks sometime at or before 5.3.

> $ zsh --emulate sh -c 'if until :; do :; done then :; fi'

This, too.

> > tc-so:Executing command [ zsh --emulate sh -c case x in (|| | ||) ;; esac ]
>
> This was a syntax error in zsh until 5.4.1; 5.4.2 starts accepting it.

I get this accepted by every version of zsh that I test.  It matches
the empty string:

% case '' in (|| | ||) print ok;; esac
ok

> > tc-so:Executing command [ zsh --emulate sh -c wait 1 ]
> > tc-se:Fail: incorrect exit status: 1, expected: 127

Probably easily fixed.

^ permalink raw reply	[relevance 3%]

* Re: The big kre zsh bug report
  2018-12-20 22:25  5%   ` The big kre zsh bug report Martijn Dekker
  2018-12-21  7:53  3%     ` Bart Schaefer
@ 2018-12-21 11:30  2%     ` Robert Elz
  1 sibling, 0 replies; 200+ results
From: Robert Elz @ 2018-12-21 11:30 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Martijn Dekker, zsh-workers

    Date:        Thu, 20 Dec 2018 23:53:52 -0800
    From:        Bart Schaefer <schaefer@brasslantern.com>
    Message-ID:  <CAH+w=7bthv3E3Lr3UC9FvroXFcS1W+fDRuS6CLPxy_eyX0szqw@mail.gmail.com>


  | > Looks logical to me: in that the ${X+$@} parameter substitution
  | > substitutes $@, within quotes, leaving "$@", which is definitely removed
  | > completely if there are no positional parameters.

  | I'd prefer that this continue to act like bash and ksh than to follow
  | the abstract spec.

This is a case where it can actually affect some real scripts, even
though it is rare.   That's why I changed what the NetBSD sh does.

  | It seems to me that changing this for $=t in zsh native mode might
  | break a lot of things, so I'll leave it open for discussion as to
  | whether it's feasible to change it only for emulation mode.  However,
  | it does differ from the most recent version of bash I have handy.

bash -c 't=" x";     IFS=" x"; set $t; IFS=":"; r="$*"; IFS=; echo $# $r'
1

That isn't even -o posix mode, nor is it the most recent bash available.
(ksh93 does the same, so does mksh/dash/...).

From the version in the prompt string where you illustrated bash
behaviour later, you seem to be testing against a very old bash
(though apparently bash 3 still exists in the wild).   bash has had
a lot of bugs fixed in the interim (and more keep getting fixed).

  | For the lazy or very busy reader:
  |
  | -h  Locate and remember utilities invoked by functions as those
  | functions are defined (the utilities are normally located when the
  | function is executed).

Yes, I know what it means.   I cannot think of a use for it, nor have
I ever seen any script that would ever use it, nor does anything I
know of implement it (though I have not tried ksh88 - ksh93 might
implement some variant, but not what is specified:

ksh93 -c 'X=$PATH; PATH=/no/such/place; set -h; g() { grep "$@";}; PATH=$X;  g fff /tmp/bbb'
grep: /tmp/bbb: No such file or directory

If the path lookups were done (only) during fn definition, then
that should not find grep rather than run it.   If -h is simply some
kind of optimisation, it is worthless (its purpose had to be as some
kind of way of guaranteeing what PATH would be used for
library functions, so they could be standalone, and work in any
environment, which standard sh "dynamic everything" does not
make easy).

[Aside: it is entirely possible -h will be removed from POSIX sometime,
I think a request for that has been lodged already, but that doesn't
mean that other shells will simply delete that current "support" for it,
ie: allowing it to be set/reset and otherwise ignoring it.]


  | > > tc-se:case_matching[147]: Test of 'var='\z'; case ${var} in (${var}) printf M;; (*) printf X;; esac' failed.
  | > >
  | > > The word to match is two chars, backslash and z, the pattern is
  | > > a quoted 'z' (the backslash becomes a quoting character).
  |
  | I don't think this is going to get fixed.  I went looking for this
  | test case but didn't find it:
  |
  | var='"z"'; case ${var} in (${var}) printf M;; (*) printf X;; esac
  |
  | This also prints M.  If backslash-z should become a quoted z,
  | shouldn't the above case also become a quoted z?

No.   It all comes from the use of RE's to define how glob works
(and the ancient implementation) - '\' quotes magic in an RE, other
forms of shell quoting do not.   So, for the string given to the matcher,
a \ is the one and only quote char.   Where it gets really messy is
that to remain consistent with itself, chars that the shell has already
quoted are never interpreted as magic, even when they would be
if (once the sh quoting is no longer there) they would be in an RE
(which is how in a literal pattern it is possible to use sh quoting
to quote the '-' in a [] match, whereas in a RE only putting the - first
or last removes its "range of chars" meaning).

All this flows from the way the original Bourne shell implemented
quoting ( ch |= 0x80 ) and that quote removal doesn't happen until
after glob or parameter expansion (for the ${var%pattern} etc stuff,
and never happens in case matching (not needed, as all that ever
wants is a match/no-match - no-one cares what matched).  Some of
it is hideous, but we are stuck with it...

In native zsh mode if you want to change that to be more like csh,
which did complain about invalid patterns, that's fine, but for emulating
sh it really isn't.

  | So that means in
  | case statements all variable references have to be treated as if they
  | were ${(Q)var} (to use zsh-speke)?  What if the quotes aren't
  | balanced?

I don't speak zsh, so this is hard, but I can guess, but no, I don't think
that is correct, and as quotes are just chars (the same as they are any
other time a variable is expanded), they don't need to be balanced.
\ is only special because it is defined to be that way for matching (and
because something needs to be for uses of glob matching in other
utilities, like find, which don't do anything like sh quoting, but need to
be able to distinguish a literal '*' from "match anything" somehow, and \
is the way it is done there too.)

  | > I'm not sure the zsh authors aim to make emulation modes quite that
  | > exact, but I'll just leave this here for their consideration.
  |
  | There isn't any --emulate bash, really, it's merly a synonym for
  | --emulate sh.

In that case, I would suggest deleting it.   It gives a false impression.
bash has lots of stuff that sh does not have, how close some of that
is to zsh native mode I have no idea.

  | And therefore no, it's not intended to be perfect.

It depends what you're aiming for with --emulate ... if it is just so you
can run native sh scripts, then most of this does not matter.  But an
alternative use is to allow zsh users to check if their scripts will
work when run with some other sh (ie: if they are portable scripts or
not) and for that, while perfection is not required, allowing too many
variations would render the --emulate stuff useless.

  | That's not true, it just doesn't handle redirection to descriptors >9
  | without the use of variant syntax.

OK.  That's better -- but again, in sh/bash mode, it really should
use sh/bash syntax (again, otherwise the emulation isn't very
useful).


  | % case in in (esac|cat
  | case> zsh: parse error near `(esac|cat'
  |
  | So this has apparently been special-cased to be silent when the shell
  | is not interactive.

I have no knowledge of zsh internals - but in many other shells when
this kind of thing happens, it is more the interactive mode that is
special cased - normally when the shell reads EOF it simply exits
(after running any EDIT trap) and the logic to do that is buried deep
in the input routines.   That is why in a lot of shells all kinds of missing
termination is ignored (which doesn't really affect any valid script,
naturally).


And somehow in all of that I managed to just delete the one case that
caused me to start to reply, so here it is again out of order ...

[Sorry about ugly formatting, it is a side effect of the way I got
this text back into this message...]

  | > > tc-se:shell_params[13]: Test of 'set -- a b c d; echo ${4294967297}'
  | failed. > > tc-se:[13] Expected output '', received 'a' > > > > This
  | indicates that 32 bit arith overflow occurred, and wasn't detected. > >
  | Confirmed (also on ksh93 and dash).

  | Checking for overflow here seems like a lot of computational expense for a
  | case that probably only happens in test suites.  Since zsh implements arrays
  | as actual non-sparse C arrays, memory is going to explode long before
  | anything manages to assign that many positional parameters. 

Don't bet on it.   An array with 2^32 unset elements, followed by one set
(assuming it is implemented in a rational way) is just 8 * (2^32 + 1) bytes,
plus noise, which is just 32 GiB (plus change) - my laptop has 32GiB today
- so we are already very very close to average consumer systems being
able to do that, lots of desktop/server type systems can install 128GiB
(or more) which would be plenty to allow this to work - especially in zsh
which apparently allows
	4294967297=hello
which other shells do not...

jinx$ zsh --emulate sh -c '1=hello; echo ${1}'
hello
jinx$ zsh --emulate sh -c '4294967297=hello; echo ${4294967297}'
hello
jinx$ zsh --emulate sh -c '4294967297=hello; echo ${1}'
hello

But even with other shells:
	set -- '' '' '' '' [repeat 2^32 times] hello
is going to be feasible (if not fast) quite soon now.   Similarly the
even slower, but feasible to write:

	set -- ''
	for i in $(seq 1 32)
	do
		set -- "$@" "$@"
	done
	set -- "$@" hello

[aside: not sure if the args to seq are correct there, didn't atually test 
that... there may be an off by 1], and I know these versions need more
space to store all the empty strings .. but that should be less than
another factor of 2.

On the NetBSD lists (because we cannot currently handle it - in the
kernel) there was recent mention of a HP system, available today, with
48TiB of ram.  For a system like that, a few tens of GiB's of memory
per process is in the noise ...  (handling processes that big is why
systems that big are designed, built, and desired).

The overflow test has negligible cost -- strtoxxx() is not really any more
expensive than atoi() and really ought to be used everywhere, it is
future proof against all kinds of bugs that "cannot happen in real life"
today, but will tomorrow.  The few extra nano-secs the test takes is in
the noise when compared with everything else that is happening.

I am not suggesting there is any need to make being able to set that
many args actually work (we certainly don't) just to detect the overflow.

When that happens here, we just treat the positional parameter as unset
and continue, other places (like as the arg to shift) we treat it just the
same as any other time the value is too big (shift count exceeds $# in
that case), and in some other places it simply becomes an error (sh
cannot handle that, so error("Number out of range") or something.)
Anything but silent truncation of the value.

kre


^ permalink raw reply	[relevance 2%]

* 'wait' exit status and warnings [was: The big kre zsh bug report]
  2018-12-21  7:53  3%     ` Bart Schaefer
@ 2018-12-25 20:44  8%       ` Martijn Dekker
    0 siblings, 1 reply; 200+ results
From: Martijn Dekker @ 2018-12-25 20:44 UTC (permalink / raw)
  To: Bart Schaefer, Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 1247 bytes --]

Op 21-12-18 om 07:53 schreef Bart Schaefer:
> On Thu, Dec 20, 2018 at 2:48 PM Martijn Dekker <martijn@inlv.org> wrote:
>>> tc-so:Executing command [ zsh --emulate sh -c wait 1 ]
>>> tc-se:Fail: incorrect exit status: 1, expected: 127
> 
> Probably easily fixed.

Well, kind of.

I got as far as the attached patch, but it turns out there is another 
bug with 'wait': '(wait %1)' in a subshell quietly returns status 0 even 
if there is no such job. ('(wait %2)' and up are ok.)

I can't figure out how to solve that bug, and perhaps that's for another 
patch anyway, so there is one regression test failure in the attached 
patch (in the second test, where 'wait' is run in a subshell with 
POSIX_BUILTINS set).

I also suppressed the warnings for POSIX_BUILTINS. As Robert Elz already 
suspected, POSIX says: "If one or more pid operands are specified that 
represent unknown process IDs, wait shall treat them as if they were 
known process IDs that exited with exit status 127."[*] (note that 'pid 
operands' include job specs like %1). That means no warnings. (bash 
prints warnings in posix mode too, but, according to my testing, no 
other shell does.)

Thanks,

- M.

[*] http://pubs.opengroup.org/onlinepubs/9699919799/utilities/wait.html

[-- Attachment #2: waitstatus-preliminary.patch --]
[-- Type: text/plain, Size: 4434 bytes --]

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index fd29ca3..cc98323 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -2362,6 +2362,8 @@ then all currently active child processes are waited for.
 Each var(job) can be either a job specification or the process ID
 of a job in the job table.
 The exit status from this command is that of the job waited for.
+If var(job) represents an unknown job or process ID, a warning is printed
+(unless the tt(POSIX_BUILTINS) option is set) and the exit status is 127.
 
 It is possible to wait for recent processes (specified by process ID,
 not by job) that were running in the background even if the process has
diff --git a/Src/jobs.c b/Src/jobs.c
index ed9f81f..73d7f26 100644
--- a/Src/jobs.c
+++ b/Src/jobs.c
@@ -1910,7 +1910,7 @@ getjob(const char *s, const char *prog)
     /* "%%", "%+" and "%" all represent the current job */
     if (*s == '%' || *s == '+' || !*s) {
 	if (curjob == -1) {
-	    if (prog)
+	    if (prog && !isset(POSIXBUILTINS))
 		zwarnnam(prog, "no current job");
 	    returnval = -1;
 	    goto done;
@@ -1921,7 +1921,7 @@ getjob(const char *s, const char *prog)
     /* "%-" represents the previous job */
     if (*s == '-') {
 	if (prevjob == -1) {
-	    if (prog)
+	    if (prog && !isset(POSIXBUILTINS))
 		zwarnnam(prog, "no previous job");
 	    returnval = -1;
 	    goto done;
@@ -1944,7 +1944,7 @@ getjob(const char *s, const char *prog)
 	    returnval = jobnum;
 	    goto done;
 	}
-	if (prog)
+	if (prog && !isset(POSIXBUILTINS))
 	    zwarnnam(prog, "%%%s: no such job", s);
 	returnval = -1;
 	goto done;
@@ -1962,7 +1962,7 @@ getjob(const char *s, const char *prog)
 			returnval = jobnum;
 			goto done;
 		    }
-	if (prog)
+	if (prog && !isset(POSIXBUILTINS))
 	    zwarnnam(prog, "job not found: %s", s);
 	returnval = -1;
 	goto done;
@@ -1976,7 +1976,8 @@ getjob(const char *s, const char *prog)
     }
     /* if we get here, it is because none of the above succeeded and went
     to done */
-    zwarnnam(prog, "job not found: %s", s);
+    if (!isset(POSIXBUILTINS))
+	zwarnnam(prog, "job not found: %s", s);
     returnval = -1;
   done:
     return returnval;
@@ -2375,9 +2376,10 @@ bin_fg(char *name, char **argv, Options ops, int func)
 		    }
 		}
 	    } else if ((retval = getbgstatus(pid)) < 0) {
-		zwarnnam(name, "pid %d is not a child of this shell", pid);
+		if (!isset(POSIXBUILTINS))
+		    zwarnnam(name, "pid %d is not a child of this shell", pid);
 		/* presumably lastval2 doesn't tell us a heck of a lot? */
-		retval = 1;
+		retval = 127;
 	    }
 	    thisjob = ocj;
 	    continue;
@@ -2391,15 +2393,16 @@ bin_fg(char *name, char **argv, Options ops, int func)
 	job = (*argv) ? getjob(*argv, name) : firstjob;
 	firstjob = -1;
 	if (job == -1) {
-	    retval = 1;
+	    retval = 127;
 	    break;
 	}
 	jstat = oldjobtab ? oldjobtab[job].stat : jobtab[job].stat;
 	if (!(jstat & STAT_INUSE) ||
 	    (jstat & STAT_NOPRINT)) {
-	    zwarnnam(name, "%s: no such job", *argv);
+	    if (!isset(POSIXBUILTINS))
+		zwarnnam(name, "%s: no such job", *argv);
 	    unqueue_signals();
-	    return 1;
+	    return 127;
 	}
         /* If AUTO_CONTINUE is set (automatically make stopped jobs running
          * on disown), we actually do a bg and then delete the job table entry. */
diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst
index 5d3d460..2df3602 100644
--- a/Test/A05execution.ztst
+++ b/Test/A05execution.ztst
@@ -341,3 +341,54 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline
 >17
 >19
 
+# Test 'wait' for unknown job/process ID.
+  wait 1
+  echo $?
+  wait %%
+  echo $?
+  wait %+
+  echo $?
+  wait %-
+  echo $?
+  wait %1
+  echo $?
+  wait %foo
+  echo $?
+  wait %\?bar
+127:'wait' exit status and warning for unknown ID
+>127
+>127
+>127
+>127
+>127
+>127
+?(eval):wait:1: pid 1 is not a child of this shell
+?(eval):wait:3: %%: no such job
+?(eval):wait:5: %+: no such job
+?(eval):wait:7: %-: no such job
+?(eval):wait:9: %1: no such job
+?(eval):wait:11: job not found: foo
+?(eval):wait:13: job not found: ?bar
+
+# Test 'wait' for unknown job/process ID (POSIX mode).
+  (setopt POSIX_BUILTINS
+  wait 1
+  echo $?
+  wait %%
+  echo $?
+  wait %+
+  echo $?
+  wait %-
+  echo $?
+  wait %1
+  echo $?
+  wait %foo
+  echo $?
+  wait %\?bar)
+127:'wait' exit status for unknown ID (POSIX mode)
+>127
+>127
+>127
+>127
+>127
+>127

^ permalink raw reply	[relevance 8%]

* [PATCH/RFC] Document some 5.{4,5,6} features in NEWS
@ 2019-01-03 18:47  4% dana
  0 siblings, 0 replies; 200+ results
From: dana @ 2019-01-03 18:47 UTC (permalink / raw)
  To: Zsh workers

I was going through the steps for creating a test release and noticed that
some new features in recent releases weren't mentioned in NEWS. I just wanted
to double-check whether there's any strong feeling regarding what's notable
enough for inclusion here. Is this fine, or is it more granular than intended?

(If the former, i'll probably also update creating-a-release.txt to say
something to the effect that NEWS should generally document new features and
serious bug fixes, but not routine fixes or completion/contrib updates.)

dana


diff --git a/NEWS b/NEWS
index 0b0c0e84c..474ab45a6 100644
--- a/NEWS
+++ b/NEWS
@@ -63,9 +63,19 @@ In shell patterns, [[:blank:]] now honours the locale instead of
 matching exclusively on space and tab, like for the other POSIX
 character classes or for extended regular expressions.
 
-Nanosecond precision on file times is supported in the module
+The zsh/system module now provides the PID of the last process
+substitution via $sysparams[procsubstpid].
+
+Time formatting via the %D prompt escape now offers nanosecond
+precision with the %. and %N format specifiers. Additionally,
+nanosecond precision on file times is supported in the module
 zsh/stat.
 
+The zsh/mathfunc module now includes a log2() function.
+
+The parameter ZLE_RECURSIVE has been added to indicate the
+current ZLE recursion level.
+
 Changes from 5.5 to 5.5.1
 -------------------------
 
@@ -89,9 +99,19 @@ typeset -A assoc=([key1]=val1 [key2]=val2)
 is allowed for compatibility with other shells.  In the case of normal
 arrays the new syntax can be mixed with the old.
 
+The %E, %S, and %U TIMEFMT specifiers now support m and u prefixes
+(e.g., %mE) to output times in milliseconds and microseconds,
+respectively.
+
+The option CHECK_RUNNING_JOBS was added to control whether zsh should
+check for running jobs in addition to suspended ones with CHECK_JOBS.
+It is enabled by default.
+
 Changes from 5.3.1 to 5.4.2
 ---------------------------
 
+There are only minor changes between 5.4 and 5.4.2.
+
 The 'exec' and 'command' precommand modifiers, and options to them, are
 now parsed after parameter expansion.  Previously, both the modifier and
 any options to it were parsed between alias expansion and parameter
@@ -111,6 +131,13 @@ turned on for an individual function with "functions -W".
 zmodload now has an option -s to be silent on a failure to find a module
 but still print other errors.
 
+The autoload builtin gained several new features to load functions from a
+file path determined at the time of definition rather than at the time of
+loading.
+
+The zsh/parameter module now exposes the file paths associated with
+shell functions via the parameters functions_source and
+dis_functions_source.
 
 Changes from 5.2 to 5.3.1
 -------------------------


^ permalink raw reply	[relevance 4%]

* Re: 'wait' exit status and warnings [was: The big kre zsh bug report]
  @ 2019-01-21 22:53  3%           ` Martijn Dekker
  0 siblings, 0 replies; 200+ results
From: Martijn Dekker @ 2019-01-21 22:53 UTC (permalink / raw)
  To: zsh-workers

Op 30-12-18 om 18:13 schreef Peter Stephenson:
> On Tue, 2018-12-25 at 20:44 +0000, Martijn Dekker wrote:
>> Op 21-12-18 om 07:53 schreef Bart Schaefer:
>>> On Thu, Dec 20, 2018 at 2:48 PM Martijn Dekker <martijn@inlv.org> wrote:
>>>>> tc-so:Executing command [ zsh --emulate sh -c wait 1 ]
>>>>> tc-se:Fail: incorrect exit status: 1, expected: 127
>>>
>>> Probably easily fixed.
>>
>> Well, kind of.
>>
>> I got as far as the attached patch, but it turns out there is another 
>> bug with 'wait': '(wait %1)' in a subshell quietly returns status 0 even 
>> if there is no such job. ('(wait %2)' and up are ok.)
> 
> Thanks --- for now I've committed this with the test doctored and a note
> in the test file.

I think my patch is wrong -- now 'fc' in POSIX mode doesn't give
warnings either, though it should. They should only be suppressed for
'wait'.

- M.

^ permalink raw reply	[relevance 3%]

* Issues with fcntl() history file locking
@ 2019-02-27 18:30  2% Philippe Troin
  2019-02-27 21:27  0% ` Bart Schaefer
  0 siblings, 1 reply; 200+ results
From: Philippe Troin @ 2019-02-27 18:30 UTC (permalink / raw)
  To: Zsh hackers list

Hi,

I've been using zsh with share_history for many years and never had any
real issues on several networks where my home directory is mounted over
NFS.  Recently, it's been giving me trouble, maybe when I bumped up my
history file size to 10k entries.

Terminal 1 on host1:                 Terminal 2 on host 2:
host1% echo 1 2 3<ENTER>             host2%
1 2 3
host1%
                                     <ENTER>
                                     host2% <UP>
                                     host2% unrelated command

I'd expect that on pressing UP on host2, my last host1 command would
show up.  It does most of the times, but not reliably enough to make me
completely happy :-)

I then discovered hist_fcntl_lock, which I had not ever set, and turned
it on.  It didn't improve anything.

After a bit of stracing, and subsequent reading of the code, I found
out that the the history file is opened and closed many times during
the history file manipulation, while the lock is maintained on one of
the open descriptors.
Unfortunately, POSIX states that the fcntl() lock will be released upon
the closing the first descriptor to the file.  Quoth 'man -s 3p fcntl':

   All locks associated with a file for a given process
   shall be removed when a file descriptor for that file is closed by
   that process or the process holding that file descriptor terminates.

The key word here is "a", in "a file descriptor ... is closed".
Don't you love standardese?

If you look at Src/hist.c, you'll see that locks are sprinkled
everywhere and both readhistfile() and writehistfile() open the history
file and are cross-recursive.
We can totally end up in a case where:
 * flockhistfile opens and puts a write lock on the history file.
 * writehistfile opens a new fd to the same file
 * history needs to be merged/trimmed or whatever else leading to a
   recursive call to...
 * readhistfile, which opens another fd to the same file, and closes
   it, at which point the lock is lost.
 * writehistfile writes the history file without lock
 * ...

Now I'm not sure if that's what's causing my mysterious shared history
lapses, but fixing that problem shouldn't hurt.

After contemplating Src/hist.c for a bit, it won't be a trivial fix.
I see two ways:  the right and hard way, and the easy messy way.

The right and hard way is to have the various calls to open() the
history file to actually use the flock_fd lock file descriptor (and not
close it when done with it, leaving that to unlockhistfile()).
I think we can open the descriptor in flockhistfile() with O_APPEND
since I haven't spotted any location where we do not write at the end
of the file.  O_APPEND can't hurt if we don't write in the middle of
the file.
That leaves the issue of truncating the file when needed.  We cannot
open(...O_TRUNC...) for the same reason:  we will need ftruncate(),
which we've avoided all these years :-) and probably an autoconf
feature test.
We may also have to keep track (or reset) the seek pointer depending on
the sequencing of the calls, I haven't fully investigated the need for
that.
The whole thing will certainly not improve the clarity of the code :-/

The easy messy way is to keep track of all the open descriptors to the
history file in a global variable, and delaying the actual close until
unlockhistfile() is called.
While it's conceptually fugly, it's arguably easier to maintain and
understand.

Any opinions?

Phil.

===
Full history options and variables
% echo $ZSH_VERSION 
   5.6.2
% setopt | grep hist
   noappendhistory       off
   nobanghist            on
   cshjunkiehistory      off
   extendedhistory       on
   histallowclobber      off
   nohistbeep            off
   histexpiredupsfirst   on
   histfcntllock         on
   histfindnodups        off
   histignorealldups     off
   histignoredups        on
   histignorespace       off
   histlexwords          off
   histnofunctions       off
   histnostore           off
   histreduceblanks      on
   nohistsavebycopy      off
   histsavenodups        off
   histsubstpattern      off
   histverify            off
   incappendhistory      off
   incappendhistorytime  off
   sharehistory          on
% set | grep -i hist
   HISTCHARS='!^#'
   HISTCMD=10264
   HISTFILE=/home/phil/.zhistory
   HISTSIZE=40960
   SAVEHIST=10240



^ permalink raw reply	[relevance 2%]

* Re: Issues with fcntl() history file locking
  2019-02-27 18:30  2% Issues with fcntl() history file locking Philippe Troin
@ 2019-02-27 21:27  0% ` Bart Schaefer
  2019-02-28  6:36  3%   ` Philippe Troin
  0 siblings, 1 reply; 200+ results
From: Bart Schaefer @ 2019-02-27 21:27 UTC (permalink / raw)
  To: Philippe Troin; +Cc: Zsh hackers list

On Wed, Feb 27, 2019 at 10:31 AM Philippe Troin <phil@fifi.org> wrote:
>
> I've been using zsh with share_history for many years and never had any
> real issues on several networks where my home directory is mounted over
> NFS.  Recently, it's been giving me trouble, maybe when I bumped up my
> history file size to 10k entries.
>
> I then discovered hist_fcntl_lock, which I had not ever set, and turned
> it on.  It didn't improve anything.

Well, it wouldn't ... in fact it would likely make things worse.
flock() historically doesn't work reliably over NFS, and if you turn
that option on you are disabling the symlink-based file locking that
is usually more NFS-friendly.  We used to do both kinds of locking
when hist_fcntl_lock, but workers/32580 reverted to using only one
kind ... I forget why I was asked to do that, probably something not
working as fast as was desired.

> Unfortunately, POSIX states that the fcntl() lock will be released upon
> the closing the first descriptor to the file.  [...and thus...]
>
>  * writehistfile writes the history file without lock

If that were the problem, you'd be likely to see corrupted entries
(the read stopping somewhere in the middle of what's being written) or
problems when both shells were writing to the file, which would also
likely manifest as corrupted entries.

Do the entries from terminal 1 NEVER show up in the file?  Are they in
the file but never show up in the history of terminal 2?  Or are they
just slow to arrive in terminal 2?

I'd be more inclined to suspect async NFS issues rather than locking.
Have you strace'd both processes to see when writes v. reads are
happening?

> The right and hard way is to have the various calls to open() the
> history file to actually use the flock_fd lock file descriptor (and not
> close it when done with it, leaving that to unlockhistfile()).
>
> The easy messy way is to keep track of all the open descriptors to the
> history file in a global variable, and delaying the actual close until
> unlockhistfile() is called.

If this actually turns out to be necessary, the second way is more
similar to how we handle descriptors in other parts of the shell.

^ permalink raw reply	[relevance 0%]

* Re: Issues with fcntl() history file locking
  2019-02-27 21:27  0% ` Bart Schaefer
@ 2019-02-28  6:36  3%   ` Philippe Troin
  0 siblings, 0 replies; 200+ results
From: Philippe Troin @ 2019-02-28  6:36 UTC (permalink / raw)
  To: Zsh hackers list

On Wed, 2019-02-27 at 13:27 -0800, Bart Schaefer wrote:
> On Wed, Feb 27, 2019 at 10:31 AM Philippe Troin <phil@fifi.org>
> wrote:
> > I've been using zsh with share_history for many years and never had
> > any
> > real issues on several networks where my home directory is mounted
> > over
> > NFS.  Recently, it's been giving me trouble, maybe when I bumped up
> > my
> > history file size to 10k entries.
> > 
> > I then discovered hist_fcntl_lock, which I had not ever set, and
> > turned
> > it on.  It didn't improve anything.
> 
> Well, it wouldn't ... in fact it would likely make things worse.
> flock() historically doesn't work reliably over NFS, and if you turn
> that option on you are disabling the symlink-based file locking that
> is usually more NFS-friendly.  We used to do both kinds of locking
> when hist_fcntl_lock, but workers/32580 reverted to using only one
> kind ... I forget why I was asked to do that, probably something not
> working as fast as was desired.

Not necessarily worse.
While you're right that (BSD) flock() never worked correctly on NFS,
that is not the case with POSIX fcntl() locks.  Zsh uses the later even
though the zsh functions are named flock*.
Also locking the file with fcntl clears the NFS attribute cache for
that inode, making sure that you get the latest data.

> > Unfortunately, POSIX states that the fcntl() lock will be released
> > upon
> > the closing the first descriptor to the file.  [...and thus...]
> > 
> >  * writehistfile writes the history file without lock
> 
> If that were the problem, you'd be likely to see corrupted entries
> (the read stopping somewhere in the middle of what's being written)
> or
> problems when both shells were writing to the file, which would also
> likely manifest as corrupted entries.
> 
> Do the entries from terminal 1 NEVER show up in the file?  Are they
> in
> the file but never show up in the history of terminal 2?  Or are they
> just slow to arrive in terminal 2?
> 
> I'd be more inclined to suspect async NFS issues rather than locking.
> Have you strace'd both processes to see when writes v. reads are
> happening?

The history file never gets corrupted.  What I'm experiencing is loss
of sync for a while.  New commands on host1 never seem to appear (or
take a long time to appear) on host2.
Given this happens randomly, it's hard to catch zsh in the act.

> > The right and hard way is to have the various calls to open() the
> > history file to actually use the flock_fd lock file descriptor (and
> > not
> > close it when done with it, leaving that to unlockhistfile()).
> > 
> > The easy messy way is to keep track of all the open descriptors to
> > the
> > history file in a global variable, and delaying the actual close
> > until
> > unlockhistfile() is called.
> 
> If this actually turns out to be necessary, the second way is more
> similar to how we handle descriptors in other parts of the shell.

I'll do further experiments.

This is my current hunch:  everything is swell as long as lines are
appended to the history file.  But, when one host decides it's time to
trim the history file is when stuff hits the fan.  If someone had an
idea on how to force zsh to trim history reliably, I'm all ears.

Phil.


^ permalink raw reply	[relevance 3%]

* Re: zargs: Argument list too long
  @ 2019-03-01 21:04  0% ` dana
  0 siblings, 0 replies; 200+ results
From: dana @ 2019-03-01 21:04 UTC (permalink / raw)
  To: gi1242+zsh; +Cc: Zsh hackers list

On 1 Mar 2019, at 12:56, gi1242+zsh@gmail.com wrote:
>But if I do
>
>   zargs -n 1 -- arg1 arg2 arg3 -- echo Argument:
>
>I get an unfriendly error message

The comments at the top of the zargs function source explain this and some
other useful things. On recent versions of zsh you can read it with:

  autoload +X zargs; cat $functions_source[zargs]

It has this to say about -n:

>POSIX -L and -n are mutually exclusive and effectively synonymous;
>zargs accepts both and considers -n to be a limit on the total number
>of arguments per command line, that is, including the initial-args.
>Thus the following fails with "argument list too long":
>  zargs -n 3 -- echo Here are four words
>The smallest limit implied by the combination of -L and -n is used.

So... just use -L (or -l) instead of -n

dana


^ permalink raw reply	[relevance 0%]

* [PATCH] rm: Accept -R as equivalent to -r
@ 2019-03-21  4:13  2% Matthew Martin
  0 siblings, 0 replies; 200+ results
From: Matthew Martin @ 2019-03-21  4:13 UTC (permalink / raw)
  To: zsh-workers

While updating the completers for builtins, I ran across that rm doesn't
support -R while it does support -r. POSIX defines -R and -r as
equivalent. I think it's easier to just support -R in the builtin rm
than fix the completer.

- Matthew Martin

---
 Doc/Zsh/mod_files.yo | 14 +++++++-------
 Src/Modules/files.c  |  7 ++++---
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/Doc/Zsh/mod_files.yo b/Doc/Zsh/mod_files.yo
index 3cf7b61e3..9f9634c86 100644
--- a/Doc/Zsh/mod_files.yo
+++ b/Doc/Zsh/mod_files.yo
@@ -144,15 +144,15 @@ fall back on copying and removing files; if this behaviour is desired,
 use tt(cp) and tt(rm) manually.  This may change in a future version.
 )
 findex(rm)
-item(tt(rm) [ tt(-dfirs) ] var(filename) ...)(
+item(tt(rm) [ tt(-dfiRrs) ] var(filename) ...)(
 Removes files and directories specified.
 
-Normally, tt(rm) will not remove directories (except with the tt(-r)
-option).  The tt(-d) option causes tt(rm) to try removing directories
+Normally, tt(rm) will not remove directories (except with the tt(-R) or tt(-r)
+options).  The tt(-d) option causes tt(rm) to try removing directories
 with tt(unlink) (see manref(unlink)(2)), the same method used for files.
 Typically only the super-user can actually succeed in unlinking
 directories in this way.
-tt(-d) takes precedence over tt(-r).
+tt(-d) takes precedence over tt(-R) and tt(-r).
 
 By default, the user will be queried before removing any file
 that the user cannot write to, but writable files will be silently
@@ -162,9 +162,9 @@ any files.  The tt(-f) option causes files to be
 silently deleted, without querying, and suppresses all error indications.
 tt(-f) takes precedence.
 
-The tt(-r) option causes tt(rm) to recursively descend into directories,
-deleting all files in the directory before removing the directory with
-the tt(rmdir) system call (see manref(rmdir)(2)).
+The tt(-R) and tt(-r) options cause tt(rm) to recursively descend into
+directories, deleting all files in the directory before removing the directory
+with the tt(rmdir) system call (see manref(rmdir)(2)).
 
 The tt(-s) option is a zsh extension to tt(rm) functionality.  It enables
 paranoid behaviour, intended to avoid common security problems involving
diff --git a/Src/Modules/files.c b/Src/Modules/files.c
index 85764d55e..6d20e38a8 100644
--- a/Src/Modules/files.c
+++ b/Src/Modules/files.c
@@ -613,7 +613,8 @@ bin_rm(char *nam, char **args, Options ops, UNUSED(int func))
     rmm.opt_interact = OPT_ISSET(ops,'i') && !OPT_ISSET(ops,'f');
     rmm.opt_unlinkdir = OPT_ISSET(ops,'d');
     err = recursivecmd(nam, OPT_ISSET(ops,'f'), 
-		       OPT_ISSET(ops,'r') && !OPT_ISSET(ops,'d'),
+		       !OPT_ISSET(ops,'d') && (OPT_ISSET(ops,'R') ||
+		                               OPT_ISSET(ops,'r')),
 		       OPT_ISSET(ops,'s'),
 	args, recurse_donothing, rm_dirpost, rm_leaf, &rmm);
     return OPT_ISSET(ops,'f') ? 0 : err;
@@ -798,7 +799,7 @@ static struct builtin bintab[] = {
     BUILTIN("ln",    0, bin_ln,    1, -1, BIN_LN,    LN_OPTS, NULL),
     BUILTIN("mkdir", 0, bin_mkdir, 1, -1, 0,         "pm:",   NULL),
     BUILTIN("mv",    0, bin_ln,    2, -1, BIN_MV,    "fi",    NULL),
-    BUILTIN("rm",    0, bin_rm,    1, -1, 0,         "dfirs", NULL),
+    BUILTIN("rm",    0, bin_rm,    1, -1, 0,         "dfiRrs", NULL),
     BUILTIN("rmdir", 0, bin_rmdir, 1, -1, 0,         NULL,    NULL),
     BUILTIN("sync",  0, bin_sync,  0,  0, 0,         NULL,    NULL),
     /* The "safe" zsh-only names */
@@ -808,7 +809,7 @@ static struct builtin bintab[] = {
     BUILTIN("zf_ln",    0, bin_ln,    1, -1, BIN_LN,    LN_OPTS, NULL),
     BUILTIN("zf_mkdir", 0, bin_mkdir, 1, -1, 0,         "pm:",   NULL),
     BUILTIN("zf_mv",    0, bin_ln,    2, -1, BIN_MV,    "fi",    NULL),
-    BUILTIN("zf_rm",    0, bin_rm,    1, -1, 0,         "dfirs", NULL),
+    BUILTIN("zf_rm",    0, bin_rm,    1, -1, 0,         "dfiRrs", NULL),
     BUILTIN("zf_rmdir", 0, bin_rmdir, 1, -1, 0,         NULL,    NULL),
     BUILTIN("zf_sync",  0, bin_sync,  0,  0, 0,         NULL,    NULL),
 
-- 
2.21.0


^ permalink raw reply	[relevance 2%]

* Re: [RFC] adding zmktemp command
  @ 2019-03-28 21:13  3%     ` Daniel Shahaf
  2019-03-29 15:20  0%       ` Clinton Bunch
  0 siblings, 1 reply; 200+ results
From: Daniel Shahaf @ 2019-03-28 21:13 UTC (permalink / raw)
  To: zsh-workers

Clinton Bunch wrote on Thu, Mar 28, 2019 at 10:00:24 -0500:
> On 3/28/2019 4:38 AM, Daniel Shahaf wrote:
> > Clinton Bunch wrote on Wed, 27 Mar 2019 21:18 +00:00:
> > > I'm thinking of adding a zmktemp command either in a new module (e.g.
> > > zsh/tempfile) or in the zsh/files module.
> > ...
> > > Thoughts?
> > A few.
> > 
> > - I wonder if implementing mktemp in the shell is easier than expecting
> >    people to install a third-party mktemp(1) binary with whatever
> >    functionality they desire.  BSD systems often have both BSD make and
> >    GNU make, so it's conceivable that HP-UX systems could have both the
> >    native mktemp(1) and a third-party one.
> >    (To be clear, I do not object to your RFC; I just wonder if there's a
> >    better solution to the underlying problem.)
> 
> That situation is why I proposed this.  On my HP-UX systems I use gnu
> coreutils mktemp, but either I have to order my path so that it's before
> /usr/bin, which can get me non-standard versions of standard commands which
> might affect the script,

You neglected to explain why none of the other possible solutions to
this subproblem is suitable for you.  (For starters, there are 'add a
directory to the front of $PATH that contains just GNU mktemp and
nothing else' and 'use the "hash" builtin to specify a different mktemp
than the one in $PATH'.)

> or name it something else (which I did, gmktemp). 
> Either way this makes for less portable scripts.

So your problem statement is "HP-UX and Linux use incompatible mktemp(1)
binaries".  I don't understand why that should be fixed in zsh;
shouldn't that be fixed by getting HP-UX to improve their mktemp?
Compare how there is any number of instances in the FreeBSD man pages of
option flags that have been added for compatibility with coreutils (see
ls(1) and find(1) for example).

(By the way: I wonder if mktemp(1) will be added to POSIX?)

As to your proposal itself, I initially thought you were proposing to
implement a drop-in replacement of some mktemp(1) out there (probably
GNU's, though for license reasons it'd be easier to crib BSD's);
however, reading your responses to Peter and Oliver I see that you might
be thinking of adding an *idiomatic* make-temporary-files interface,
e.g., one that returns an fd and/or returns the filename in REPLY to
save a fork.  Which is it?  Could you sketch the API that will be
provided to script authors?  Is it "see GNU coreutils' mktemp(1) man
page, plus the -f option to return an fd"?

> That also requires that
> the script writer have access to install packages or the wherewithal to
> build these packages and install them in their home directory themselves.

By this argument, we should ship an rsync implementation in zsh if HP-UX
doesn't happen to ship rsync in part of its (HP-UX's) default installation.

> > - O_EXCL is exposed by zsh/system's 'sysopen' builtin, so a pure zsh
> >    implementation should be possible.
> 
> I didn't think about a pure zsh implementation, but modifying the template
> character by character in zsh sounds like at least as much work as it is in
> C, but slower.

Given that there's going to be a syscall at the end anyway [open(O_EXCL)],
I'm not sure if the overhead of zsh over pure C would be noticeable.

I mentioned a pure zsh implementation because it could be implemented as
an autoloaded function and released as a plugin (rather than a module),
so it would be installable by users who don't or can't compile their own
zsh, and it would even be compatible with existing zsh binaries out
there.

Cheers,

Daniel

^ permalink raw reply	[relevance 3%]

* Re: [RFC] adding zmktemp command
  2019-03-28 21:13  3%     ` Daniel Shahaf
@ 2019-03-29 15:20  0%       ` Clinton Bunch
  0 siblings, 0 replies; 200+ results
From: Clinton Bunch @ 2019-03-29 15:20 UTC (permalink / raw)
  To: Daniel Shahaf, zsh-workers


On 3/28/2019 4:13 PM, Daniel Shahaf wrote:
> Clinton Bunch wrote on Thu, Mar 28, 2019 at 10:00:24 -0500:
>> On 3/28/2019 4:38 AM, Daniel Shahaf wrote:
>>> Clinton Bunch wrote on Wed, 27 Mar 2019 21:18 +00:00:
>>>> I'm thinking of adding a zmktemp command either in a new module (e.g.
>>>> zsh/tempfile) or in the zsh/files module.
>>> ...
>>>> Thoughts?
>>> A few.
>>>
>>> - I wonder if implementing mktemp in the shell is easier than expecting
>>>     people to install a third-party mktemp(1) binary with whatever
>>>     functionality they desire.  BSD systems often have both BSD make and
>>>     GNU make, so it's conceivable that HP-UX systems could have both the
>>>     native mktemp(1) and a third-party one.
>>>     (To be clear, I do not object to your RFC; I just wonder if there's a
>>>     better solution to the underlying problem.)
>> That situation is why I proposed this.  On my HP-UX systems I use gnu
>> coreutils mktemp, but either I have to order my path so that it's before
>> /usr/bin, which can get me non-standard versions of standard commands which
>> might affect the script,
> You neglected to explain why none of the other possible solutions to
> this subproblem is suitable for you.  (For starters, there are 'add a
> directory to the front of $PATH that contains just GNU mktemp and
> nothing else' and 'use the "hash" builtin to specify a different mktemp
> than the one in $PATH'.)
Didn't think of the hash builtin.
>
>> or name it something else (which I did, gmktemp).
>> Either way this makes for less portable scripts.
> So your problem statement is "HP-UX and Linux use incompatible mktemp(1)
> binaries".  I don't understand why that should be fixed in zsh;
> shouldn't that be fixed by getting HP-UX to improve their mktemp?
> Compare how there is any number of instances in the FreeBSD man pages of
> option flags that have been added for compatibility with coreutils (see
> ls(1) and find(1) for example).

Fixing the incompatibilities of HP-UX mktemp requires more than adding a 
few ignored options.  It doesn't make temporary directories and in fact 
uses -d to specify the directory to create the file.  It doesn't take a 
template string.  And most importantly doesn't create the temporary file 
by default (you have to give a -c option to do so).  It also uses the 
highly predictable HP-UX mktemp(3) to generate the name.

>
> (By the way: I wonder if mktemp(1) will be added to POSIX?)
It should, but who knows if it will.
>
> As to your proposal itself, I initially thought you were proposing to
> implement a drop-in replacement of some mktemp(1) out there (probably
> GNU's, though for license reasons it'd be easier to crib BSD's);
> however, reading your responses to Peter and Oliver I see that you might
> be thinking of adding an *idiomatic* make-temporary-files interface,
> e.g., one that returns an fd and/or returns the filename in REPLY to
> save a fork.  Which is it?  Could you sketch the API that will be
> provided to script authors?  Is it "see GNU coreutils' mktemp(1) man
> page, plus the -f option to return an fd"?

I'm thinking of something that *could* be used as a drop-in replacement 
for mktemp (from the online man pages I can find, other than HP-UX, the 
other *nix with mktemp accept the same short options as GNU), but I want 
to make it better (that seems to be the zsh way :)

The -f var option mentioned elsewhere to return the opened file descriptor.

a -i (invisible) that will unlink the file as soon as it's opened.  (Not 
sure what the use case for this is, but there must be one as both 
stdio's tempfile and perl's File::Temp (as an option) offer this 
functionality)

a -E to erase the file on exit (if I can figure out how to use the exit 
hook)

a -e to clear the FD_CLOEXEC on the file descriptor so external programs 
could inherit the open file descriptor (perhaps useful on those systems 
with /dev/fd or with other specially written programs)

>
>> That also requires that
>> the script writer have access to install packages or the wherewithal to
>> build these packages and install them in their home directory themselves.
> By this argument, we should ship an rsync implementation in zsh if HP-UX
> doesn't happen to ship rsync in part of its (HP-UX's) default installation.
I suppose that argument could be made, but opening temp files is a much 
more common use case than rsync.  To be honest I've used temp files in 
scripts a lot more often than some of the other builtins in zsh/files 
like chgrp or chown
>
>>> - O_EXCL is exposed by zsh/system's 'sysopen' builtin, so a pure zsh
>>>     implementation should be possible.
>> I didn't think about a pure zsh implementation, but modifying the template
>> character by character in zsh sounds like at least as much work as it is in
>> C, but slower.
> Given that there's going to be a syscall at the end anyway [open(O_EXCL)],
> I'm not sure if the overhead of zsh over pure C would be noticeable.
>
> I mentioned a pure zsh implementation because it could be implemented as
> an autoloaded function and released as a plugin (rather than a module),
> so it would be installable by users who don't or can't compile their own
> zsh, and it would even be compatible with existing zsh binaries out
> there.
That wouldn't allow the -i option or the -e mentioned above.
> Cheers,
>
> Daniel

^ permalink raw reply	[relevance 0%]

* Re: Why does zsh un-ignores SIGQUIT?
  @ 2019-04-25 21:17  5%   ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2019-04-25 21:17 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: zsh-workers

2019-04-24 17:13:51 +0100, Peter Stephenson:
> On Wed, 2019-04-24 at 09:13 +0100, Stephane Chazelas wrote:
> > $ (trap '' QUIT; grep SigIgn /proc/self/status; ./Src/zsh -c 'grep SigIgn /proc/self/status')
> > SigIgn: 0000000000000004
> > SigIgn: 0000000000000000
> > 
> > That only seems to be happening for SIGQUIT.
> > 
> > (that's from the current git HEAD)
> 
> SIGQUIT is ignored internally within the shell, visible below the patched
> code.  The shell knows if you've explicitly ignored SIGQUIT, but not if
> it's ignored by inheritance when the shell starts.  The flag is tested
> in entersubsh().
[...patch skipped...]


Thanks,

that change seems to introduce a new inconsistency though. See
below.

POSIX does require that signals that were ignored upon startup
of the shell remain ignored.

POSIX> Signals that were ignored on entry to a non-interactive
POSIX> shell cannot be trapped or reset, although no error need
POSIX> be reported when attempting to do so. An interactive
POSIX> shell may reset or catch signals ignored on entry.

zsh, thankfully ignores that requirement. That's an annoying
requirement that probably has its origin on systems that didn't
have terminal job control (tcsetpgrp()...) and is usually a
nuisance today.

In zsh

   trap handler INT

Installs the "handler" on SIGINT even if SIGINT was ignored on
startup.

What I found out recently though
(https://unix.stackexchange.com/questions/513781/regain-ability-to-use-c-to-close-backgrounded-then-effectively-foregrounded-p/515159#515159)
is that it does seem to honour the POSIX requirement when it
comes to "resetting" the signal disposition (to its default).

In zsh

   trap - INT

Does not reset the default signal disposition of SIGINT if
SIGINT was ignored upon startup.

$ (trap "" INT; zsh -c 'trap - INT; grep SigIgn /proc/self/status')
SigIgn: 0000000000000002

One can work around that by setting a trap first:

$ (trap "" INT; zsh -c 'trap : INT; trap - INT; grep SigIgn /proc/self/status')
SigIgn: 0000000000000000

Even explicitely ignoring it will do:

$ (trap "" INT; zsh -c 'trap "" INT; trap - INT; grep SigIgn /proc/self/status')
SigIgn: 0000000000000000


Which brings us to this change, which means SIGQUIT is now
treated specially in this regard:

$ (trap "" INT QUIT; ./Src/zsh -c 'trap - INT QUIT; grep SigIgn /proc/self/status')
SigIgn: 0000000000000002

(with the diff applied)

I was able to reset the default handler for SIGQUIT but not for
SIGINT.

I wonder what the rationale is for why zsh will happily install a
handler on an ignored signal but not reset to default.

I can understand where the POSIX requirement is coming from.

When you do

myscript &

on a system that doesn't have job control, you don't want
processes started by the script to be killed when you press ^C.

Or when you do:

nohup myscript

You want myscript and all the processes it spawns to be immune
to hang-ups.

But I don't understand why zsh would let me set a handler but
not reset to default. It would make more sense to me if it was the
reverse. If I want to reset the default handler without having
set one in the first place, surely I know what I'm doing and
what I ask should be honoured.

Here, unless someone can think of a good reason why zsh behaves
the way it does, I'd suggest zsh ignore the POSIX requirement
altogether and honours what the user wants: apply the traps as
requested regardless of whether the signal was ignored on
startup or not, at least in zsh emulation.

Maybe honour the POSIX requirements in sh emulation.

While I'm at it, there's another related POSIX requirement that
I often find annoying:

In non-interactive shells.

cmd &

is meant to run cmd with SIGINT and SIGQUIT ignored (some form
of crude and ancient uncalled for job control).

That's actually more annoying than the other POSIX requirement
mentioned earlier.

When you do:

(cmd1 & cmd2) | cmd3


There's no reason cmd1 should not also be killed by SIGINT when
you press ^C.

That's where being able to cancel that ignoring of SIGINT can
come handy.

zsh does honour that latter requirement though.

What's the sentiment of zsh developer about potentially ignoring
that POSIX requirement as well? What can that break?

-- 
Stephane

^ permalink raw reply	[relevance 5%]

* Re: [BUG]builtin echo error doing arguments parsing
  2018-02-24  8:20  5%               ` Stephane Chazelas
@ 2019-04-27  6:46  5%                 ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2019-04-27  6:46 UTC (permalink / raw)
  To: Peter Stephenson, zsh workers

(full quote below for reference as that was over a year ago)

I had requested the Austin Group amend the POSIX specification
to allow for echo -e, echo -E, echo -:

http://austingroupbugs.net/view.php?id=1222

But (as kind of anticipated) it looks like they will add -e and
-E, but not zsh's -

http://austingroupbugs.net/view.php?id=1222#c4373
https://posix@posix.rhansen.org/p/2019-04-25 (password in
https://www.mail-archive.com/austin-group-l@opengroup.org/msg03909.html)

So it may be worth disabling the special handling of "echo -" in
sh emulation (and keep the rest as-is). Also, maybe add a
xpg_echo alias to no_bsd_echo for bash compatibility.

-- 
Stephane

2018-02-24 08:20:40 +0000, Stephane Chazelas:
> 2018-02-22 19:34:23 +0000, Peter Stephenson:
> > On Thu, 22 Feb 2018 20:00:58 +0100
> > Mikael Magnusson <mikachu@gmail.com> wrote:
> > > I didn't try the patch but currently echo
> > > -- just outputs --, only - terminates options for echo.
> > 
> > Yes, the -- behaviour appears to be general behaviour, in fact,
> > so not something that should be changed.  So indeed it's hard to
> > do this at the moment in a shell script without some kind of kludge
> > for zsh.  "disable echo" and use /bin/echo might be the best bet.
> > 
> > However, I'm not really sure if that makes it less or actually more
> > useful to align with other shells (with POSIXBUILTINS) from now on...
> > it's not obvious perpetuating the need for a kludge for ever more
> > is the best bet.
> [...]
> 
> IMO, the best thing to do here is to do nothing. Leave it as it
> is.
> 
> The fact that - marks the end of options in zsh is documented
> and relatively well known.
> 
> See
> https://unix.stackexchange.com/questions/65803/why-is-printf-better-than-echo
> 
> That makes it one of the very few echo implementations that can
> actually output arbitrary strings reliably (the only one  if you
> consider that zsh is the only shell that can store NULs in its
> variables)
> 
> echo -E - $var
> 
> The only other modern implementation I'm aware of that can do
> that (in a Bourne-like shell) is yash's with its:
> 
> ECHO_STYLE=raw echo "$var"
> 
> To be POSIX compliant, echo -- *must* output --<nl> (-- as an
> end-of-option marker must *not* be supported), and support for
> -, -e and -E should be disabled. Also echo -nn should output
> -nn<nl>.
> 
> However doing that would certainly break many scripts as the sh
> emulation is often used to interpret code written for bash and
> bash is also not POSIX compliant in that regard even in POSIX
> mode (unless the xpg_echo option is also enabled) as "echo -e"
> doesn't output "-e<nl>" there.
> 
> To be UNIX compliant, no option should be recognised and -e
> should be the default.
> 
> People already know or should already know that echo cannot be
> used for portability/reliability. It's too late to fix it and
> zsh's implementation is actually the least broken of them (for
> the very reason that it supports a way to mark the end of
> options)..
> 
> zsh does support the POSIX printf and the ksh print which have a
> more reliable and portable API (at least when limited to the
> basic usage of echo, with the caveat that print '\01234' behaves
> differently in zsh than in other ksh implementations).
> 
> See also https://github.com/att/ast/issues/370 for ksh93, where
> they considered changing the behaviour of echo and eventually
> backed down when considering the backward compatibility risk.
> 
> Note that pdksh was another shell that skipped "-" arguments.
> But the "-" didn't mark the end of options, it was a bug in the
> option parsing.

^ permalink raw reply	[relevance 5%]

* PATCH: completion option updates
@ 2019-05-22  0:01  3% Oliver Kiddle
  0 siblings, 0 replies; 200+ results
From: Oliver Kiddle @ 2019-05-22  0:01 UTC (permalink / raw)
  To: Zsh workers

Following is a patch to update a number of completion functions based on
diffing help output. Relevant versions are as follows.

ethtool 5.1
valgrind 3.15.0
sqlite 3.28.0
coreutils 8.31 (env)
ruby 2.6.3p62, gem 3.0.3
dig 9.11.6-P1
util-linux 2.34 (lsblk)
bash 5.0.2
ISC dhclient 4.4.1
entr 4.2
ss 190319
gnutls 3.6.7
cryptsetup 2.2.0
xsetroot 1.1.2
tree 1.8.0
GNU sed 4.7
wget 1.20.3
strace 5.0
tiff 4.0.10
links 2.19
openssh 8.0p1 - this was already done but args to -T were missed
binutils 2.32 (nm and objdump)

Oliver

diff --git a/Completion/Linux/Command/_cryptsetup b/Completion/Linux/Command/_cryptsetup
index ddb2ad3a9..45159d0be 100644
--- a/Completion/Linux/Command/_cryptsetup
+++ b/Completion/Linux/Command/_cryptsetup
@@ -6,7 +6,8 @@ local -a actions state line expl
 (( $#words > 2 )) && ign='!'
 _arguments -s \
   '(-v --verbose)'{-v,--verbose}'[enable verbose mode]' \
-  '--debug[enable debug mode]' \
+  '--debug[show debug messages]' \
+  '--debug-json[show debug messages including JSON metadata]' \
   '(-c --cipher)'{-c+,--cipher=}'[set cipher]:cipher specification' \
   '(-h --hash)'{-h+,--hash=}'[hash algorithm]:hash algorithm' \
   '(-y --verify-passphrase)'{-y,--verify-passphrase}'[query for password twice]' \
@@ -48,6 +49,7 @@ _arguments -s \
   '--perf-same_cpu_crypt[use dm-crypt same_cpu_crypt performance compatibility option]' \
   '--perf-submit_from_crypt_cpus[use dm-crypt submit_from_crypt_cpus performance compatibility option]' \
   '--deferred[device removal is deferred until the last user closes it]' \
+  '--serialize-memory-hard-pbkdf[use global lock to serialize memory]' \
   '--pbkdf=[specify PBKDF algorithm for LUKS2]:algorithm:(argon2i argon2id pbkdf2)' \
   '--pbkdf-memory=[specify PBKDF memory cost limit]:limit (kilobytes)' \
   '--pbkdf-parallel=[specify PBKDF parallel cost]:threads' \
@@ -67,6 +69,19 @@ _arguments -s \
   '--subsystem=[set subsystem label for the LUKS2 device]:subsystem' \
   '--unbound[create unbound (no assigned data segment) LUKS2 keyslot]' \
   '--json-file=[read or write token to json file]:json file:_files -g "*.json(-.)"' \
+  '--luks2-metadata-size=[specify LUKS2 header metadata area size]:size (bytes)' \
+  '--luks2-keyslots-size=[specify LUKS2 header keyslots area size]:size (bytes)' \
+  '--refresh[refresh (reactivate) device with new parameters]' \
+  '--keyslot-key-size=[specify size of the encryption key]:size (bits)' \
+  '--keyslot-cipher=[specify cipher used for LUKS2 keyslot encryption]:cipher' \
+  '--encrypt[Encrypt LUKS2 device (in-place encryption)]' \
+  '--decrypt[decrypt LUKS2 device (remove encryption)]' \
+  '--init-only[initialize LUKS2 reencryption in metadata only]' \
+  '--reduce-device-size=[reduce data device size (move data offset)]:size (bytes)' \
+  '--hotzone-size=[specify maximal reencryption hotzone size]:size (bytes)' \
+  '--resilience=[specify reencryption hotzone resilience type]:resilience type:(checksum journal none)' \
+  '--resilience-hash=[specify reencryption hotzone checksums hash]:string' \
+  '--active-name=[override device autodetection of dm device to be reencrypted]:string' \
   "${ign}(- : *)--version[show version information]" \
   "${ign}(- : *)"{-\?,--help}'[display help information]' \
   "${ign}(- : *)--usage[display brief usage]" \
@@ -82,6 +97,7 @@ case $state in
       'resize:resize an active mapping'
       'benchmark:benchmark cipher'
       'repair:try to repair on-disk metadata'
+      'reencrypt:reencrypt LUKS2 device'
       'erase:erase all keyslots'
       'convert:convert LUKS from/to LUKS2 format'
       'config:set permanent configuration options for LUKS2'
@@ -114,7 +130,7 @@ case $state in
       benchmark) args=( '--cipher=:cipher' );;
       luksKillSlot) args=( $device ':key slot number' );;
       remove|status|resize|*lose|luksSuspend|luksResume) args=( $mapping );;
-      erase|convert|config|repair|(luks(AddKey|Erase|RemoveKey|DelKey|UUID|Dump)|isLuks))
+      erase|convert|config|repair|reencrypt|(luks(AddKey|Erase|RemoveKey|DelKey|UUID|Dump)|isLuks))
 	args=( $device )
       ;;
       luks(Format|AddKey|RemoveKey|ChangeKey|ConvertKey))
diff --git a/Completion/Linux/Command/_ethtool b/Completion/Linux/Command/_ethtool
index 33b7681dc..dccda4684 100644
--- a/Completion/Linux/Command/_ethtool
+++ b/Completion/Linux/Command/_ethtool
@@ -48,7 +48,8 @@ _arguments -C \
   '--get-phy-tunable[get PHY tunable]' \
   '--reset[reset hardware components]' \
   '--show-fec[query device for forward error correction support]' \
-  '--set-fec[configure forward error correction for device]' && return
+  '--set-fec[configure forward error correction for device]' \
+  {-Q,--per-queue}'[apply per-queue command]' && return
 
 if [[ -n $state ]]; then
   case $words[CURRENT-1] in
@@ -61,7 +62,7 @@ if [[ -n $state ]]; then
       _wanted onoff expl 'enabled' compadd off on
     fi
   ;;
-  autoneg|adaptive-[rt]x|raw|hex|sg|tso|ufo|gso|lro|eee|tx-lpi|downshift)
+  autoneg|adaptive-[rt]x|raw|hex|sg|tso|ufo|gso|lro|eee|tx-lpi|downshift|fast-link-down)
     _wanted onoff expl 'enabled' compadd off on
   ;;
   rx-usecs|rx-frames|rx-usecs-irq|rx-frames-irq|tx-usecs|tx-frames) ;&
@@ -70,7 +71,7 @@ if [[ -n $state ]]; then
   rx-frames-high|tx-usecs-high|tx-frames-high|sample-interval|dmac|rx-mini) ;&
   rx-jumbo|offset|length|magic|value|phyad|proto|tos|tclass|l4proto|src-port) ;&
   dst-port|spi|l4data|vlan-etype|vlan|user-def|action|vf|queue|loc) ;&
-  other|combined|tx-timer|count)
+  other|combined|tx-timer|count|msecs)
     _message -e numbers 'number'
   ;;
   speed)
@@ -279,10 +280,10 @@ if [[ -n $state ]]; then
       _wanted behaviours expl behaviour compadd -F line - eee advertise tx-lpi tx-timer
     ;;
     --set-phy-tunable)
-      _wanted options expl tunable compadd -F line - downshift count
+      _wanted options expl tunable compadd -F line - downshift count fast-link-down msecs
     ;;
     --get-phy-tunable)
-      _wanted options expl tunable compadd downshift
+      _wanted options expl tunable compadd downshift fast-link-down
     ;;
     --reset)
       _wanted components expl component compadd flags dedicated all \
@@ -295,6 +296,13 @@ if [[ -n $state ]]; then
         _wanted encodings expl encoding compadd -F line auto off rs baser
       fi
     ;;
+    -Q|--pre-queue)
+      if (( CURRENT == 4 )); then
+        _wanted options expl option compadd - queue_mask
+      elif (( CURRENT == 6)); then
+        _wanted options expl option compadd -c --show-coalescing -C --coalesce
+      fi
+    ;;
     esac
   ;;
   esac
diff --git a/Completion/Linux/Command/_lsblk b/Completion/Linux/Command/_lsblk
index a95ebe776..8a9bc18bf 100644
--- a/Completion/Linux/Command/_lsblk
+++ b/Completion/Linux/Command/_lsblk
@@ -1,11 +1,12 @@
 #compdef lsblk
 
 local sep ret=1
-local -a values dedup suf=( -qS , )
+local -a values dedup suf
 local curcontext="$curcontext" state line expl
 typeset -A opt_args
 
 _arguments -C -s -S \
+  '(H -E --dedup)'{-E+,--dedup=}'[de-duplicate output by specified column]:column:->columns' \
   '(H -a --all)'{-a,--all}'[print all devices]' \
   '(H -b --bytes)'{-b,--bytes}'[print size in bytes rather than in human readable format]' \
   '(H -d --nodeps)'{-d,--nodeps}"[don't print slaves or holders]" \
@@ -30,7 +31,7 @@ _arguments -C -s -S \
   '(H)'{-i,--ascii}'[output ascii characters only]' \
   '(H)'{-J,--json}'[use JSON output format]' \
   '(H)'{-l,--list}'[use list format output]' \
-  '(H)'{-t,--tree}'[use tree format output]' \
+  '(H)'{-T+,--tree=}'[use tree format output]:column:->columns' \
   '(H)'{-P,--pairs}'[use key="value" output format]' \
   '(H)'{-r,--raw}'[use raw output format]' \
   + 'H' \
@@ -44,6 +45,7 @@ case $state in
   ;|
   *list)
     dedup=( ${(Ms.,.)PREFIX##*,} ${(Ms.,.)SUFFIX%%,*} )
+    suf=( -qS , )
     compset -S ',*' && suf=()
     compset -P '*,'
   ;|
diff --git a/Completion/Linux/Command/_ss b/Completion/Linux/Command/_ss
index b1bfa207c..f19b94995 100644
--- a/Completion/Linux/Command/_ss
+++ b/Completion/Linux/Command/_ss
@@ -20,6 +20,7 @@ _arguments -C -s \
   "($info -i --info)"{-i,--info}'[show internal TCP information]' \
   "($info)--tipcinfo[show internal tipc socket information]" \
   "($info -s --summary)"{-s,--summary}'[print summary statistics]' \
+  "($info)--tos[show tos and priority information]" \
   "($info -b --bpf)"{-b,--bpf}'[show bpf filter socket information]' \
   "($info -E --events)"{-E,--events}'[continually display sockets as they are destroyed]' \
   "($info -Z --context)"{-Z,--context}'[display process SELinux security contexts]' \
diff --git a/Completion/Linux/Command/_strace b/Completion/Linux/Command/_strace
index 83ccc6afb..e6dc88659 100644
--- a/Completion/Linux/Command/_strace
+++ b/Completion/Linux/Command/_strace
@@ -19,6 +19,7 @@ _arguments -C -s \
   '(-c)-T[show the time spent in system calls]' \
   '(-xx)-x[print all non-ASCII strings in hexadecimal string format]' \
   '(-x)-xx[print all strings in hexadecimal string format]' \
+  '-X+[set the format for printing of named constants and flags]:format:(raw abbrev verbose)' \
   '(-c -yy)-y[print paths associated with file descriptor arguments]' \
   '(-c -y)-yy[print protocol specific information associated with socket file descriptors]' \
   '(-C -i -k -r -ff -t -tt -ttt -T -y -yy)-c[count time, calls, and errors for each system call and report a summary]' \
diff --git a/Completion/Linux/Command/_valgrind b/Completion/Linux/Command/_valgrind
index 21b7d88c7..4a710e7f7 100644
--- a/Completion/Linux/Command/_valgrind
+++ b/Completion/Linux/Command/_valgrind
@@ -39,6 +39,8 @@ common_report_errors=(
   '--exit-on-first-error=-[exit on the first error]:enable:(yes no)' \
   '--error-exitcode=-[exit code to return if errors found]:exit code' \
   '--error-markers=-[add lines with begin/end markers before/after]:markers (begin,end)' \
+  '(--show-error-list)-s[show detected errors list and suppression counts at exit]' \
+  '--show-error-list=-[show detected errors list and suppression counts at exit]:enable [no]:(yes no)'
   '--keep-debuginfo=-[keep symbols etc for unloaded code]:enable:(yes no)' \
   '--show-below-main=-[continue stack traces below main()]:enable [no]:(yes no)' \
   '--default-suppression=-[load default suppressions]:enable [yes]:(yes no)' \
@@ -84,6 +86,7 @@ args_addrcheck=(
 args_drd=(
   $common_own_malloc
   $common_read_varinfo
+  $common_report_errors
   '--check-stack-var=-[detect data races on stack variables]:enable [no]:(yes no)'
   '--exclusive-threshold=-[print an error if any mutex or writer lock is held longer than specified time]:time (ms)'
   '--first-race-only=-[report only the first detected data race]:enable [no]:(yes no)'
@@ -111,6 +114,7 @@ args_drd=(
 args_memcheck=(
   $args_addrcheck
   $common_read_varinfo
+  $common_report_errors
 )
 
 args_cachegrind=(
@@ -125,6 +129,7 @@ args_cachegrind=(
 args_helgrind=(
   $common_own_malloc
   $common_read_varinfo
+  $common_report_errors
   '--free-is-write=-[treat heap frees as writes]:enable [no]:(yes no)'
   '--track-lockorders=-[show lock ordering errors]:enable [yes]:(no yes)'
   '--history-level=-[specify amount of history to show for data races]:level [full]:((
@@ -186,6 +191,7 @@ args_exp_dhat=(
 
 args_exp_sgcheck=(
   $common_partial
+  $common_report_errors
   '--enable-sg-checks=-[enable stack & global array checking]:enable [yes]:(yes no)'
 )
 
diff --git a/Completion/Unix/Command/_bash b/Completion/Unix/Command/_bash
index cc219f788..383b369cb 100644
--- a/Completion/Unix/Command/_bash
+++ b/Completion/Unix/Command/_bash
@@ -22,6 +22,7 @@ args=(
   '--noprofile[do not load /etc/profile, ~/.bash_profile, etc.]'
   '--norc[do not load ~/.bashrc]'
   '--posix[enable POSIX mode]'
+  '--pretty-print[format a shell script]'
   '(-r --restricted)--restricted[act as restricted shell]'
   '(: -)--version[display version information]'
   # This is ugly, but this way the + variants have accurate descriptions. Note
diff --git a/Completion/Unix/Command/_dhclient b/Completion/Unix/Command/_dhclient
index ee309e1fd..fe5347bbc 100644
--- a/Completion/Unix/Command/_dhclient
+++ b/Completion/Unix/Command/_dhclient
@@ -45,7 +45,7 @@ case $OSTYPE in
       '-g[force giaddr field]:relay:_hosts'
       '-i[use a DUID with DHCPv4 clients]'
       '-I[use the standard DDNS scheme]'
-      '--version[display version number]'
+      '(-)--version[display version number]'
       '(-4)-S[use Information-request to get only stateless configuration]'
       '(-4)*-T[ask for IPv6 temporary addresses]'
       '(-4)-P[enable IPv6 prefix delegation]'
@@ -67,6 +67,10 @@ case $OSTYPE in
       '--request-options[specify list of options the client is to request from the server]:options'
       '--timeout[specify time after which dhclient will decide that no DHCP servers can be contacted]:timeout'
       '--dad-wait-time[specify maximum time to wait for duplicate address detection]:time (seconds)'
+      '--prefix-len-hint[specify length to request when requesting new prefixes]:length'
+      '--decline-wait-time[specify time an IPv4 client should wait between declining an address and issuing a discover]:time (seconds)'
+      '--address-prefix-len[specify length of the prefix for IPv6 addresses]:length'
+      '(-)'{--help,-h}'[display usage information]'
     )
   ;;
 esac
diff --git a/Completion/Unix/Command/_dig b/Completion/Unix/Command/_dig
index d37f18643..d2ffb0a33 100644
--- a/Completion/Unix/Command/_dig
+++ b/Completion/Unix/Command/_dig
@@ -78,6 +78,7 @@ _arguments -s -C $args \
   '*-6[use IPv6 only]' \
   '*-t+[specify type]:type:_dns_types' \
   '*-q+[specify host name to query]:host:_hosts' \
+  "-r[don't read ~/.digrc]" \
   '*-x+[reverse lookup]:IP address' \
   '*-k+[specify TSIG key file]:file:_files' \
   '*-y+[specify TSIG key]:hmac\:name\:key' \
diff --git a/Completion/Unix/Command/_entr b/Completion/Unix/Command/_entr
index e1ba7cf2b..3e2261a18 100644
--- a/Completion/Unix/Command/_entr
+++ b/Completion/Unix/Command/_entr
@@ -4,11 +4,12 @@ local curcontext="$curcontext" state state_descr line ret=1
 typeset -A opt_args
 
 _arguments -s -S \
+  '(-r)-a[respond to all events rather than consolidating them to avoid looping]' \
   '-c[execute clear before invoking utility]' \
   '-d[track directories and exit if a new file is added]' \
   "-n[non-interactive mode; don't access TTY]" \
   '-p[postpone first execution of the utility]' \
-  '-r[reload a persistent child process]' \
+  '(-a)-r[reload a persistent child process]' \
   '(*)-s[evaluate the first argument using interpreter specified by $SHELL]' \
   '(-): :->command' \
   '*::arguments:_normal' && ret=0
diff --git a/Completion/Unix/Command/_env b/Completion/Unix/Command/_env
index 932a7fc89..27978d936 100644
--- a/Completion/Unix/Command/_env
+++ b/Completion/Unix/Command/_env
@@ -11,6 +11,10 @@ case $variant in
       '(--ignore-environment -i --help --version)*'{-u+,--unset=}'[remove variable from the environment]:env var to remove:_parameters -g "*export*"'
       '(-C --chdir)'{-C+,--chdir=}'[change working directory]:directory:_directories'
       '(-S --split-string)'{-S+,--split-string=}'[perform word splitting]:string to split'
+      '--block-signal=[block delivery of signals to command]:signal:_sequence _signals'
+      '--default-signal=[reset handling of signals to the default]:signal:_sequence _signals'
+      '--ignore-signal=[set handling of signals to do nothing]:signal:_sequence _signals'
+      '--list-signal-handling[list non default signal handling to stderr]'
       '(-v --debug)'{-v,--debug}'[print verbose information for each processing step]'
       "${ign}(- *)--help[display help information]"
       "${ign}(- *)--version[display version information]"
diff --git a/Completion/Unix/Command/_gem b/Completion/Unix/Command/_gem
index 512d2d193..b35a5c358 100644
--- a/Completion/Unix/Command/_gem
+++ b/Completion/Unix/Command/_gem
@@ -28,12 +28,12 @@ if [[ $state = command ]]; then
   else
     cmds=(
       build cert check cleanup contents dependency environment fetch
-      generate_index help install list lock mirror open outdated owner
+      generate_index help install info list lock mirror open outdated owner
       pristine push query rdoc search server signin signout sources
       specification stale uninstall unpack update which yank
     )
     cmds=( ${(M)cmds:#${words[1]}*} )
-    if (( ${#cmds} == 1 )); then
+    if (( ${#cmds} == 1 )) || [[ $cmds[1] = install ]]; then
       cmd="$cmds[1]"
       curcontext="${curcontext%:*:*}:gem-${cmd}:"
     fi
@@ -65,15 +65,19 @@ if [[ $state = command ]]; then
     contents|pristine|rdoc)
       args+=( '(*)--all[apply to all installed gems]' )
     ;|
-    list|query) def[local]='!' ;|
+    info|list|query) def[local]='!' ;|
     search) def[remote]='!' ;|
-    list|query|search)
+    info|list|query|search)
       args+=( ${(e)lropts}
         '(-a --all)'{-a,--all}'[display all gem versions]'
         '(-e --exact)'{-e,--exact}'[use exact string matching instead of regex]'
         '(-I --no-installed -i --installed)'{-i,--installed}'[check if gem is installed]'
         '(-I --no-installed -i --installed)'{-I,--no-installed}'[check if gem is not installed]'
         '--no-versions[display only gem names]'
+      )
+    ;|
+    list|query|search)
+      args+=(
         '(-d --details)'{-d,--details}'[display detailed gem information]'
         '!(-d --details)--no-details'
       )
@@ -114,11 +118,15 @@ if [[ $state = command ]]; then
         '--suggestions[suggest alternates when gems are not found]'
       )
     ;|
+    (un|)install|pristine|update)
+      args+=(
+        '(-n --bindir)'{-n,--bindir=}'[specify directory where binary files are located]:directory:_directories'
+      )
+    ;|
     (un|)install|update)
       args+=(
         '--ignore-dependencies[ignore dependency requirements]'
         '(-i --install-dir)'{-i,--install-dir=}'[specify gem repository directory to get installed gems]:directory:_directories'
-        '(-n --bindir)'{-n,--bindir=}'[specify directory where binary files are located]:directory:_directories'
       )
     ;|
     owner|push)
@@ -130,13 +138,18 @@ if [[ $state = command ]]; then
     owner|push|signin|yank)
       args+=( '--host=[use another gemcutter-compatible host]:host:_urls' )
     ;|
+    owner|push|signin)
+      args+=( '--otp=[specify digit code for multifactor authentication]:code' )
+    ;|
     install|pristine|update)
       args+=( '(-E --env-shebang)'{-E,--env-shebang}'[rewrite executables with a shebang of /usr/bin/env]' )
     ;|
-
     build)
       args+=(
-        '--force[skip validation of the spec]'
+        '(--strict)--force[skip validation of the spec]'
+        '(--force)--strict[consider warnings as errors when validating the spec]'
+        '(-o --output)'{-o+,--output=}'[output gem with the given filename]:file:_files'
+        '-C+[run as if specified directory was the current directory]:directory:_directories'
         '1:gemspec file:_files -g "*.gemspec(-.)"'
       )
     ;;
@@ -150,6 +163,7 @@ if [[ $state = command ]]; then
         '(-K --private-key)'{-K,--private-key=}'[specify key for --sign or --build]:key'
         '(-s --sign)'{-s,--sign=}'[sign specified certificate with the key from -K and the certificate from -C]:certificate'
         '(-d --days)'{-d,--days=}'[specify days before certificate expires]:days'
+        '(-R --re-sign)'{-R,--re-sign}'[re-sign the certificate]'
       )
     ;;
     check)
@@ -161,7 +175,10 @@ if [[ $state = command ]]; then
       )
     ;;
     cleanup)
-      args+=( '(-n -d --dryrun)'{-n,-d,--dryrun}"[don't uninstall gems]" )
+      args+=(
+        '(-n -d --dryrun)'{-n,-d,--dryrun}"[don't uninstall gems]"
+        "--user-install[cleanup in user's home directory instead of GEM_HOME]"
+      )
     ;;
     contents)
       args+=(
diff --git a/Completion/Unix/Command/_gnutls b/Completion/Unix/Command/_gnutls
index 6035c5f83..b0d33e2fa 100644
--- a/Completion/Unix/Command/_gnutls
+++ b/Completion/Unix/Command/_gnutls
@@ -60,6 +60,7 @@ case "$service" in
       '--no-ca-verification[disable CA certificate verification]' '!--ca-verification'
       '--ocsp[enable OCSP certificate verification]' '!--no-oscp'
       '(-r --resume)'{-r,--resume}'[establish a session and resume]'
+      '--earlydata=[send early data on resumption from the specified file]:file:_files'
       '(-e --rehandshake)'{-e,--rehandshake}'[connect, establish a session and rehandshake immediately]'
       "--verify-hostname-str=[specify server's hostname to use for validation]:hostname"
       '(-s --starttls)'{-s,--starttls}'[start TLS on EOF or SIGALRM]'
@@ -89,6 +90,7 @@ case "$service" in
       '--inline-commands[inline commands of the form ^<cmd>^]'
       '--inline-commands-prefix=[change delimiter used for inline commands]:delimiter [^]'
       '--fips140-mode[report status of FIPS140-2 mode in gnutls library]'
+      '--logfile=[redirect informational messages to a specific file]:file:_files'
     )
   ;;
 
@@ -97,6 +99,8 @@ case "$service" in
       '--sni-hostname-fatal[send fatal alert on sni-hostname mismatch]'
       '*--alpn=[specify ALPN protocol to be enabled by the server]:protocol'
       '--alpn-fatal[send fatal alert on non-matching ALPN name]'
+      '--earlydata[accept early data]'
+      '--maxearlydata=[specify maximum early data size to accept]:size'
       "--nocookie[don't require cookie on DTLS sessions]"
       '(-g --generate)'{-g,--generate}'[generate Diffie-Hellman parameters]'
       '(-q --quiet)'{-q,--quiet}'[suppress some messages]'
@@ -189,6 +193,8 @@ case "$service" in
       '--stdout-info[print information to stdout instead of stderr]'
       '--ask-pass[enable interaction for entering password when in batch mode]'
       '--pkcs-cipher=[specify cipher to use for pkcs operations]:cipher:(3des 3des-pkcs12 aes-128 aes-192 aes-256 rc2-40 arcfour)'
+      '!(--no-text)--text'
+      "--no-text[don't output textual information before PEM-encoded certificates, private keys, etc]"
     )
   ;;
 
diff --git a/Completion/Unix/Command/_links b/Completion/Unix/Command/_links
index 8bb9fee1c..3f55e9c8b 100644
--- a/Completion/Unix/Command/_links
+++ b/Completion/Unix/Command/_links
@@ -108,10 +108,10 @@ _arguments -C \
   '-html-target-in-new-window[allow opening new windows from html]' \
   '-html-margin[specify margin]:margin (spaces)' \
   '-html-user-font-size[specify font size in graphics mode]:size' \
-  '-html-t-text-color[specify text color in text mode]:color (0..15)' \
-  '-html-t-link-color[specify link color in text mode]:color (0..15)' \
-  '-html-t-background-color[specify background color in text mode]:color (0..15)' \
-  '-html-t-ignore-document-color[ignore colors from HTML in text mode]:ignore:(0 1)' \
+  '-html-text-color[specify text color in text mode]:color (0..15)' \
+  '-html-link-color[specify link color in text mode]:color (0..15)' \
+  '-html-background-color[specify background color in text mode]:color (0..15)' \
+  '-html-ignore-document-color[ignore colors from HTML in text mode]:ignore:(0 1)' \
   '-html-g-text-color[specify text color in graphics mode]:color (0xRRGGBB)' \
   '-html-g-link-color[specify link color in graphics mode]:color (0xRRGGBB)' \
   '-html-g-background-color[specify background color in graphics mode]:color (0xRRGGBB)' \
diff --git a/Completion/Unix/Command/_nm b/Completion/Unix/Command/_nm
index 4754a6623..423fd3223 100644
--- a/Completion/Unix/Command/_nm
+++ b/Completion/Unix/Command/_nm
@@ -59,6 +59,8 @@ if _pick_variant -r variant binutils=GNU elftoolchain=elftoolchain elfutils=elfu
     binutils)
       compset -P '@' && files='*:options file:_files'
       args+=(
+        '!(--no-recurse-limit)--recurse-limit'
+        '--no-recurse-limit[disable demangling recursion limit]'
 	'(-f --format -P)-f+[specify output format]:format:(bsd sysv posix)'
 	'(-C --no-demangle)--demangle=-[decode symbol names]::style:(auto gnu lucid arm hp edg gnu-v3 java gnat rust dlang)'
 	'--plugin[load specified plugin]:plugin'
diff --git a/Completion/Unix/Command/_objdump b/Completion/Unix/Command/_objdump
index 07dbd31de..d502f2803 100644
--- a/Completion/Unix/Command/_objdump
+++ b/Completion/Unix/Command/_objdump
@@ -24,6 +24,7 @@ case $variant in
     "*"{-P,--private=}"[Display object format specific contents]:option"
     "(-h --section-headers --headers)"{-h,--section-headers,--headers}"[Display the contents of the section headers]"
     "(-x --all-headers)"{-x,--all-headers}"[Display the contents of all headers]"
+    '--disassemble=[display assembler contents for specified symbol]:symbol'
     "(-D --disassemble-all)"{-D,--disassemble-all}"[Display assembler contents of all sections]"
     "(-S --source)"{-S,--source}"[Intermix source code with disassembly]"
     "(-g --debugging)"{-g,--debugging}"[Display debug information in object file]"
@@ -55,6 +56,8 @@ case $variant in
     "(-F --file-offsets)"{-F,--file-offsets}"[Include file offsets when displaying information]"
     "(-C --demangle)-C[Decode mangled/processed symbol names]"
     "(-C --demangle)--demangle=-[decode mangled/processed symbol names]::style:(auto gnu lucid arm hp edg gnu-v3 java gnat rust dlang)"
+    '!(--no-recurse-limit)--recurse-limit'
+    '--no-recurse-limit[disable demangling recursion limit]'
     "(-w --wide)"{-w,--wide}"[Format output for more than 80 columns]"
     "(-z --disassemble-zeroes)"{-z,--disassemble-zeroes}"[Do not skip blocks of zeroes when disassembling]"
 
diff --git a/Completion/Unix/Command/_ruby b/Completion/Unix/Command/_ruby
index d69c378fc..11df80f5d 100644
--- a/Completion/Unix/Command/_ruby
+++ b/Completion/Unix/Command/_ruby
@@ -36,15 +36,24 @@ opts=(
   '-s[enable some switch parsing for switches after script name]'
   '-S[look for the script using PATH environment variable]'
   '-T-[turn on tainting checks]::taint level [1]:((0\:strings\ from\ streams/environment/ARGV\ are\ tainted 1\:no\ dangerous\ operation\ by\ tainted\ value 2\:process/file\ operations\ prohibited 3\:all\ generated\ objects\ are\ tainted 4\:no\ global\ \(non-tainted\)\ variable\ modification/no\ direct\ output))'
-  '(-v --verbose)'{-v,--verbose}'[print version number, then turn on verbose mode]'
+  '(--verbose)-v[print version number, then turn on verbose mode]'
+  '(-v)--verbose[turn on verbose mode and disable script from stdin]'
   '-x-[strip off text before #!ruby line and perhaps cd to directory]:directory:_files -/'
   '(1 * -)--copyright[print the copyright]'
-  --{en,dis}'able=[enable or disable features]:feature:(gems did_you_mean rubyopt frozen_string_literal all)'
+  --{en,dis}'able=[enable or disable features]:feature:(gems did_you_mean rubyopt frozen_string_literal jit all)'
   \!--{en,dis}able-{gems,rubyopt,all}
   '--dump=[dump debug information]:information:_sequence compadd - insns yydebug parsetree parsetree_with_comment'
   --{external,internal}'-encoding=:charset:->charsets'
   '!'{-y,--yydebug}
   '!--dump=:target:(version copyright usage yydebug syntax parsetree parsetree_with_comment insns)'
+  '--jit[enable jit with default options]'
+  '--jit-warnings[enable printing JIT warnings]'
+  '--jit-debug[enable JIT debugging (very slow)]'
+  '--jit-wait[wait until JIT compilation is finished everytime (for testing)]'
+  '--jit-save-temps[save JIT temporary files]'
+  '--jit-verbose=-[print JIT logs of level num or less to stderr]:maximum log level [0]'
+  '--jit-max-cache=-[specify max number of methods to be JIT-ed in a cache]:number [1000]'
+  '--jit-min-calls=-[specify number of calls to trigger JIT]:calls [5]'
 )
 
 irb=(
@@ -70,7 +79,6 @@ irb=(
 
 erb=(
   "-P[don't evaluate lines which start with %]"
-  '-S[specify safe level for running script]:level:(1 2 3 4)'
   '-T[specify trim mode]:mode [0]:((0\:EOL\ remains 1\:EOL\ removed\ if\ line\ ends\ with\ %\> 2\:EOL\ removed\ if\ line\ starts\ with\ \<%\ and\ ends\ with\ %\> -\:EOL\ is\ removed\ if\ line\ ends\ with\ -%\>,\ leading\ whitespace\ removed\ after\ \<%-))'
   '(-d --debug)'{-d,--debug}'[set debugging flags (set $DEBUG to true)]'
   '-n[used with -x, prepends line number to output]'
diff --git a/Completion/Unix/Command/_sed b/Completion/Unix/Command/_sed
index f03278364..b08eaa281 100644
--- a/Completion/Unix/Command/_sed
+++ b/Completion/Unix/Command/_sed
@@ -66,6 +66,7 @@ elif _pick_variant -r variant gnu=GNU unix --version; then
   aopts=( )
   (( $#words > 2 )) && ign='!'
   args+=(
+    '--debug[annotate program execution]'
     '--follow-symlinks[follow symlinks when processing in place]'
     '(-i --in-place -s --separate)'{-i-,--in-place=-}$inplace
     '(-c --copy)'{-c,--copy}'[copy instead of rename when shuffling files in in-place mode]'
diff --git a/Completion/Unix/Command/_sqlite b/Completion/Unix/Command/_sqlite
index 4604fb40c..924b80926 100644
--- a/Completion/Unix/Command/_sqlite
+++ b/Completion/Unix/Command/_sqlite
@@ -49,6 +49,7 @@ options+=(
   '(-*batch -*interactive)'$^dashes'-batch[force batch I/O]'
   '(-*batch -*interactive)'$^dashes'-interactive[force interactive I/O]'
   $^dashes'-lookaside[specify size and number of entries for lookaside memory]:size (bytes): :entries'
+  $^dashes'-memtrace[trace all memory allocations and deallocations'
   $^dashes'-mmap[set default mmap size]:size'
   $^dashes'-newline[set output row separator]:separator [\n]'
   $^dashes'-pagecache[specify size and number of slots for page cache memory]:size (bytes): :slots'
@@ -69,6 +70,7 @@ if [[ -n $words[(r)-A*] ]]; then
     + '(commands)' \
     '(-c --create)'{-c,--create}'[create a new archive]'
     '(-u --update)'{-u,--update}'[update or add files to an existing archive]'
+    '(-i --insert)'{-i,--insert}'[like -u but always add even if mtime unchanged]'
     '(-t --list)'{-t,--list}'[list contents of archive]'
     '(-x --extract)'{-x,--extract}'[extract files from archive]'
   )
diff --git a/Completion/Unix/Command/_ssh b/Completion/Unix/Command/_ssh
index 408f1d05d..f8f4e0091 100644
--- a/Completion/Unix/Command/_ssh
+++ b/Completion/Unix/Command/_ssh
@@ -97,7 +97,7 @@ _ssh () {
       '-M+[specify maximum number of signatures]:number' \
       '-s+[add keys provided by the PKCS#11 shared library]:library:_files -g "*.(so|dylib)(|.<->)(-.)"' \
       '-t+[set maximum lifetime for identity]:maximum lifetime (in seconds or time format):' \
-      "-T[test usability of identity files' private keys]" \
+      "-T[test usability of identity files' private keys]:*:public key file:_files -g '*.pub(-.)'" \
       '-v[verbose mode]' \
       '-q[be quiet after a successful operation]' \
       '-X[unlock the agent]' \
diff --git a/Completion/Unix/Command/_tiff b/Completion/Unix/Command/_tiff
index 128aeb0c0..da55b541c 100644
--- a/Completion/Unix/Command/_tiff
+++ b/Completion/Unix/Command/_tiff
@@ -249,7 +249,7 @@ if [[ -n "$state" ]]; then
       _message -e values "compression quality (0-100), or \`r' (output RGB)"
       ret=0
       ;;
-    lzw|lzma|zip)
+    lzw|lzma|zip|zstd|webp)
       _values 'LZW and deflate option' \
         '1[without differencing]' \
         '2[with differencing]' && ret=0
@@ -260,7 +260,7 @@ if [[ -n "$state" ]]; then
     while _tags; do
       while _next_label values expl 'compression scheme'; do
         compadd "$expl[@]" - none jbig g4 packbits sgilog && ret=0
-        compadd "$expl[@]" -qS: - lzw zip lzma jpeg g3 && ret=0
+        compadd "$expl[@]" -qS: - lzw zip lzma zstd webp jpeg g3 && ret=0
       done
       (( ret )) || return 0
     done
diff --git a/Completion/Unix/Command/_tree b/Completion/Unix/Command/_tree
index 4fd9aed44..595249126 100644
--- a/Completion/Unix/Command/_tree
+++ b/Completion/Unix/Command/_tree
@@ -49,6 +49,7 @@ _arguments -s -S \
   '(-n -C -X)-H[turn on HTML output]:base HREF' \
   '(-n -C -X)-T[title for HTML output]:title' \
   '(-n -C -X)--nolinks[turn off hyperlinks in HTML output]' \
+  '--fromfile[read paths from specified files]' \
   '(-)--version[version of tree]' \
   '(-)--help[verbose usage listing]' \
   '*:directory:_files -/'
diff --git a/Completion/Unix/Command/_wget b/Completion/Unix/Command/_wget
index 2a4e722d8..49c8e8b01 100644
--- a/Completion/Unix/Command/_wget
+++ b/Completion/Unix/Command/_wget
@@ -16,6 +16,7 @@ _arguments -C -s \
   '*-n+[turn off flags]:flags:->noflags' \
   '--report-speed=:type:(bits)' \
   '(--input-file -i)'{--input-file=,-i+}'[specify input file]:file containing URLs:_files' \
+  '--input-metalink=[download files covered in local Metalink file]:file:_files' \
   '(--force-html -F)'{--force-html,-F}'[treat input file as html]' \
   '(--base -B)'{--base=,-B+}'[prepend URL to relative links]:base URL:_urls' \
   '--config=[specify config file]:config file:_files' \
@@ -57,7 +58,11 @@ _arguments -C -s \
   '--local-encoding=[specify local encoding for IRIs]:encoding' \
   '--remote-encoding=[specify default remote encoding]:encoding' \
   '--unlink[remove file before clobber]' \
-  '--no-xattr[turn off storage of metadata in extended file attributes]' \
+  '--keep-badhash[Keep files with checksum mismatch (append .badhash)]' \
+  '--metalink-index=[metalink application/metalink4+xml metaurl ordinal]:number' \
+  '--metalink-over-http[use Metalink metadata from HTTP response headers]' \
+  '--preferred-location[preferred location for Metalink resources]' \
+  '--xattr[turn on storage of metadata in extended file attributes]' \
   '(-nd --no-directories)'{-nd,--no-directories}"[don't create directories]" \
   '(--force-directories -x)'{--force-directories,-x}'[force creation of directories]' \
   '(-nH --no-host-directories)'{-nH,--no-host-directories}"[don't create host directories]" \
@@ -102,7 +107,7 @@ _arguments -C -s \
   "--ca-directory=[specify dir where hash list of CA's are stored]:directory:_directories" \
   '--crl-file=[specify file with bundle of CRLs]:file:_files' \
   '--pinnedpubkey=:file:_files' \
-  '--random-file[specify file with random data for seeding generator]:file:_files' \
+  '!--random-file=:file:_files' \
   '--egd-file=[specify filename of EGD socket]:file:_files' \
   '--ciphers=[set the priority string (GnuTLS) or cipher list string (OpenSSL) directly]:string' \
   '--no-hsts[disable HSTS]' \
diff --git a/Completion/X/Command/_x_utils b/Completion/X/Command/_x_utils
index 13c5572af..4b98ec48e 100644
--- a/Completion/X/Command/_x_utils
+++ b/Completion/X/Command/_x_utils
@@ -150,9 +150,10 @@ xon)
   ;;
 xsetroot)
   _x_arguments \
-    -{help,def,gray,grey,rv} \
+    -{help,version,def,default,gray,grey,rv,reverse} \
     '-cursor:cursor file:_files -g \*.\(\#i\)\(xbm\|curs\(\|or\)\):mask file:_files -g \*.\(\#i\)\(xbm\|curs\(\|or\)\|mask\)\(-.\)' \
     '-cursor_name:cursor name:_x_cursor' \
+    '-xcf:cursor file:_files:size' \
     '-bitmap:bitmap file:_files -g \*.\(\#i\)xbm\(-.\)' \
     '-mod:x grid distance (1-16): :y grid distance (1-16)' \
     '-fg:foreground color:_x_color' \


^ permalink raw reply	[relevance 3%]

* Re: Issue with string slices
  @ 2019-06-18 18:12  3%       ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2019-06-18 18:12 UTC (permalink / raw)
  To: Roman Perepelitsa
  Cc: Peter Stephenson, Zsh hackers list, joão marcos pereira bezerra

2019-06-17 11:05:32 +0200, Roman Perepelitsa:
> To print $something as is, use one of these forms:
> 
>   echo -nE - "$something"
>   print -nr -- "$something"
>   printf '%s' "$something"
> 
> The first two are ZSH specific, the last is portable. ZSH also allows
> you to omit quotes here unless SH_WORD_SPLIT option is set.
[...]

The second one comes from ksh, it's not zsh-specific and
predates POSIX, let alone POSIX's printf, but nowadays is not as
portable as printf.

If you want to target zsh and ksh only, "print" may be more
desirable as there are still some pdksh derivatives where printf
is not built-in.

"unless SH_WORD_SPLIT *or GLOB_SUBST* is set"

-- 
Stephane

^ permalink raw reply	[relevance 3%]

* $~var and backslash
@ 2019-06-18 20:34  3% Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2019-06-18 20:34 UTC (permalink / raw)
  To: Zsh hackers list

There's currently a discussion on the POSIX mailing list around
the meaning of \ in shell wildcard patterns (beside its meaning
as a quoting operator).

See
http://austingroupbugs.net/view.php?id=1234
and the discussion that follows at
https://www.mail-archive.com/austin-group-l@opengroup.org/msg03630.html
(beware it's long and still ongoing).

That's just for context, no need to read the whole discussion
to understand what I'm saying below.

zsh does seem to be doing some level of processing of backslash
when it comes to \ and pattern matching and globbing, it's not
much worse than several other shells, but I have noted a few
problems.

1. (and the main one) it's completely undocumented (and it's the
same in all the other shells that somehow treat \ as a wildcard
quoting operator).

That:

pattern='\*' string='*' zsh -c '[[ $string = $~pattern ]] && echo yes'

should output "yes" and not

pattern='\*' zsh -c '[[ $pattern = $~pattern ]] && echo yes'

is AFAICT not documented at all.

2. I'm not sure I can find a logic behind when \ is understood
as a quoting operator:

string='\x' pattern='\x' matches
string='\+' pattern='\+' matches

But:

string='?' pattern='\?' matches
string='*' pattern='\*' matches
string='\' pattern='\\' matches

ok, those are wildcard operators, but:

string='\-' pattern='\-' doesn't match (but matches on string='-')
is it because - is special in <1-3> or in [a-b], but then:
string='\' pattern='[a\-a]' matches and also on
string='-' pattern='[a\-a]'
(so \ has escaped - but has not been removed?)

Same for ^ and !.

string='|' pattern='\|' matches but not with shglob. Yet
string='~' pattern='\~' matches both with and without shglob.

It doesn't make much sense to me.

One can always use [|] pr [~] instead of \ (which is also the
most portable in other shells), so it's not very critical.

-- 
Stephane

^ permalink raw reply	[relevance 3%]

* [PATCH] posix_builtins: allow exporting a readonly
  2018-04-18 19:58  4% [PATCH] posix_builtins: allow exporting a reaonly Martijn Dekker
  2018-04-18 20:07  0% ` Bart Schaefer
@ 2019-06-20 18:47  0% ` Martijn Dekker
  2019-06-21  8:59  0%   ` Peter Stephenson
  1 sibling, 1 reply; 200+ results
From: Martijn Dekker @ 2019-06-20 18:47 UTC (permalink / raw)
  To: zsh-workers

Op 18-04-18 om 20:58 schreef Martijn Dekker:
> POSIX_BUILTINS incorrectly prohibits exporting a readonly variable. All 
> other POSIX shells allow this and there is nothing in the POSIX text[*] 
> that says it's not allowed. The attached patch fixes this.

This seems to have been forgotten about, so I'm trying again.

To recap:

$ zsh -c 'readonly foo=123; export foo'			# OK
$ zsh --emulate sh -c 'readonly foo=123; export foo'	# BUG
zsh:1: read-only variable: foo

I had another look at it and I think this patch (attached) should be 
even more straightforward. It simply checks if we are running the 
'export' command and then reverts to normal behaviour instead of the 
special-casing for POSIXBUILTINS which is incorrect for this case.

Note that the patched code block is only executed with POSIXBUILTINS 
active so won't otherwise affect anything in any case.

Also, attempts to assign using 'export' still correctly error out with 
the patch, as they do on other shells:

$ zsh --emulate sh -c 'readonly foo=123; export foo=123'
zsh:1: read-only variable: foo

Thanks,

- M.

-- 
modernish -- harness the shell
https://github.com/modernish/modernish

^ permalink raw reply	[relevance 0%]

* Re: [PATCH] posix_builtins: allow exporting a readonly
  2019-06-20 18:47  0% ` [PATCH] posix_builtins: allow exporting a readonly Martijn Dekker
@ 2019-06-21  8:59  0%   ` Peter Stephenson
  2019-06-22 11:54  0%     ` Martijn Dekker
  0 siblings, 1 reply; 200+ results
From: Peter Stephenson @ 2019-06-21  8:59 UTC (permalink / raw)
  To: zsh-workers

On Thu, 2019-06-20 at 19:47 +0100, Martijn Dekker wrote:
> Op 18-04-18 om 20:58 schreef Martijn Dekker:
> > 
> > POSIX_BUILTINS incorrectly prohibits exporting a readonly variable. All 
> > other POSIX shells allow this and there is nothing in the POSIX text[*] 
> > that says it's not allowed. The attached patch fixes this.
> This seems to have been forgotten about, so I'm trying again.
> 
> To recap:
> 
> $ zsh -c 'readonly foo=123; export foo'			# OK
> $ zsh --emulate sh -c 'readonly foo=123; export foo'	# BUG
> zsh:1: read-only variable: foo
> 
> I had another look at it and I think this patch (attached) should be 
> even more straightforward.

This sounds fine but I don't think there is an attachment.

pws


^ permalink raw reply	[relevance 0%]

* Re: [PATCH] posix_builtins: allow exporting a readonly
  2019-06-21  8:59  0%   ` Peter Stephenson
@ 2019-06-22 11:54  0%     ` Martijn Dekker
  0 siblings, 0 replies; 200+ results
From: Martijn Dekker @ 2019-06-22 11:54 UTC (permalink / raw)
  To: zsh-workers

[-- Attachment #1: Type: text/plain, Size: 817 bytes --]

Op 21-06-19 om 09:59 schreef Peter Stephenson:
> On Thu, 2019-06-20 at 19:47 +0100, Martijn Dekker wrote:
>> Op 18-04-18 om 20:58 schreef Martijn Dekker:
>>>   
>>> POSIX_BUILTINS incorrectly prohibits exporting a readonly variable. All
>>> other POSIX shells allow this and there is nothing in the POSIX text[*]
>>> that says it's not allowed. The attached patch fixes this.
>> This seems to have been forgotten about, so I'm trying again.
>>   
>> To recap:
>>   
>> $ zsh -c 'readonly foo=123; export foo'			# OK
>> $ zsh --emulate sh -c 'readonly foo=123; export foo'	# BUG
>> zsh:1: read-only variable: foo
>>   
>> I had another look at it and I think this patch (attached) should be
>> even more straightforward.
> 
> This sounds fine but I don't think there is an attachment.

Woops. :-/

Here it is...

- M.

[-- Attachment #2: BUG_NOEXPRO.patch --]
[-- Type: text/plain, Size: 459 bytes --]

diff --git a/Src/builtin.c b/Src/builtin.c
index 2453f82c0..e863cc4bb 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2171,7 +2171,7 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 	    !ASG_VALUEP(asg))
 	    on |= PM_UNSET;
 	else if (usepm && (pm->node.flags & PM_READONLY) &&
-		 !(on & PM_READONLY)) {
+		 !(on & PM_READONLY) && func != BIN_EXPORT) {
 	    zerr("read-only variable: %s", pm->node.nam);
 	    return NULL;
 	}

^ permalink raw reply	[relevance 0%]

* */ globs and non-searchable directories
@ 2019-06-28  9:36  3% Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2019-06-28  9:36 UTC (permalink / raw)
  To: Zsh hackers list

Not really a bug as such but a difference from other shells:

$ mkdir -m a=x searchable
$ mkdir -m a=r readable

$ echo *(/)
readable searchable
$ echo */
searchable/

All the other shells I tried output:

$ echo */
readable/ searchable/

In any case, all the shells I tried agree with zsh on:

$ echo */.
searchable/.

(though note the bug in current versions of bash5 as discussed
on the POSIX ML:

$ bash5 -c 'echo */.'
searchable/.
$ bash5 -c 'echo */"."'
readable/.
)

-- 
Stephane

^ permalink raw reply	[relevance 3%]

* [PATCH] getopts: fix returned value on missing option argument
@ 2019-06-28 22:17  3% Martijn Dekker
  0 siblings, 0 replies; 200+ results
From: Martijn Dekker @ 2019-06-28 22:17 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 632 bytes --]

Problem:

$ zsh -c 'while getopts x: opt; do echo "$opt"; done' dummy -x
zsh:1: argument expected after -x option
:

Expected output: the ':' at the end should be a '?'. The opt variable 
should only be set to ':' in "quiet" mode, i.e. if the option string 
starts with a ':' (so in this case would be ':x:').

Ref.: http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html
(the two bullet points following "If an option-argument is missing:").

The attached patch fixes this to match POSIX and the behaviour of every 
other shell.

- Martijn

-- 
modernish -- harness the shell
https://github.com/modernish/modernish

[-- Attachment #2: BUG_GETOPTSMA.patch --]
[-- Type: text/plain, Size: 1799 bytes --]

diff --git a/Src/builtin.c b/Src/builtin.c
index e863cc4bb..9b9e76c77 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -5511,14 +5511,12 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
     /* check for legality */
     if(opch == ':' || !(p = memchr(optstr, opch, lenoptstr))) {
 	p = "?";
-    err:
 	zsfree(zoptarg);
 	setsparam(var, ztrdup(p));
 	if(quiet) {
 	    zoptarg = metafy(optbuf, lenoptbuf, META_DUP);
 	} else {
-	    zwarn(*p == '?' ? "bad option: %c%c" :
-		  "argument expected after %c%c option",
+	    zwarn("bad option: %c%c",
 		  "?-+"[lenoptbuf], opch);
 	    zoptarg=ztrdup("");
 	}
@@ -5529,8 +5527,17 @@ bin_getopts(UNUSED(char *name), char **argv, UNUSED(Options ops), UNUSED(int fun
     if(p[1] == ':') {
 	if(optcind == lenstr) {
 	    if(!args[zoptind]) {
-		p = ":";
-		goto err;
+		zsfree(zoptarg);
+		if(quiet) {
+		    setsparam(var, ztrdup(":"));
+		    zoptarg = metafy(optbuf, lenoptbuf, META_DUP);
+		} else {
+		    setsparam(var, ztrdup("?"));
+		    zoptarg = ztrdup("");
+		    zwarn("argument expected after %c%c option",
+			  "?-+"[lenoptbuf], opch);
+		}
+		return 0;
 	    }
 	    p = ztrdup(args[zoptind++]);
 	} else
diff --git a/Test/B10getopts.ztst b/Test/B10getopts.ztst
index 7eba5a4b1..72c9e209e 100644
--- a/Test/B10getopts.ztst
+++ b/Test/B10getopts.ztst
@@ -79,3 +79,20 @@
   test_getopts +x
 1:one illegal option, + variant
 >test_getopts:3: bad option: +x
+
+  set -- -x
+  OPTIND=1
+  while getopts x: opt; do
+    echo "$opt,${OPTARG:-Empty}"
+  done
+0:missing option-argument (error message mode)
+>?,Empty
+?(eval):3: argument expected after -x option
+
+  set -- -x
+  OPTIND=1
+  while getopts :x: opt; do
+    echo "$opt,${OPTARG:-Empty}"
+  done
+0:missing option-argument (quiet mode)
+>:,x

^ permalink raw reply	[relevance 3%]

* Re: [Feature Request] Adding option to support triple quotes
  @ 2019-08-14  9:54  3% ` Stephane Chazelas
  2019-08-15 14:28  0%   ` Aryn Starr
  0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2019-08-14  9:54 UTC (permalink / raw)
  To: Aryn Starr; +Cc: zsh-workers

2019-08-13 10:36:53 +0430, Aryn Starr:
> Adding an option to zsh to support triple quotes
> (TRIPLE_QUOTES) would make possible much more readable code.
[...]

We could also implement mksh's "$(<<'EOF'
arbitrary text that mustn't end in empty lines
EOF
)"

That already works in zsh, but is not optimised like in mksh as
it does "$(cat <<'EOF'
arbitrary text that mustn't end in empty lines
EOF
)" while mksh treats it specially as effectively a kind of
quotes.

mksh also supports $(<<'EOF'
foo
EOF)

(though that's probably not POSIX).

See also perl's q(...) and qq(...) that are more compatible with
the shell syntax than those python '''/""".

See:

$ a="'"; print -r ${(qq)a}
''\'''

-- 
Stephane





^ permalink raw reply	[relevance 3%]

* Re: [Feature Request] Adding option to support triple quotes
  2019-08-14  9:54  3% ` Stephane Chazelas
@ 2019-08-15 14:28  0%   ` Aryn Starr
  0 siblings, 0 replies; 200+ results
From: Aryn Starr @ 2019-08-15 14:28 UTC (permalink / raw)
  To: Stephane Chazelas; +Cc: zsh-workers

That EOF solution is good if there is a lot of text, but for short snippets, it’s too many characters to type. Completely against the shell’s spirit of arcane brevity. :))
I also thought of a (kind of) new application for the triple quotes. I think it’ll enable a lot more functions like this:
cc() python -c "from math import *; print($*);"
Since we’ll be able to easily write the target language’s code in our shell, with minimal typing.

> On Aug 14, 2019, at 2:24 PM, Stephane Chazelas <stephane.chazelas@gmail.com> wrote:
> 
> 2019-08-13 10:36:53 +0430, Aryn Starr:
>> Adding an option to zsh to support triple quotes
>> (TRIPLE_QUOTES) would make possible much more readable code.
> [...]
> 
> We could also implement mksh's "$(<<'EOF'
> arbitrary text that mustn't end in empty lines
> EOF
> )"
> 
> That already works in zsh, but is not optimised like in mksh as
> it does "$(cat <<'EOF'
> arbitrary text that mustn't end in empty lines
> EOF
> )" while mksh treats it specially as effectively a kind of
> quotes.
> 
> mksh also supports $(<<'EOF'
> foo
> EOF)
> 
> (though that's probably not POSIX).
> 
> See also perl's q(...) and qq(...) that are more compatible with
> the shell syntax than those python '''/""".
> 
> See:
> 
> $ a="'"; print -r ${(qq)a}
> ''\'''
> 
> -- 
> Stephane
> 
> 
> 
> 


^ permalink raw reply	[relevance 0%]

* Re: [bug] Problem with functions -s (string arg) math function & specific input
  @ 2019-08-19  7:01  3% ` Stephane Chazelas
  2019-08-19  9:53  0%   ` Sebastian Gniazdowski
  0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2019-08-19  7:01 UTC (permalink / raw)
  To: Sebastian Gniazdowski; +Cc: Zsh hackers list

2019-08-19 03:00:24 +0200, Sebastian Gniazdowski:
> Hello,
> the contents of the string argument seems to be evaluated in some way
> (while it shouldn't). Reproducing snippet:
> 
> mtest() { print ${#1}; }
> functions -s -M mtest 1 1 mtest
> input=') &>/dev/null'
> (( mtest($input) ))
> 
> Output:
> 0
> zsh: bad math expression: operand expected at `>/dev/null...'
[...]

Expansions are done first, and then the result evaluated as
an arithmetic expression. That's a POSIX requirement (for
$((...)))

So here, you're evaluating the:

mtest() &>/dev/null

arithmetic expression.

Just like in:

a='1] + b[2'
echo $((b[$a]))

you're evaluating the b[1] + b[2] arithmetic expression.

-- 
Stephane

^ permalink raw reply	[relevance 3%]

* Re: [bug] Problem with functions -s (string arg) math function & specific input
  2019-08-19  7:01  3% ` Stephane Chazelas
@ 2019-08-19  9:53  0%   ` Sebastian Gniazdowski
  0 siblings, 0 replies; 200+ results
From: Sebastian Gniazdowski @ 2019-08-19  9:53 UTC (permalink / raw)
  To: Sebastian Gniazdowski, Zsh hackers list

On Mon, 19 Aug 2019 at 09:01, Stephane Chazelas
<stephane.chazelas@gmail.com> wrote:
> 2019-08-19 03:00:24 +0200, Sebastian Gniazdowski:
> > Hello,
> > the contents of the string argument seems to be evaluated in some way
> > (while it shouldn't). Reproducing snippet:
> >
> > mtest() { print ${#1}; }
> > functions -s -M mtest 1 1 mtest
> > input=') &>/dev/null'
> > (( mtest($input) ))
> >
> > Output:
> > 0
> > zsh: bad math expression: operand expected at `>/dev/null...'
> [...]
>
> Expansions are done first, and then the result evaluated as
> an arithmetic expression. That's a POSIX requirement (for
> $((...)))
>
> So here, you're evaluating the:
>
> mtest() &>/dev/null

So it seems that ${MATCH//\)/\\\)} should suffice to fix this problem,
as it seems that once a string is within parens (correctly), further
expansions are put on hold for the -s functions.
-- 
Sebastian Gniazdowski
News: https://twitter.com/ZdharmaI
IRC: https://kiwiirc.com/client/chat.freenode.net:+6697/#zplugin
Blog: http://zdharma.org

^ permalink raw reply	[relevance 0%]

* RFC: &&/|| vs. & operator precedence
@ 2019-08-24 20:16  5% Martijn Dekker
  0 siblings, 0 replies; 200+ results
From: Martijn Dekker @ 2019-08-24 20:16 UTC (permalink / raw)
  To: Zsh hackers list

Sorry if this has been discussed before -- it's hard to search for '&' 
in the archives.

On all other shells,
	true && false || foo &
launches 'true && false || foo' as a background job. On zsh (including 
sh mode), only 'foo' is the background job.

IOW, the '&' operator on other POSIX shells takes an entire AND-OR 
(&&/||) list as the background job, whereas on zsh, only the last 
command is taken as the background job.

POSIX says in 2.9.3:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_03
| A list is a sequence of one or more AND-OR lists separated by the
| operators ';' and '&'.

but later on in 2.9.3.1:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_03_02
| If a command is terminated by the control operator <ampersand> ( '&'
| ), the shell shall execute the command asynchronously in a subshell.

So that is quite ambiguous. The first suggests that '&' should act like 
most POSIX shells do, the second suggests it should act like zsh does.

This ambiguity is basically the subject of an Austin Group bug opened by 
Stéphane Chazelas:
http://austingroupbugs.net/view.php?id=1254

He is proposing that POSIX should prescribe the behaviour of the 
majority, which would make zsh non-compliant as of the next POSIX edition.

An alternative might be to make this unspecified instead, so that both 
zsh and other shells are compliant as is, and cross-platform scripters 
would be expected to use explicit braces where necessary (as they 
already have to do now).

So, in anticipation of this being resolved one way or another in POSIX, 
it might be worth discussing what (if anything) should change in zsh. I 
think the opinion of the zsh maintainers matters here.

Would it be feasible to make zsh act like other POSIX shells in sh 
emulation mode only? I could imagine that being difficult as this 
touches on shell grammar, not builtins.

If not, would it be acceptable to change the precedence of these 
operators to match other POSIX shells in zsh as a whole? How many old 
zsh scripts could that break?

- Martijn

-- 
modernish -- harness the shell
https://github.com/modernish/modernish

^ permalink raw reply	[relevance 5%]

* Re: arithmetic expression from outside
  @ 2019-09-05 19:49  3%   ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2019-09-05 19:49 UTC (permalink / raw)
  To: zsh-workers

2019-09-05 11:22:53 -0700, Bart Schaefer:
[...]
> % N='sum[$(echo Hello, world!>&2)]' bash -c 'echo $((N))'
> Hello, world!
> 0
> 
> Zsh at least does not do that with parameters from the environment,
> although I'm not exactly sure what prevents it.

It does, but the variable has to be an array:

$ N='psvar[$(echo Hello, world!>&2)]' zsh -c 'echo $((N))'
Hello, world!
zsh:1: bad math expression: empty string

See also
https://unix.stackexchange.com/questions/172103/security-implications-of-using-unsanitized-data-in-shell-arithmetic-evaluation

That's regularly reported as a security vulnerability or at
least a misfeature that causes security vulnerabilities (to bash
and zsh at least; first time I heard about it was from Oliver
Kiddle here shortly after shellshock) and I agree it is.

It would be nice if something could be done about it, but I'm
not sure there's an easy solution other than redesigning a new
arithmetic expression parsing from scratch.

POSIX gets in the way because it requires $(($1)) to be done in
two steps: first expansion of $1 and then evaluation of the
expression resulting from that expansion (so with a value like
PATH=7734, you get a nasty side effect even with dash or other
shells that don't have extensions over the standard).

I'm not sure we can do anything safe without coming up with a
completely new, incompatible and pretty limited syntax.

-- 
Stephane


^ permalink raw reply	[relevance 3%]

* PATCH: completion of SELinux contexts
@ 2019-09-26 22:10  7% Oliver Kiddle
  0 siblings, 0 replies; 200+ results
From: Oliver Kiddle @ 2019-09-26 22:10 UTC (permalink / raw)
  To: Zsh workers

There are quite a number of commands that have an option that takes an
SELinux context as it's argument. Typically we've not handled this in
completion functions. However it isn't too hard to do a reasonable job
of completing them (apart from the level component which I don't really
understand). The following adds helper functions for contexts, users,
roles and types and a completion function for chcon (which comes from
util-linux). And modifies completions for cp, find, install, mkdir,
mknod and sudo to use the new helpers.

Oliver

diff --git a/Completion/Linux/Command/_chcon b/Completion/Linux/Command/_chcon
new file mode 100644
index 000000000..2d523f287
--- /dev/null
+++ b/Completion/Linux/Command/_chcon
@@ -0,0 +1,24 @@
+#compdef chcon
+
+local ign
+
+(( $#words > 2 )) && ign='!'
+_arguments -C -s -S \
+  '(-h --no-dereference)--dereference[dereference symlinks]' \
+  '(-h --no-dereference --dereference)'{-h,--no-dereference}'[operate on symlinks themselves]' \
+  '(1 -u --user -r --role  -l --range -t --type)--reference=[copy security context of specified file]:file:_files' \
+  '(1 --reference -u --user)'{-u+,--user=}'[set user in the target security context]: :_selinux_users' \
+  '(1 --reference -r --role)'{-r+,--role=}'[set role in the target security context]: :_selinux_roles' \
+  '(1 --reference -t --type)'{-t+,--type=}'[set type in the target security context]: :_selinux_types' \
+  '(1 --reference -l --range)'{-l+,--range=}'[set range in the target security context]:selinux range' \
+  '(--recursive -R)'{--recursive,-R}'[recurse subdirectories]' \
+  '(-v --verbose)'{-v,--verbose}'[output a diagnostic for every file processed]' \
+  '(-H -L -P)-H[follow symlinks on the command line]' \
+  '(-H -L -P)-L[follow all symlinks]' \
+  "(-H -L -P)-P[don't follow symlinks (default)]" \
+  '!(--preserve-root)--no-preserve-root' \
+  "--preserve-root[fail to operate recursively on '/']" \
+  '(--reference -u --user -r --role  -l --range -t --type)1:security context:_selinux_contexts' \
+  "${ign}--help[display help information]" \
+  "${ign}--version[display version information]" \
+  '*:file:_files'
diff --git a/Completion/Linux/Type/_selinux_contexts b/Completion/Linux/Type/_selinux_contexts
new file mode 100644
index 000000000..4c2cf4288
--- /dev/null
+++ b/Completion/Linux/Type/_selinux_contexts
@@ -0,0 +1,14 @@
+#autoload
+
+local -a parts suf
+
+parts=( users roles types )
+while compset -P 1 '*:' && (( $+parts[1] )) ; do
+  shift parts
+done
+if (( $+parts[1] )); then
+  compset -S ':*' || suf=( -S : )
+  _selinux_$parts[1] $suf
+else
+  _message -e selinux-ranges 'selinux range'
+fi
diff --git a/Completion/Linux/Type/_selinux_roles b/Completion/Linux/Type/_selinux_roles
new file mode 100644
index 000000000..92b4c36cb
--- /dev/null
+++ b/Completion/Linux/Type/_selinux_roles
@@ -0,0 +1,7 @@
+#autoload
+
+local -a seroles expl
+
+seroles=( ${(f)"$(_call_program selinux-roles seinfo --flat -r)"} )
+_description selinux-roles expl "selinux role"
+compadd "$@" "$expl[@]" -a seroles
diff --git a/Completion/Linux/Type/_selinux_types b/Completion/Linux/Type/_selinux_types
new file mode 100644
index 000000000..ef31f45d2
--- /dev/null
+++ b/Completion/Linux/Type/_selinux_types
@@ -0,0 +1,7 @@
+#autoload
+
+local -a setypes expl
+
+setypes=( ${(f)"$(_call_program selinux-types seinfo --flat -t)"} )
+_description selinux-types expl "selinux type"
+compadd "$@" "$expl[@]" -a setypes
diff --git a/Completion/Linux/Type/_selinux_users b/Completion/Linux/Type/_selinux_users
new file mode 100644
index 000000000..f046c92cf
--- /dev/null
+++ b/Completion/Linux/Type/_selinux_users
@@ -0,0 +1,8 @@
+#autoload
+
+local -a seusers expl
+
+seusers=( ${(f)"$(_call_program selinux-users seinfo --flat -u)"} )
+(( $#seusers )) || seusers=( guest_u root staff_u sysadm_u system_u unconfined_u user_u )
+_description selinux-users expl "selinux user"
+compadd "$@" "$expl[@]" -a seusers
diff --git a/Completion/Unix/Command/_cp b/Completion/Unix/Command/_cp
index ae448213a..f7411055b 100644
--- a/Completion/Unix/Command/_cp
+++ b/Completion/Unix/Command/_cp
@@ -32,7 +32,7 @@ if _pick_variant gnu=GNU unix --version; then
     '(-v --verbose)'{-v,--verbose}'[explain what is being done]' \
     '(-x --one-file-system)'{-x,--one-file-system}'[stay on this file system]' \
     '(--context)-Z[set destination SELinux security context]' \
-    '(-Z)--context=-[set destination SELinux security context]::context' \
+    '(-Z)--context=-[set destination SELinux security context]:: :_selinux_contexts' \
     '(- *)--help' '(- *)--version' \
     '*:file or directory:_files'
 else
diff --git a/Completion/Unix/Command/_find b/Completion/Unix/Command/_find
index 3b9150b17..916fcf2e6 100644
--- a/Completion/Unix/Command/_find
+++ b/Completion/Unix/Command/_find
@@ -98,7 +98,7 @@ case $variant in
     args+=(
       '(- *)-help' '(-)--help'
       '(- *)-version' '(-)--version'
-      '-D[print diagnostics]:debug option:(help tree search stat rates opt exec)'
+      '-D[print diagnostics]:debug option:(exec opt rates search stat time tree all help)'
       '-O+[enable query optimisation]:level:(1 2 3)'
       '*-daystart'
       '-regextype:regexp syntax:(help findutils-default awk egrep ed emacs gnu-awk grep posix-awk posix-basic posix-egrep posix-extended posix-minimal-basic sed)'
@@ -116,7 +116,7 @@ case $variant in
       '*-fprintf:output file:_files:output format'
       '*-printf:output format'
     )
-    [[ $OSTYPE = linux-gnu ]] && args+=( '*-context:SELinux context' )
+    [[ $OSTYPE = linux-gnu ]] && args+=( '*-context:SELinux context (glob pattern):_selinux_contexts' )
   ;;
 esac
 
diff --git a/Completion/Unix/Command/_install b/Completion/Unix/Command/_install
index 60b0f6153..5ad84645e 100644
--- a/Completion/Unix/Command/_install
+++ b/Completion/Unix/Command/_install
@@ -25,7 +25,7 @@ if _pick_variant gnu='Free Soft' unix --version; then
   args+=(
     $common_args
     '(-b --backup)--backup=[create backup; optionally specify method]:: :->controls'
-    "${lx}--context=[like -Z, or specify SELinux security context to set]::SELinux security context"
+    "${lx}--context=-[like -Z, or specify SELinux security context to set]::SELinux security context:_selinux_contexts"
     '-D[create all leading destination path components]'
     '(: -)--help[display help information]'
     "${lx}--preserve-context[preserve SELinux security context]"
diff --git a/Completion/Unix/Command/_mkdir b/Completion/Unix/Command/_mkdir
index 0ae6be14b..4cd6bda32 100644
--- a/Completion/Unix/Command/_mkdir
+++ b/Completion/Unix/Command/_mkdir
@@ -22,7 +22,8 @@ case $variant in
     aopts=()
     if [[ $OSTYPE == linux* ]]; then
       args+=(
-        '(-Z --context)'{-Z,--context=}'[set SELinux context]:SELinux context'
+        '(--context)-Z[set SELinux context]'
+        '(-Z)--context=-[set SELinux context]::SELinux context:_selinux_contexts'
       )
     fi
     args+=(
diff --git a/Completion/Unix/Command/_mkfifo b/Completion/Unix/Command/_mkfifo
index 4f1d8c87e..a055e4a1c 100644
--- a/Completion/Unix/Command/_mkfifo
+++ b/Completion/Unix/Command/_mkfifo
@@ -10,7 +10,7 @@ if _pick_variant gnu='Free Soft' unix --version; then
   )
   [[ $OSTYPE == linux* ]] && args+=(
     '(--context)-Z[set SELinux security context to default]'
-    '(-Z)--context=-[like -Z, or specify SELinux security context]:SELinux security context'
+    '(-Z)--context=-[like -Z, or specify SELinux security context]::SELinux security context:_selinux_contexts'
   )
 else
   aopts=( -A '-*' )
diff --git a/Completion/Unix/Command/_mknod b/Completion/Unix/Command/_mknod
index 902f49b9f..8f07328db 100644
--- a/Completion/Unix/Command/_mknod
+++ b/Completion/Unix/Command/_mknod
@@ -22,7 +22,7 @@ if _pick_variant gnu='Free Soft' $OSTYPE --version; then
   )
   [[ $OSTYPE == linux* ]] && args+=(
     '(--context)-Z[set SELinux security context to default]'
-    '(-Z)--context=-[like -Z, or specify SELinux security context]:SELinux security context'
+    '(-Z)--context=-[like -Z, or specify SELinux security context]::SELinux security context:_selinux_contexts'
   )
 else
   aopts=( -A '-*' )
diff --git a/Completion/Unix/Command/_sudo b/Completion/Unix/Command/_sudo
index 10fa2e82e..41e32cbae 100644
--- a/Completion/Unix/Command/_sudo
+++ b/Completion/Unix/Command/_sudo
@@ -23,9 +23,9 @@ args=(
   \*{-l,--list}"[list user's privileges or check a specific command]"
   '(-n --non-interactive)'{-n,--non-interactive}'[non-interactive mode, no prompts are used]'
   '(-p --prompt)'{-p+,--prompt=}'[use the specified password prompt]:prompt'
-  '(-r --role)'{-r+,--role=}'[create SELinux security context with specified role]:role'
+  '(-r --role)'{-r+,--role=}'[create SELinux security context with specified role]: :_selinux_roles'
   '(-S --stdin)'{-S,--stdin}'[read password from standard input]'
-  '(-t --type)'{-t+,--type=}'[create SELinux security context with specified type]:type'
+  '(-t --type)'{-t+,--type=}'[create SELinux security context with specified type]: :_selinux_types'
   '(-T --command-timeout)'{-T+,--command-timeout=}'[terminate command after specified time limit]:timeout'
   '(-U --other-user)'{-U+,--other-user=}'[in list mode, display privileges for user]:user:_users'
   '(-u --user)'{-u+,--user=}'[run command (or edit file) as specified user]:user:_users'

^ permalink raw reply	[relevance 7%]

* [PATCH] POSIX compliant nice error checking
@ 2019-10-16 16:26  8% ` _RuRo_ (Андрей Стоцкий)
  2019-10-16 16:43  5%   ` Peter Stephenson
  0 siblings, 1 reply; 200+ results
From: _RuRo_ (Андрей Стоцкий) @ 2019-10-16 16:26 UTC (permalink / raw)
  To: zsh-workers

In `Src/exec.c`, the return value of nice(5) is checked, however according to
POSIX (http://man7.org/linux/man-pages/man2/nice.2.html), one should check errno
instead, since nice can return negative values even on success. Since nice(5)
increments the niceness by 5, the return value can be negative, if the original
niceness was less than -5. For example, with nice -6, you'll get the annoying
`zsh: nice(5) failed: success` error message:

```
> sudo renice -6 $$
17187 (process ID) old priority 0, new priority -6
> cat &
[1] 6200
zsh: nice(5) failed: success
[1]  + 6200 suspended (tty input)  cat
> kill %1
[1]  + 6200 terminated  cat
```

With nice -5 this doesn't happen:

```
> sudo renice -5 $$
17187 (process ID) old priority -6, new priority -5
> cat &
[1] 7559
[1]  + 7559 suspended (tty input)  cat
> kill %1
[1]  + 7559 terminated  cat
```

Checking errno instead of the return value fixes this.

---
 Src/exec.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/Src/exec.c b/Src/exec.c
index 042ba065a..08411ce28 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2762,9 +2762,12 @@ execcmd_fork(Estate state, int how, int type, Wordcode varspc,
     sigtrapped[SIGEXIT] = 0;
 #ifdef HAVE_NICE
     /* Check if we should run background jobs at a lower priority. */
-    if ((how & Z_ASYNC) && isset(BGNICE))
-	if (nice(5) < 0)
+    if ((how & Z_ASYNC) && isset(BGNICE)) {
+	errno = 0;
+	nice(5);
+	if (errno)
 	    zwarn("nice(5) failed: %e", errno);
+    }
 #endif /* HAVE_NICE */
 
     return 0;
-- 
2.23.0


^ permalink raw reply	[relevance 8%]

* Re: [PATCH] POSIX compliant nice error checking
  2019-10-16 16:26  8% ` [PATCH] POSIX compliant nice error checking _RuRo_ (Андрей Стоцкий)
@ 2019-10-16 16:43  5%   ` Peter Stephenson
  0 siblings, 0 replies; 200+ results
From: Peter Stephenson @ 2019-10-16 16:43 UTC (permalink / raw)
  To: zsh-workers

On Wed, 2019-10-16 at 19:26 +0300, _RuRo_ (Андрей Стоцкий) wrote:
> In `Src/exec.c`, the return value of nice(5) is checked, however
> according to POSIX (http://man7.org/linux/man-pages/man2/nice.2.html),
> one should check errno instead, since nice can return negative values
> even on success. Since nice(5) increments the niceness by 5, the
> return value can be negative, if the original niceness was less than
> -5. For example, with nice -6, you'll get the annoying `zsh: nice(5)
> failed: success` error message:

Thanks, I've committed that --- I suppose there's a vague possibility of
a problem with older systems that might not have set errno but that
doesn't seem worth worrying about.

pws

^ permalink raw reply	[relevance 5%]

* Segfault with terminal width <= 6
@ 2019-10-28 13:34  2% Roman Perepelitsa
  0 siblings, 0 replies; 200+ results
From: Roman Perepelitsa @ 2019-10-28 13:34 UTC (permalink / raw)
  To: Zsh hackers list

When terminal width is <= 6, there is memory corruption somewhere that
leads to segfault. It reproduces reliably on my machine with the
following sequence.

1. Resize your terminal to 6x6. Height doesn't matter but it's
important for width to be <= 6.

2. Type `PROMPT='' zsh -df`. The value of PROMPT doesn't matter. I'm
using empty propt so that my "screenshots" look the same as what you
would see if you attempted to reproduce this.

3. Press and hold `x` until you see `>` appearing on the first line.
It doesn't matter if you hold it longer than necessary.

    >....
    xxxxxx
    xxxxxx
    xxxxxx
    xxxxxx

4. Press and hold left arrow until `>` disappears. It doesn't matter
if you hold it longer than necessary.

    xxxxxx
    xxxxxx
    xxxxxx
    xxxxxx
    xxxxxx
    <....

5. At this point memory is corrupted and many actions can crash zsh.
The simplest is to press Ctrl+C.

    free(): invalid next size (fast)
    zsh: abort (core dumped)

Here's a backtrace:

    #0  __GI_raise (sig=sig@entry=6)
        at ../sysdeps/unix/sysv/linux/raise.c:51
    #1  0x00007f8dcba57801 in __GI_abort () at abort.c:79
    #2  0x00007f8dcbaa0897 in __libc_message (
        action=action@entry=do_abort,
        fmt=fmt@entry=0x7f8dcbbcdb9a "%s\n")
        at ../sysdeps/posix/libc_fatal.c:181
    #3  0x00007f8dcbaa790a in malloc_printerr (
        str=str@entry=0x7f8dcbbcf800 "free(): invalid next size
(fast)") at malloc.c:5350
    #4  0x00007f8dcbaaef60 in _int_free (have_lock=0,
        p=0x55f7fcc5f1b0, av=0x7f8dcbe02c40 <main_arena>)
        at malloc.c:4213
    #5  __GI___libc_free (mem=0x55f7fcc5f1c0) at malloc.c:3124
    #6  0x00007f8dca3ce6e3 in freechanges (p=0x55f7fcc5f270)
        at zle_utils.c:1452
    #7  0x00007f8dca3ce65f in freeundo () at zle_utils.c:1436
    #8  0x00007f8dca3ad564 in zleread (lp=0x55f7fbcace20 <prompt>,
        rp=0x0, flags=3, context=0,
        init=0x7f8dca3d75c0 "zle-line-init",
        finish=0x7f8dca3d75b0 "zle-line-finish") at zle_main.c:1371
    #9  0x00007f8dca3b052b in zle_main_entry (cmd=1,
        ap=0x7ffe7fd8f620) at zle_main.c:2119
    #10 0x000055f7fba0a83c in zleentry (cmd=1) at init.c:1605
    #11 0x000055f7fba0bb8d in inputline () at input.c:295
    #12 0x000055f7fba0b9d1 in ingetc () at input.c:228
    #13 0x000055f7fb9fd945 in ihgetc () at hist.c:408
    #14 0x000055f7fba15e99 in gettok () at lex.c:611
    #15 0x000055f7fba15576 in zshlex () at lex.c:275
    #16 0x000055f7fba3d3b0 in parse_event (endtok=37) at parse.c:581
    #17 0x000055f7fba0695e in loop (toplevel=1, justonce=0)
        at init.c:150
    #18 0x000055f7fba0ad38 in zsh_main (argc=2, argv=0x7ffe7fd8fae8)
        at init.c:1770
    #19 0x000055f7fb9bc0b7 in main (argc=2, argv=0x7ffe7fd8fae8)
        at ./main.c:93

If you do something different on step 5, it'll crash with a different
stack trace. All stack traces I've seen lead to __GI___libc_free.

This appears to be an old bug. zsh-4.3.17 crashes in the same manner.
I haven't tried it with an older version.

Roman.

^ permalink raw reply	[relevance 2%]

* [PATCH 0/1] Run final pipeline command in a subshell in sh mode
@ 2019-11-24 23:41  5% brian m. carlson
  2019-11-24 23:41  4% ` [PATCH 1/1] exec: run " brian m. carlson
  2019-12-07 19:23  0% ` [PATCH 0/1] Run " brian m. carlson
  0 siblings, 2 replies; 200+ results
From: brian m. carlson @ 2019-11-24 23:41 UTC (permalink / raw)
  To: zsh-workers

POSIX sh implementations run each command in a pipeline in a subshell,
although zsh (and AT&T ksh) do not: instead, they run the final command
in the main shell.

zsh is starting to be used in some cases as /bin/sh, such as on macOS
Catalina.  Whether this is a good idea or not, it makes sense to emulate
the POSIX behavior as much as possible when emulating sh, since that's
the least surprising behavior.  This patch does exactly that.

With this patch, using "zsh --emulate sh" passes all but one test of the
Git testsuite.  The remaining failure is due to zsh preserving NUL bytes
in the output of command substitutions, which is permitted by POSIX; I
will be sending a patch to fix that bug in the Git testsuite.

I will admit that some of the tests included look bizarre, such as
piping to an assignment, but I felt it was important to hit as many
cases as possible.

I'm not subscribed to the list, so please CC me with any comments, and
I'll try to address them promptly.  If you'd like me to include a
changelog entry, please say so, and I'm happy to include one.

brian m. carlson (1):
  exec: run final pipeline command in a subshell in sh mode

 Src/exec.c           | 10 ++++++----
 Test/B07emulate.ztst | 22 ++++++++++++++++++++++
 2 files changed, 28 insertions(+), 4 deletions(-)


^ permalink raw reply	[relevance 5%]

* [PATCH 1/1] exec: run final pipeline command in a subshell in sh mode
  2019-11-24 23:41  5% [PATCH 0/1] Run final pipeline command in a subshell in sh mode brian m. carlson
@ 2019-11-24 23:41  4% ` brian m. carlson
  2019-12-07 19:23  0% ` [PATCH 0/1] Run " brian m. carlson
  1 sibling, 0 replies; 200+ results
From: brian m. carlson @ 2019-11-24 23:41 UTC (permalink / raw)
  To: zsh-workers

zsh typically runs the final command in a pipeline in the main shell
instead of a subshell.  However, POSIX requires that all commands in a
pipeline run in a subshell, but permits zsh's behavior as an extension.

Since zsh may be used as /bin/sh in some cases (such as macOS Catalina),
it makes sense to have the POSIX behavior when emulating sh, so do that
by checking for being the final item of a multi-item pipeline and
creating a subshell in that case.
---
 Src/exec.c           | 10 ++++++----
 Test/B07emulate.ztst | 22 ++++++++++++++++++++++
 2 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/Src/exec.c b/Src/exec.c
index 6014ec9a5..66dc8db2d 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2861,11 +2861,13 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 	    pushnode(args, dupstring("fg"));
     }
 
-    if ((how & Z_ASYNC) || output) {
+    if ((how & Z_ASYNC) || output ||
+	(last1 == 2 && input && EMULATION(EMULATE_SH))) {
 	/*
-	 * If running in the background, or not the last command in a
-	 * pipeline, we don't need any of the rest of this function to
-	 * affect the state in the main shell, so fork immediately.
+	 * If running in the background, not the last command in a
+	 * pipeline, or the last command in a multi-stage pipeline
+	 * in sh mode, we don't need any of the rest of this function
+	 * to affect the state in the main shell, so fork immediately.
 	 *
 	 * In other cases we may need to process the command line
 	 * a bit further before we make the decision.
diff --git a/Test/B07emulate.ztst b/Test/B07emulate.ztst
index 7b1592fa9..45c39b51d 100644
--- a/Test/B07emulate.ztst
+++ b/Test/B07emulate.ztst
@@ -276,3 +276,25 @@ F:Some reserved tokens are handled in alias expansion
 0:--emulate followed by other options
 >yes
 >no
+
+  emulate sh -c '
+  foo () {
+    VAR=foo &&
+    echo $VAR | bar &&
+    echo "$VAR"
+  }
+  bar () {
+    tr f b &&
+    VAR="$(echo bar | tr r z)" &&
+    echo "$VAR"
+  }
+  foo
+  '
+  emulate sh -c 'func() { echo | local def="abc"; echo $def;}; func'
+  emulate sh -c 'abc="def"; echo | abc="ghi"; echo $abc'
+0:emulate sh uses subshell for last pipe entry
+>boo
+>baz
+>foo
+>
+>def

^ permalink raw reply	[relevance 4%]

* Re: [PATCH 0/1] Run final pipeline command in a subshell in sh mode
  2019-11-24 23:41  5% [PATCH 0/1] Run final pipeline command in a subshell in sh mode brian m. carlson
  2019-11-24 23:41  4% ` [PATCH 1/1] exec: run " brian m. carlson
@ 2019-12-07 19:23  0% ` brian m. carlson
  1 sibling, 0 replies; 200+ results
From: brian m. carlson @ 2019-12-07 19:23 UTC (permalink / raw)
  To: zsh-workers

[-- Attachment #1: Type: text/plain, Size: 907 bytes --]

> POSIX sh implementations run each command in a pipeline in a subshell,
> although zsh (and AT&T ksh) do not: instead, they run the final command
> in the main shell.
> 
> zsh is starting to be used in some cases as /bin/sh, such as on macOS
> Catalina.  Whether this is a good idea or not, it makes sense to emulate
> the POSIX behavior as much as possible when emulating sh, since that's
> the least surprising behavior.  This patch does exactly that.
> 
> With this patch, using "zsh --emulate sh" passes all but one test of the
> Git testsuite.  The remaining failure is due to zsh preserving NUL bytes
> in the output of command substitutions, which is permitted by POSIX; I
> will be sending a patch to fix that bug in the Git testsuite.

Gentle ping.  Is there any feedback that folks have on this patch?
-- 
brian m. carlson: Houston, Texas, US
OpenPGP: https://keybase.io/bk2204

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 868 bytes --]

^ permalink raw reply	[relevance 0%]

* BUG: small posix glitch in ${x%*}
@ 2019-12-09 22:07  5% ` Jan Grant
  2019-12-10 10:48  4%   ` Peter Stephenson
  0 siblings, 1 reply; 200+ results
From: Jan Grant @ 2019-12-09 22:07 UTC (permalink / raw)
  To: zsh-workers

As I read the spec, the smallest suffix that can match * is the null string; so

    x=123; echo ${x%*}

should output "123". Zsh (as of 5.7.1) drops the last character.

(The behaviour for prefixes is correct.)

^ permalink raw reply	[relevance 5%]

* Re: BUG: small posix glitch in ${x%*}
  2019-12-09 22:07  5% ` BUG: small posix glitch in ${x%*} Jan Grant
@ 2019-12-10 10:48  4%   ` Peter Stephenson
  0 siblings, 0 replies; 200+ results
From: Peter Stephenson @ 2019-12-10 10:48 UTC (permalink / raw)
  To: zsh-workers

On Mon, 2019-12-09 at 22:07 +0000, Jan Grant wrote:
> As I read the spec, the smallest suffix that can match * is the null
> string; so
> 
>     x=123; echo ${x%*}
> 
> should output "123". Zsh (as of 5.7.1) drops the last character.
> 
> (The behaviour for prefixes is correct.)

Yes, that's right --- there are already some age-old icky special cases
for this in some places but not this one.  So a bit of copy and paste...

pws

diff --git a/Src/glob.c b/Src/glob.c
index 92fd64e7c..674563c8f 100644
--- a/Src/glob.c
+++ b/Src/glob.c
@@ -2909,6 +2909,12 @@ igetmatch(char **sp, Patprog p, int fl, int n, char *replstr,
 	     */
 	    mb_charinit();
 	    tmatch = NULL;
+	    set_pat_start(p, l);
+	    if (pattrylen(p, send, 0, 0, &patstralloc, umltot) &&
+		!--n) {
+		*sp = get_match_ret(&imd, umltot, umltot);
+		return 1;
+	    }
 	    for (ioff = 0, t = s, umlen = umltot; t < send; ioff++) {
 		set_pat_start(p, t-s);
 		if (pattrylen(p, t, umlen, 0, &patstralloc, ioff))
diff --git a/Test/D04parameter.ztst b/Test/D04parameter.ztst
index b6e85a9fe..c91af1a9c 100644
--- a/Test/D04parameter.ztst
+++ b/Test/D04parameter.ztst
@@ -2534,3 +2534,26 @@ F:behavior, see http://austingroupbugs.net/view.php?id=888
 0:Global variables are not trashed by "foo=bar builtin" (regression test)
 >function-value
 >global-value
+
+ foo=pws
+ print ${foo%*}
+0:Smallest match at end can match zero-length string
+>pws
+
+ foo=pws
+ print ${foo%?}
+0:Smallest match at end with a character always matches one
+>pw
+
+ setopt extendedglob
+ foo=pws
+ print ${foo%s#}
+ print ${foo%%s#}
+0:Smallest / largest match with non-trivial closure
+>pws
+>pw
+
+ foo=pws
+ print ${foo%%*}
+0:Largest match at end matches entire string
+>


^ permalink raw reply	[relevance 4%]

* [PATCH] more documentation typos
@ 2019-12-10 19:34  5% Martijn Dekker
  0 siblings, 0 replies; 200+ results
From: Martijn Dekker @ 2019-12-10 19:34 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 149 bytes --]

Here are some more typo fixes in Etc/, Functions/, NEWS and README.

- M.

-- 
modernish -- harness the shell
https://github.com/modernish/modernish

[-- Attachment #2: typos2.patch --]
[-- Type: text/plain, Size: 6242 bytes --]

diff --git a/Etc/FAQ.yo b/Etc/FAQ.yo
index a9ce754b5..cf4348cbe 100644
--- a/Etc/FAQ.yo
+++ b/Etc/FAQ.yo
@@ -1390,7 +1390,7 @@ sect(Why do my autoloaded functions not autoload [the first time]?)
   redefine the function when you called it.
 
   From version 3.1, there is an option tt(KSH_AUTOLOAD) to allow full ksh
-  compatiblity, i.e. the function myem(must) be in the second form
+  compatibility, i.e. the function myem(must) be in the second form
   above.  If that is not set, zsh tries to guess which form you are
   using:  if the file contains only a complete definition of the
   function in the second form, and nothing else apart from comments
@@ -1936,7 +1936,7 @@ label(327)
      operator.  As the mytt(**) operator cannot be grouped (inside
      parentheses it is treated as mytt(*)), this is one way to exclude some
      subdirectories from matching a mytt(**).  Note that this can be quite
-     inefficent because the shell performs a complete search for
+     inefficient because the shell performs a complete search for
      mytt(**/foo) before it uses the pattern after the mytt(~) to exclude
      files from the match.  The file is excluded if mytt(bar) occurs
      em(anywhere), in any directory segment or the final file name.
@@ -2483,7 +2483,7 @@ label(52)
   tt(unsubscribe) to unsubscribe.  The mailing software (tt(ezlm)) has
   various bells and whistles: you can retrieve archived messages.
   Mail email(zsh-workers-help@zsh.org) for detailed information.
-  Adminstrative matters are best sent to
+  Administrative matters are best sent to
   email(zsh-workers-owner@zsh.org).
   real name is email(Geoff Wing <gcw@zsh.org>).
   
diff --git a/Etc/zsh-development-guide b/Etc/zsh-development-guide
index ecbd3c081..cbada7de9 100644
--- a/Etc/zsh-development-guide
+++ b/Etc/zsh-development-guide
@@ -891,7 +891,7 @@ The wrapper function should be defined like:
 The first two arguments should only be used to pass them to
 `runshfunc()' which will execute the shell function. The last argument 
 is the name of the function to be executed. The arguments passed to
-the function can be accessed vie the global variable `pparams' (a
+the function can be accessed via the global variable `pparams' (a
 NULL-terminated array of strings).
 
 The return value of the wrapper function should be zero if it calls
diff --git a/Functions/Prompts/prompt_oliver_setup b/Functions/Prompts/prompt_oliver_setup
index 979411d4b..2df919950 100644
--- a/Functions/Prompts/prompt_oliver_setup
+++ b/Functions/Prompts/prompt_oliver_setup
@@ -5,7 +5,7 @@ prompt_oliver_help() {
 With this prompt theme, the prompt contains the current directory,
 history number, number of jobs (if non-zero) and the previous
 command's exit code (if non-zero) and a final character which
-depends on priviledges.
+depends on privileges.
 
 The colour of the prompt depends on two associative arrays -
 $pcolour and $tcolour. Each array is indexed by the name of the
diff --git a/Functions/Zle/insert-composed-char b/Functions/Zle/insert-composed-char
index c0922e7d4..636895a89 100644
--- a/Functions/Zle/insert-composed-char
+++ b/Functions/Zle/insert-composed-char
@@ -27,7 +27,7 @@
 #  '   Acute
 #  >   Circumflex
 #  ?   Tilde
-#  -   Macron.  (A horizonal bar over the letter.)
+#  -   Macron.  (A horizontal bar over the letter.)
 #  (   Breve.  (A shallow dish shape over the letter.)
 #  .   Dot above, or no dot with lower case i, or dot in the middle of L or l.
 #  :   Diaeresis (Umlaut)
@@ -43,7 +43,7 @@
 #  9   Horn
 # Hence A! is upper case A with a grave, c, is lower case c with cedilla.
 #
-# Some other composed charaters:
+# Some other composed characters:
 # Various ligatures:
 #  AE ae OE oe IJ ij
 #
diff --git a/NEWS b/NEWS
index f0b23b9f2..c1322ed0f 100644
--- a/NEWS
+++ b/NEWS
@@ -547,7 +547,7 @@ Expansion (parameters, globbing, etc.) and redirection
   calculated by the (l) and (r) flags or the # operator should take
   account of the printing width of characters in multibyte mode, whether
   0, 1 or more.  (mm) causes printing characters to count as 1 and
-  non-printing chracters to count as 0.
+  non-printing characters to count as 0.
 
 - The parameter substitution flag (q-) picks the most minimal way of
   quoting the parameter words, to make the result as readable as possible.
diff --git a/README b/README
index 7ece48183..2dd82b61f 100644
--- a/README
+++ b/README
@@ -204,7 +204,7 @@ of this change is that variables that should have active ranges need
   [[ b = [${~cset}] ]]
 
 The "~" causes the "-" character to be active.  In sh emulation the
-"~" is unncessary in this example and double quotes must be used to
+"~" is unnecessary in this example and double quotes must be used to
 suppress the range behaviour of the "-".
 
 2) The first argument to 'repeat' is now evaluated as an arithmetic
@@ -217,7 +217,7 @@ leading zeroes and the OCTAL_ZEROES option is set.
 3) For some time the shell has had a POSIX_TRAPS option which determines
 whether the EXIT trap has POSIX behaviour (the trap is only run at shell
 exit) or traditional zsh behaviour (the trap is run once and discarded
-when the enclosing fuction or shell exits, whichever happens first).
+when the enclosing function or shell exits, whichever happens first).
 The use of this option has now been made "sticky" on the EXIT trap ---
 in other words, the setting of the option at the point where the trap is
 set now determines whether the trap has POSIX or traditional zsh
@@ -344,7 +344,7 @@ can be used with both assoiative arrays and normal arrays.  In the
 unlikely event that you wish to create an array with an entry
 matching a file whose name consists of one of a range of characters
 matched as a [...] expression, followed by an equal sign, followed
-by arbitrary other charaters, it is now necessary to quote the equals
+by arbitrary other characters, it is now necessary to quote the equals
 sign.
 
 Incompatibilites between 5.0.7 and 5.0.8
@@ -361,7 +361,7 @@ or both of the arguments were floating point.  Now, the C math
 library fmod() operator is used to implement the operation where
 one of the arguments is floating point.  For example:
 
-Old behavour:
+Old behaviour:
 
 % print $(( 5.5 % 2 ))
 1

^ permalink raw reply	[relevance 5%]

* [PATCH] POSIX_CD: disable stack entries
@ 2019-12-12 10:38  5% ` Martijn Dekker
  2019-12-12 10:50  3%   ` Peter Stephenson
  0 siblings, 1 reply; 200+ results
From: Martijn Dekker @ 2019-12-12 10:38 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 782 bytes --]

Recognising directory stack entries for the 'cd' builtin is not 
compatible with POSIX, because cd'ing into directories with names like 
+123 or -4567 no longer works without prefixing './', even after '--'. 
In POSIX, only the '-' operand has such a special meaning. The attached 
patch disables directory stack entries for 'cd' if POSIX_CD is active.

This patch also changes the behaviour of the 'chdir' equivalent. For 
POSIX compliance, only 'cd' needs to be changed, as POSIX has no 
'chdir'. I could change the patch to only change 'cd', but it would 
involve slightly more code, and would make 'cd' and 'chdir' no longer 
exactly equivalent. If this is preferred, please let me know.

Thanks,

- M.

-- 
modernish -- harness the shell
https://github.com/modernish/modernish

[-- Attachment #2: POSIX_CD.patch --]
[-- Type: text/plain, Size: 1836 bytes --]

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 15d3e0cf4..415bce613 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -290,6 +290,8 @@ of the list shown by the tt(dirs) command, starting with zero.
 An argument of the form `tt(-)var(n)' counts from the right.
 If the tt(PUSHD_MINUS) option is set, the meanings of `tt(PLUS())'
 and `tt(-)' in this context are swapped.
+If the tt(POSIX_CD) option is set, this form of tt(cd) is not recognised
+and will be interpreted as the first form.
 
 If the tt(-q) (quiet) option is specified, the hook function tt(chpwd)
 and the functions in the array tt(chpwd_functions) are not called.
diff --git a/Doc/Zsh/options.yo b/Doc/Zsh/options.yo
index 903c31134..88bb643d1 100644
--- a/Doc/Zsh/options.yo
+++ b/Doc/Zsh/options.yo
@@ -148,7 +148,8 @@ ifzman(zmanref(zshbuiltins))\
 ifnzman(noderef(Shell Builtin Commands)).
 If the option is set, the shell does not test for directories beneath
 the local directory (`tt(.)') until after all directories in tt(cdpath)
-have been tested.
+have been tested, and the tt(cd) and tt(chdir) commands do not recognise
+arguments of the form `{tt(PLUS())|tt(-)}var(n)' as directory stack entries.
 
 Also, if the option is set, the conditions under which the shell
 prints the new directory after changing to it are modified.  It is
diff --git a/Src/builtin.c b/Src/builtin.c
index bd7736d2c..7bf4281da 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -912,7 +912,7 @@ cd_get_dest(char *nam, char **argv, int hard, int func)
 	char *end;
 
 	doprintdir++;
-	if (argv[0][1] && (argv[0][0] == '+' || argv[0][0] == '-')
+	if (!isset(POSIXCD) && argv[0][1] && (argv[0][0] == '+' || argv[0][0] == '-')
 	    && strspn(argv[0]+1, "0123456789") == strlen(argv[0]+1)) {
 	    dd = zstrtol(argv[0] + 1, &end, 10);
 	    if (*end == '\0') {

^ permalink raw reply	[relevance 5%]

* Re: [PATCH] POSIX_CD: disable stack entries
  2019-12-12 10:38  5% ` [PATCH] POSIX_CD: disable stack entries Martijn Dekker
@ 2019-12-12 10:50  3%   ` Peter Stephenson
  0 siblings, 0 replies; 200+ results
From: Peter Stephenson @ 2019-12-12 10:50 UTC (permalink / raw)
  To: zsh-workers

On Thu, 2019-12-12 at 11:38 +0100, Martijn Dekker wrote:
> Recognising directory stack entries for the 'cd' builtin is not 
> compatible with POSIX, because cd'ing into directories with names like 
> +123 or -4567 no longer works without prefixing './', even after '--'. 
> In POSIX, only the '-' operand has such a special meaning. The attached 
> patch disables directory stack entries for 'cd' if POSIX_CD is active.
> 
> This patch also changes the behaviour of the 'chdir' equivalent. For 
> POSIX compliance, only 'cd' needs to be changed, as POSIX has no 
> 'chdir'. I could change the patch to only change 'cd', but it would 
> involve slightly more code, and would make 'cd' and 'chdir' no longer 
> exactly equivalent. If this is preferred, please let me know.

Thanks, that sounds perfectly reasonable and I've committed it.

The intention of the POSIX options is entirely in accord with your
own --- make this clean and simple, with minimal confusion
with the clever stuff the shell does the rest of the time.

pws


^ permalink raw reply	[relevance 3%]

* Re: regexp-replace and ^, word boundary or look-behind operators
  @ 2019-12-16 21:27  4% ` Stephane Chazelas
    0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2019-12-16 21:27 UTC (permalink / raw)
  To: Zsh hackers list

2019-12-16 21:10:13 +0000, Stephane Chazelas:
> The way regexp-replace works means that these things:
> 
> $ a='aaab'; regexp-replace a '^a' x; echo "$a"
> xxxb
> $ a='abab'; regexp-replace a '\<ab' '<$MATCH>'; echo $a
> <ab><ab>
> $ set -o rematchpcre
> $ a=xxx; regexp-replace a '(?<!x)x' y; echo $a
> yyy
[...]

FWIW, looks like some sed implementations (like that of the
heirloom toolchest or busybox) or ksh93 have the same problem:

$ echo xxx | busybox sed 's/\<x/y/g'
yyy
$ a=xxx ksh -c 'echo ${a//~(E:^x)/y}'
yyy
$ a=xxx ksh -c 'echo ${a//[[:<:]]x/y}'
yyy

It may be that the POSIX regex API doesn't have a way to fix
that (REG_NOTBOL addresses the ^ case, but there's nothing about
\< / \b / [[:<]] which are non-POSIX extensions anyway).

PCRE should be OK, so it could be just a matter of
exposing it via the pcre_match builtin and document the
limitation otherwise for EREs (PCRE is the new de-facto standard
anyway).

-- 
Stephane

^ permalink raw reply	[relevance 4%]

* Re: [PATCH] Re: regexp-replace and ^, word boundary or look-behind operators
  @ 2019-12-18  0:22  4%       ` Daniel Shahaf
  2020-01-01 14:03 10%         ` [PATCH v2] " Stephane Chazelas
  0 siblings, 1 reply; 200+ results
From: Daniel Shahaf @ 2019-12-18  0:22 UTC (permalink / raw)
  To: Stephane Chazelas, zsh-workers

Stephane Chazelas wrote on Tue, 17 Dec 2019 11:11 +00:00:
> +++ b/Doc/Zsh/contrib.yo
> @@ -4301,6 +4301,9 @@ and arithmetic expressions which will be 
> replaced:  in particular, a
>  reference to tt($MATCH) will be replaced by the text matched by the 
> pattern.
>  
>  The return status is 0 if at least one match was performed, else 1.
> +
> +Note that if not using PCRE, using the tt(^) or word boundary operators
> +(where available) may not work properly.

Suggest to avoid the double negative:

1. s/not using PCRE/using POSIX ERE's/

2. Add "(ERE's)" after "POSIX extended regular expressions" in the first paragraph

I'll push a minor change to that first paragraph in a moment.

>  )
> +++ b/Functions/Example/zpgrep
> @@ -2,24 +2,31 @@
> +eval $1=\$5

How about «: ${(P)1::="$5"}» to avoid eval?

^ permalink raw reply	[relevance 4%]

* Test failures in --disable-multibyte
@ 2019-12-31 18:39  2% Daniel Shahaf
  0 siblings, 0 replies; 200+ results
From: Daniel Shahaf @ 2019-12-31 18:39 UTC (permalink / raw)
  To: zsh-workers

While looking into workers/45164 (${(S)%%*}) I found that igetmatch() had two
implementations, depending on whether multibyte was enabled.  I wanted to test
both codepaths, so I built clean master with --disable-multibyte and ran the
test suite and got several failures (see below).  Some of the failures are
probably bugs in the tests, not in the C core, but even so, given that so many
tests fail — and failed in 5.7.1 too — I wonder whether anyone actually builds
zsh with --disable-multibyte.

The --enable-multibyte flag was added to configure in 2005, and hasn't changed
much since.  The functions that configure checks for when deciding whether to
--enable-multibyte are all in POSIX.1-2001, and all but one of them (wcwidth())
are also in C99.

In the circumstances, I wonder if we should just make those functions a hard
dependency and remove the --disable-multibyte (#ifndef MULTIBYTE_SUPPORT)
codepaths entirely.

Thoughts?

Cheers,

Daniel

[[[
./Test/A03quoting.ztst: starting.
--- /tmp/zsh.ztst.15381/ztst.out	2019-12-31 18:07:13.215429795 +0000
+++ /tmp/zsh.ztst.15381/ztst.tout	2019-12-31 18:07:13.215429795 +0000
@@ -1 +1,3 @@
-$'one\\two\n\'buckle\'\tmy\\shoe\n'
+'one\two
+'\''buckle'\''	my\shoe
+'
Test ./Test/A03quoting.ztst failed: output differs from expected as shown above for:
 foo=$'one\\two\n\'buckle\'\tmy\\shoe\n'
 print -r ${(q+)foo}
Was testing: Extended minimal quoting of quotes and backslashes

./Test/B02typeset.ztst: starting.
--- /tmp/zsh.ztst.22994/ztst.out	2019-12-31 18:07:24.467350785 +0000
+++ /tmp/zsh.ztst.22994/ztst.tout	2019-12-31 18:07:24.467350785 +0000
@@ -1,7 +1,7 @@
 l o c a
 typeset -UT SCALAR array=( l o c a ) ''
 typeset -aT SCALAR array=( l o c a ) ''
-SCALAR=$'l\C-@o\C-@c\C-@a'
+SCALAR=loca
 array=( l o c a )
 local unique tied array SCALAR
 array local tied SCALAR array
Test ./Test/B02typeset.ztst failed: output differs from expected as shown above for:
 typeset -T SCALAR=$'l\000o\000c\000a\000l' array $'\000'
 typeset -U SCALAR
 print $array
 typeset -p SCALAR array
 typeset -m SCALAR array
 typeset +m SCALAR array
 [[ $SCALAR == $'l\000o\000c\000a' ]]
Was testing: Tied parameters and uniquified arrays with NUL-character as separator

./Test/B03print.ztst: starting.
--- /tmp/zsh.ztst.23078/ztst.out	2019-12-31 18:07:25.019346909 +0000
+++ /tmp/zsh.ztst.23078/ztst.tout	2019-12-31 18:07:25.023346880 +0000
@@ -1,2 +1,2 @@
 typeset -g foo='once more'
-typeset -g foo=$'into\C-@the-breach\C-@-'
+typeset -g foo=intothe-breach-
Test ./Test/B03print.ztst failed: output differs from expected as shown above for:
 unset foo
 print -v foo once more
 typeset -p foo
 printf -v foo "%s\0%s-" into the breach
 typeset -p foo
Was testing: print and printf into a variable

./Test/D01prompt.ztst: starting.
--- /tmp/zsh.ztst.24994/ztst.out	2019-12-31 18:07:42.087227059 +0000
+++ /tmp/zsh.ztst.24994/ztst.tout	2019-12-31 18:07:42.091227031 +0000
@@ -1 +1 @@
-
+V
Test ./Test/D01prompt.ztst failed: output differs from expected as shown above for:
  print ${(%U)Y-%(v}
Was testing: Regression test for test on empty psvar

./Test/D04parameter.ztst: starting.
--- /tmp/zsh.ztst.25437/ztst.out        2019-12-31 18:07:43.699215739 +0000                                                                                                                                        
+++ /tmp/zsh.ztst.25437/ztst.tout	2019-12-31 18:07:43.703215711 +0000
@@ -1 +1 @@
-^?^@
+\C-?\C-@
Test ./Test/D04parameter.ztst failed: output differs from expected as shown above for:
  foo=$'\x7f\x00'
  print -r -- ${(V)foo}
Was testing: ${(V)...}

./Test/V09datetime.ztst: starting.
--- /tmp/zsh.ztst.27420/ztst.out	2019-12-31 18:07:49.207177063 +0000
+++ /tmp/zsh.ztst.27420/ztst.tout	2019-12-31 18:07:49.207177063 +0000
@@ -1 +1 @@
-1973^@03^@03
+1973\C-@03\C-@03
Test ./Test/V09datetime.ztst failed: output differs from expected as shown above for:
  print -r -- ${(V)"$(strftime $'%Y\0%m\0%d' 100000000)"}
Was testing: Embedded nulls

./Test/V10private.ztst: starting.
Test ./Test/V10private.ztst failed: bad status 1, expected 0 from:
 if (( UID )); then
   ZTST_verbose=0 $ZTST_exe +Z -f $ZTST_srcdir/ztst.zsh private.TMP/B02
 else
   ZTST_skip="cannot re-run typeset tests when tests run as superuser"
 fi
Was testing: typeset still works with zsh/param/private module loaded

**************************************
43 successful test scripts, 7 failures, 3 skipped
**************************************
]]]

^ permalink raw reply	[relevance 2%]

* [PATCH v2] regexp-replace and ^, word boundary or look-behind operators
  2019-12-18  0:22  4%       ` Daniel Shahaf
@ 2020-01-01 14:03 10%         ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2020-01-01 14:03 UTC (permalink / raw)
  To: zsh-workers

2019-12-18 00:22:53 +0000, Daniel Shahaf:
[...]
> > +
> > +Note that if not using PCRE, using the tt(^) or word boundary operators
> > +(where available) may not work properly.
> 
> Suggest to avoid the double negative:
> 
> 1. s/not using PCRE/using POSIX ERE's/
> 
> 2. Add "(ERE's)" after "POSIX extended regular expressions" in the first paragraph
> 
> I'll push a minor change to that first paragraph in a moment.
[...]

Thanks, I've incorporated that suggesting and fixed an issue
with PCRE when the replacement was empty or generated more than
one element.

diff --git a/Doc/Zsh/contrib.yo b/Doc/Zsh/contrib.yo
index 558342711..9a804fc11 100644
--- a/Doc/Zsh/contrib.yo
+++ b/Doc/Zsh/contrib.yo
@@ -4284,7 +4284,7 @@ See also the tt(pager), tt(prompt) and tt(rprompt) styles below.
 findex(regexp-replace)
 item(tt(regexp-replace) var(var) var(regexp) var(replace))(
 Use regular expressions to perform a global search and replace operation
-on a variable.  POSIX extended regular expressions are used,
+on a variable.  POSIX extended regular expressions (ERE) are used,
 unless the option tt(RE_MATCH_PCRE) has been set, in which case
 Perl-compatible regular expressions are used
 (this requires the shell to be linked against the tt(pcre)
@@ -4302,6 +4302,9 @@ and arithmetic expressions which will be replaced:  in particular, a
 reference to tt($MATCH) will be replaced by the text matched by the pattern.
 
 The return status is 0 if at least one match was performed, else 1.
+
+Note that if using POSIX EREs, the tt(^) or word boundary operators
+(where available) may not work properly.
 )
 findex(run-help)
 item(tt(run-help) var(cmd))(
diff --git a/Functions/Example/zpgrep b/Functions/Example/zpgrep
index 8b1edaa1c..556e58cd6 100644
--- a/Functions/Example/zpgrep
+++ b/Functions/Example/zpgrep
@@ -2,24 +2,31 @@
 #
 
 zpgrep() {
-local file pattern
+local file pattern ret
 
 pattern=$1
 shift
+ret=1
 
 if ((! ARGC)) then
 	set -- -
 fi
 
-pcre_compile $pattern
+zmodload zsh/pcre || return
+pcre_compile -- "$pattern"
 pcre_study
 
 for file
 do
 	if [[ "$file" == - ]] then
-		while read -u0 buf; do pcre_match $buf && print $buf; done
+		while IFS= read -ru0 buf; do
+			pcre_match -- "$buf" && ret=0 && print -r -- "$buf"
+		done
 	else
-		while read -u0 buf; do pcre_match $buf && print $buf; done < "$file"
+		while IFS= read -ru0 buf; do
+			pcre_match -- "$buf" && ret=0 && print -r -- "$buf"
+		done < "$file"
 	fi
 done
+return "$ret"
 }
diff --git a/Functions/Misc/regexp-replace b/Functions/Misc/regexp-replace
index dec105524..0d5948075 100644
--- a/Functions/Misc/regexp-replace
+++ b/Functions/Misc/regexp-replace
@@ -8,36 +8,84 @@
 # $ and backtick substitutions; in particular, $MATCH will be replaced
 # by the portion of the string matched by the regular expression.
 
-integer pcre
+# we use positional parameters instead of variables to avoid
+# clashing with the user's variable. Make sure we start with 3 and only
+# 3 elements:
+argv=("$1" "$2" "$3")
 
-[[ -o re_match_pcre ]] && pcre=1
+# $4 records whether pcre is enabled as that information would otherwise
+# be lost after emulate -L zsh
+4=0
+[[ -o re_match_pcre ]] && 4=1
 
 emulate -L zsh
-(( pcre )) && setopt re_match_pcre
-
-# $4 is the string to be matched
-4=${(P)1}
-# $5 is the final string
-5=
-# 6 indicates if we made a change
-6=
+
+
 local MATCH MBEGIN MEND
 local -a match mbegin mend
 
-while [[ -n $4 ]]; do
-  if [[ $4 =~ $2 ]]; then
-    # append initial part and subsituted match
-    5+=${4[1,MBEGIN-1]}${(e)3}
-    # truncate remaining string
-    4=${4[MEND+1,-1]}
-    # indicate we did something
-    6=1
-  else
-    break
-  fi
-done
-5+=$4
-
-eval ${1}=${(q)5}
-# status 0 if we did something, else 1.
-[[ -n $6 ]]
+if (( $4 )); then
+  # if using pcre, we're using pcre_match and a running offset
+  # That's needed for ^, \A, \b, and look-behind operators to work
+  # properly.
+
+  zmodload zsh/pcre || return 2
+  pcre_compile -- "$2" && pcre_study || return 2
+
+  # $4 is the current *byte* offset, $5, $6 reserved for later use
+  4=0 6=
+
+  local ZPCRE_OP
+  while pcre_match -b -n $4 -- "${(P)1}"; do
+    # append offsets and computed replacement to the array
+    # we need to perform the evaluation in a scalar assignment so that if
+    # it generates an array, the elements are converted to string (by
+    # joining with the first chararacter of $IFS as usual)
+    5=${(e)3}
+    argv+=(${(s: :)ZPCRE_OP} "$5")
+
+    # for 0-width matches, increase offset by 1 to avoid
+    # infinite loop
+    4=$((argv[-2] + (argv[-3] == argv[-2])))
+  done
+
+  (($# > 6)) || return # no match
+
+  set +o multibyte
+
+  # $5 contains the result, $6 the current offset
+  5= 6=1
+  for 2 3 4 in "$@[7,-1]"; do
+    5+=${(P)1[$6,$2]}$4
+    6=$(($3 + 1))
+  done
+  5+=${(P)1[$6,-1]}
+else
+  # in ERE, we can't use an offset so ^, (and \<, \b, \B, [[:<:]] where
+  # available) won't work properly.
+
+  # $4 is the string to be matched
+  4=${(P)1}
+
+  while [[ -n $4 ]]; do
+    if [[ $4 =~ $2 ]]; then
+      # append initial part and substituted match
+      5+=${4[1,MBEGIN-1]}${(e)3}
+      # truncate remaining string
+      if ((MEND < MBEGIN)); then
+        # zero-width match, skip one character for the next match
+        ((MEND++))
+	5+=${4[1]}
+      fi
+      4=${4[MEND+1,-1]}
+      # indicate we did something
+      6=1
+    else
+      break
+    fi
+  done
+  [[ -n $6 ]] || return # no match
+  5+=$4
+fi
+
+eval $1=\$5


^ permalink raw reply	[relevance 10%]

* [PATCH] Make --enable-gdbm default to false, rather than default to true with an unavoidable warning.
@ 2020-01-03 20:08  5% Daniel Shahaf
  0 siblings, 0 replies; 200+ results
From: Daniel Shahaf @ 2020-01-03 20:08 UTC (permalink / raw)
  To: zsh-workers

---
As the subject says.  If it's something the user might not want to do,
make it opt-in.

Also, there was no way for a user who understood the implications to
turn off the warning.

Cheers,

Daniel


 README       |  4 ++++
 configure.ac | 14 +++-----------
 2 files changed, 7 insertions(+), 11 deletions(-)

diff --git a/README b/README
index 5d9d9073a..5ae6087a2 100644
--- a/README
+++ b/README
@@ -33,6 +33,10 @@ details, see the documentation.
 Incompatibilities since 5.7.1
 -----------------------------
 
+Build-time change: The default value of the --enable-gdbm configure
+argument has changed from "yes" to "no".  Thus, the zsh/db/gdbm module will
+not be built unless --enable-gdbm is passed explicitly.
+
 The history expansion !:1:t2 used to be interpreted such that the 2
 was a separate character added after the history expansion.  Now
 it is an argument to the :t modifier.
diff --git a/configure.ac b/configure.ac
index 8fd4d452f..256584538 100644
--- a/configure.ac
+++ b/configure.ac
@@ -444,9 +444,10 @@ dnl Do you want to look for capability support?
 AC_ARG_ENABLE(cap,
 AS_HELP_STRING([--enable-cap],[enable the search for POSIX capabilities (may require additional headers to be added by hand)]))
 
+# Default off for licensing reasons
 AC_ARG_ENABLE(gdbm,
-AS_HELP_STRING([--disable-gdbm],[turn off search for gdbm library]),
-[gdbm="$enableval"], [gdbm=yes])
+AS_HELP_STRING([--enable-gdbm],[enable the search for the GDBM library (see the zsh/db/gdbm module)]),
+[gdbm="$enableval"], [gdbm=no])
 
 dnl ------------------
 dnl CHECK THE COMPILER
@@ -3298,13 +3299,4 @@ fi
 echo "See config.modules for installed modules and functions.
 "
 
-case x$LIBS in
-  *-lgdbm*)
-  echo "WARNING: zsh will be linked against libgdbm.
-This means the binary is covered by the GNU General Public License.
-This does not affect the source code.
-Run configure with --disable-gdbm if required."
-  ;;
-esac
-
 exit 0

^ permalink raw reply	[relevance 5%]

* [PATCH] New helper script for listing XFail tests.
@ 2020-01-12 22:40  3% Daniel Shahaf
  0 siblings, 0 replies; 200+ results
From: Daniel Shahaf @ 2020-01-12 22:40 UTC (permalink / raw)
  To: zsh-workers

---
It currently lists these:

% Test/list-XFails
Test/C03traps.ztst:895:-f:(workers/44007) function execution continues after 'exit' in trap
Test/D02glob.ztst:737:-f:unreadable directories can be globbed (users/24619, users/24626)
% 

It uses --color, which isn't in POSIX, but we can cross the portability
bridge when we come to it.

Cheers,

Daniel
(I'd still like to pick up and finish 44007…)

 Test/list-XFails | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100755 Test/list-XFails

diff --git a/Test/list-XFails b/Test/list-XFails
new file mode 100755
index 000000000..8d94a3d04
--- /dev/null
+++ b/Test/list-XFails
@@ -0,0 +1,5 @@
+#!/bin/sh
+# This script lists tests that are currently expected to fail.  (I.e., tests
+# for bugs that haven't been fixed yet.)
+
+grep --color -E -n '^[-0-9.dDqf]*f[-0-9.dDqf]*:' -- "$(dirname -- "$0")"/*.ztst

^ permalink raw reply	[relevance 3%]

* [PATCH] sh/ksh init: don't initialise lowercase parameters
@ 2020-01-28 15:19  3% ` Martijn Dekker
  2020-01-28 15:26  0%   ` Peter Stephenson
  2020-01-29  8:49  3%   ` Daniel Shahaf
  0 siblings, 2 replies; 200+ results
From: Martijn Dekker @ 2020-01-28 15:19 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 847 bytes --]

In POSIX sh and in ksh, it's the convention that lowercase variable 
names are reserved for scripts, and uppercase names may be used by the 
shell or system. So most lowercase zsh parameters are not initialised 
when zsh is invoked as sh or ksh (through a symlink or the --emulate 
option).

However, there are two left that are initialised in sh/ksh mode: 
'histchars' and 'signals'. So this can conflict with POSIX scripts.

Also, if zsh is invoked as sh or ksh, 'histchars' is available whereas 
the 'HISTCHARS' equivalent is not. It seems quite obvious that this 
should be the other way around.

The attached patch makes zsh, when invoked as sh or ksh, not initialise 
'signals', and initialise 'HISTCHARS' instead of 'histchars'. It also 
updates the documentation.

-- 
modernish -- harness the shell
https://github.com/modernish/modernish

[-- Attachment #2: posixnames.patch --]
[-- Type: text/plain, Size: 3722 bytes --]

diff --git a/Doc/Zsh/compat.yo b/Doc/Zsh/compat.yo
index f1be15fee..d085dfaa7 100644
--- a/Doc/Zsh/compat.yo
+++ b/Doc/Zsh/compat.yo
@@ -19,7 +19,7 @@ tt(argv),
 tt(cdpath),
 tt(fignore),
 tt(fpath),
-tt(HISTCHARS),
+tt(histchars),
 tt(mailpath),
 tt(MANPATH),
 tt(manpath),
@@ -30,6 +30,7 @@ tt(PROMPT2),
 tt(PROMPT3),
 tt(PROMPT4),
 tt(psvar),
+tt(signals),
 tt(status),
 tt(watch).
 
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 8daf33d5e..5f772bb50 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -850,7 +850,7 @@ item(tt(SHLVL) <S>)(
 Incremented by one each time a new shell is started.
 )
 vindex(signals)
-item(tt(signals))(
+item(tt(signals) <Z>)(
 An array containing the names of the signals.  Note that with
 the standard zsh numbering of array indices, where the first element
 has index 1, the signals are offset by 1 from the signal number
@@ -1161,7 +1161,7 @@ with the tt(-u) attribute is referenced.  If an executable
 file is found, then it is read and executed in the current environment.
 )
 vindex(histchars)
-item(tt(histchars) <S>)(
+item(tt(histchars) <S> <Z>)(
 Three characters used by the shell's history and lexical analysis
 mechanism.  The first character signals the start of a history
 expansion (default `tt(!)').  The second character signals the
@@ -1173,8 +1173,8 @@ tt(histchars) to characters with a locale-dependent meaning will be
 rejected with an error message.
 )
 vindex(HISTCHARS)
-item(tt(HISTCHARS) <S> <Z>)(
-Same as tt(histchars).  (Deprecated.)
+item(tt(HISTCHARS) <S>)(
+Same as tt(histchars).
 )
 vindex(HISTFILE)
 item(tt(HISTFILE))(
diff --git a/NEWS b/NEWS
index af59cb4e6..9d96ef0d1 100644
--- a/NEWS
+++ b/NEWS
@@ -44,6 +44,10 @@ has not changed, but code such as the following:
 should be changed either to use 'return' instead of 'exit', or to have
 the try/always block outside of any function.
 
+If the shell is invoked as sh or ksh (through a symlink or with the
+'--emulate' option), the 'HISTCHARS' parameter is now available instead
+of 'histchars', and the 'signals' parameter is not initialised.
+
 Changes from 5.6.2 to 5.7.1
 ---------------------------
 
diff --git a/Src/params.c b/Src/params.c
index 863b32600..699dc7ccd 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -309,7 +309,7 @@ IPDEF1("TTYIDLE", ttyidle_gsu, PM_READONLY_SPECIAL),
 #define IPDEF2(A,B,C) {{NULL,A,PM_SCALAR|PM_SPECIAL|C},BR(NULL),GSU(B),0,0,NULL,NULL,NULL,0}
 IPDEF2("USERNAME", username_gsu, PM_DONTIMPORT|PM_RESTRICTED),
 IPDEF2("-", dash_gsu, PM_READONLY_SPECIAL),
-IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT),
+IPDEF2("HISTCHARS", histchars_gsu, PM_DONTIMPORT),
 IPDEF2("HOME", home_gsu, PM_UNSET),
 IPDEF2("TERM", term_gsu, PM_UNSET),
 IPDEF2("TERMINFO", terminfo_gsu, PM_UNSET),
@@ -413,7 +413,7 @@ IPDEF8("MODULE_PATH", &module_path, "module_path", PM_DONTIMPORT|PM_RESTRICTED|P
 
 /* All of these have sh compatible equivalents.                */
 IPDEF1("ARGC", argc_gsu, PM_READONLY_SPECIAL),
-IPDEF2("HISTCHARS", histchars_gsu, PM_DONTIMPORT),
+IPDEF2("histchars", histchars_gsu, PM_DONTIMPORT),
 IPDEF4("status", &lastval),
 IPDEF7("prompt", &prompt),
 IPDEF7("PROMPT", &prompt),
@@ -935,8 +935,10 @@ createparamtable(void)
     setsparam("ZSH_ARGZERO", ztrdup(posixzero));
     setsparam("ZSH_VERSION", ztrdup_metafy(ZSH_VERSION));
     setsparam("ZSH_PATCHLEVEL", ztrdup_metafy(ZSH_PATCHLEVEL));
-    setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *)));
-    for (t = sigs; (*sigptr++ = ztrdup_metafy(*t++)); );
+    if (!EMULATION(EMULATE_SH|EMULATE_KSH)) {
+	setaparam("signals", sigptr = zalloc((SIGCOUNT+4) * sizeof(char *)));
+	for (t = sigs; (*sigptr++ = ztrdup_metafy(*t++)); );
+    }
 
     noerrs = 0;
 }

^ permalink raw reply	[relevance 3%]

* Re: [PATCH] sh/ksh init: don't initialise lowercase parameters
  2020-01-28 15:19  3% ` [PATCH] sh/ksh init: don't initialise lowercase parameters Martijn Dekker
@ 2020-01-28 15:26  0%   ` Peter Stephenson
  2020-01-29  8:49  3%   ` Daniel Shahaf
  1 sibling, 0 replies; 200+ results
From: Peter Stephenson @ 2020-01-28 15:26 UTC (permalink / raw)
  To: zsh-workers

On Tue, 2020-01-28 at 16:19 +0100, Martijn Dekker wrote:
> In POSIX sh and in ksh, it's the convention that lowercase variable 
> names are reserved for scripts, and uppercase names may be used by the 
> shell or system. So most lowercase zsh parameters are not initialised 
> when zsh is invoked as sh or ksh (through a symlink or the --emulate 
> option).
> 
> However, there are two left that are initialised in sh/ksh mode: 
> 'histchars' and 'signals'. So this can conflict with POSIX scripts.
> 
> Also, if zsh is invoked as sh or ksh, 'histchars' is available whereas 
> the 'HISTCHARS' equivalent is not. It seems quite obvious that this 
> should be the other way around.
> 
> The attached patch makes zsh, when invoked as sh or ksh, not initialise 
> 'signals', and initialise 'HISTCHARS' instead of 'histchars'. It also 
> updates the documentation.

That sounds fine to me --- as you know, general policy has always been
that emulation modes are there solely for compatibility with other
shells and backward compatibility is irrelevant, whereas native mode
is the other way round.

I'm guessing histchars got that way round in some kind of mistaken
link up with compatibility with csh, which tends to deal only
with lower case variables.  But that's just a guess, don't think
it really matters.

pws


^ permalink raw reply	[relevance 0%]

* Re: [PATCH] sh/ksh init: don't initialise lowercase parameters
  2020-01-28 15:19  3% ` [PATCH] sh/ksh init: don't initialise lowercase parameters Martijn Dekker
  2020-01-28 15:26  0%   ` Peter Stephenson
@ 2020-01-29  8:49  3%   ` Daniel Shahaf
  2020-01-29 18:36  0%     ` Bart Schaefer
  1 sibling, 1 reply; 200+ results
From: Daniel Shahaf @ 2020-01-29  8:49 UTC (permalink / raw)
  To: Martijn Dekker; +Cc: Zsh hackers list

Martijn Dekker wrote on Tue, Jan 28, 2020 at 16:19:41 +0100:
> In POSIX sh and in ksh, it's the convention that lowercase variable names
> are reserved for scripts, and uppercase names may be used by the shell or
> system.

I wouldn't mind having a test for this.  How about:

diff --git a/Test/B07emulate.ztst b/Test/B07emulate.ztst
index 7b1592fa9..28a381aab 100644
--- a/Test/B07emulate.ztst
+++ b/Test/B07emulate.ztst
@@ -276,3 +276,7 @@ F:Some reserved tokens are handled in alias expansion
 0:--emulate followed by other options
 >yes
 >no
+
+ $ZTST_testdir/../Src/zsh --emulate sh -fc 'typeset -pm "[a-z]*"'
+ $ZTST_testdir/../Src/zsh --emulate ksh -fc 'typeset -pm "[a-z]*"'
+0:sh/ksh emulations don't define lowercase variables

> +++ b/Doc/Zsh/compat.yo
> @@ -1173,8 +1173,8 @@ tt(histchars) to characters with a locale-dependent meaning will be
>  vindex(HISTCHARS)
> -item(tt(HISTCHARS) <S> <Z>)(
> -Same as tt(histchars).  (Deprecated.)
> +item(tt(HISTCHARS) <S>)(
> +Same as tt(histchars).
>  )

Why de-deprecate the uppercase spelling?  Do ksh or POSIX sh define "HISTCHARS"
in uppercase?  The lowercase spelling does seem to be a csh/tcsh thing.

Cheers,

Daniel

^ permalink raw reply	[relevance 3%]

* Re: [PATCH] sh/ksh init: don't initialise lowercase parameters
  2020-01-29  8:49  3%   ` Daniel Shahaf
@ 2020-01-29 18:36  0%     ` Bart Schaefer
  2020-01-29 21:35  0%       ` Daniel Shahaf
  0 siblings, 1 reply; 200+ results
From: Bart Schaefer @ 2020-01-29 18:36 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Martijn Dekker, Zsh hackers list

On Wed, Jan 29, 2020 at 2:50 AM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
>
> Why de-deprecate the uppercase spelling?  Do ksh or POSIX sh define "HISTCHARS"
> in uppercase?  The lowercase spelling does seem to be a csh/tcsh thing.

I would agree with removing "Deprecated" from the doc for HISTCHARS.
It's pretty clear at this point that we're not actually going to
remove it, and there needs to be one form of the variable even in
non-zsh modes for the feature to work, and we're not allowed to claim
the lower-case one.

^ permalink raw reply	[relevance 0%]

* Re: [PATCH] sh/ksh init: don't initialise lowercase parameters
  2020-01-29 18:36  0%     ` Bart Schaefer
@ 2020-01-29 21:35  0%       ` Daniel Shahaf
  0 siblings, 0 replies; 200+ results
From: Daniel Shahaf @ 2020-01-29 21:35 UTC (permalink / raw)
  To: zsh-workers; +Cc: Martijn Dekker

Bart Schaefer wrote on Wed, 29 Jan 2020 12:36 -0600:
> On Wed, Jan 29, 2020 at 2:50 AM Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> >
> > Why de-deprecate the uppercase spelling?  Do ksh or POSIX sh define "HISTCHARS"
> > in uppercase?  The lowercase spelling does seem to be a csh/tcsh thing.
> 
> I would agree with removing "Deprecated" from the doc for HISTCHARS.
> It's pretty clear at this point that we're not actually going to
> remove it, and there needs to be one form of the variable even in
> non-zsh modes for the feature to work, and we're not allowed to claim
> the lower-case one.

I see.  Yes, we can bless $HISTCHARS as the preferred spelling for
native mode and keep $histchars for csh compatibility…but doesn't this
imply that we should add a $SIGNALS variable (and possibly deprecate
$signals)?

^ permalink raw reply	[relevance 0%]

* Re: [PATCH ALTERNATE] builtins: kill: Do not set signal on current pgroup when pid is empty
  @ 2020-02-15 14:10  3%   ` Daniel Shahaf
  2020-02-15 15:15  0%     ` Chris Down
  0 siblings, 1 reply; 200+ results
From: Daniel Shahaf @ 2020-02-15 14:10 UTC (permalink / raw)
  To: Chris Down; +Cc: zsh-workers

Chris Down wrote on Sat, 15 Feb 2020 09:52 -0400:
> There are two ways to solve this issue:
> 
> 1. Add special handling to `kill` to avoid this case. See this patch[0]
>    for a version that does that.
> 2. Change how isanum behaves. Since the only two call sites that use it
>    both seem like they should handle the case where the input char array
>    is empty, that seems like a reasonable overall change to me, but
>    either works.

Thanks for the patch and the revisions.  I prefer #2.  If no one
objects (or says POSIX requires «kill ''» to be equivalent to
«kill 0»…) I'll apply it.

It would be nice to have a regression test for this.
Test/C03traps.ztst has the examples, but the test should be added to
a B*.ztst file (probably a new one).  Would you happen to have time to
look into this?  No worries if not.

Cheers,

Daniel

> After this patch:
> 
>     % trap 'exit 5' TERM
>     % kill ''
>     kill: illegal pid:
> 
> 0: https://www.zsh.org/mla/workers/2020/msg00251.html
> ---
>  Src/jobs.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/Src/jobs.c b/Src/jobs.c
> index e7438251e..0485f2c7c 100644
> --- a/Src/jobs.c
> +++ b/Src/jobs.c
> @@ -1854,13 +1854,14 @@ scanjobs(void)
>  
>  /* This simple function indicates whether or not s may represent      *
>   * a number.  It returns true iff s consists purely of digits and     *
> - * minuses.  Note that minus may appear more than once, and the empty *
> - * string will produce a `true' response.                             */
> + * minuses.  Note that minus may appear more than once.               */
>  
>  /**/
>  static int
>  isanum(char *s)
>  {
> +    if (*s == '\0')
> +	return 0;
>      while (*s == '-' || idigit(*s))
>  	s++;
>      return *s == '\0';


^ permalink raw reply	[relevance 3%]

* Re: [PATCH ALTERNATE] builtins: kill: Do not set signal on current pgroup when pid is empty
  2020-02-15 14:10  3%   ` Daniel Shahaf
@ 2020-02-15 15:15  0%     ` Chris Down
  0 siblings, 0 replies; 200+ results
From: Chris Down @ 2020-02-15 15:15 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: zsh-workers

Daniel Shahaf writes:
>Chris Down wrote on Sat, 15 Feb 2020 09:52 -0400:
>> There are two ways to solve this issue:
>>
>> 1. Add special handling to `kill` to avoid this case. See this patch[0]
>>    for a version that does that.
>> 2. Change how isanum behaves. Since the only two call sites that use it
>>    both seem like they should handle the case where the input char array
>>    is empty, that seems like a reasonable overall change to me, but
>>    either works.
>
>Thanks for the patch and the revisions.  I prefer #2.  If no one
>objects (or says POSIX requires «kill ''» to be equivalent to
>«kill 0»…) I'll apply it.

Sure thing, thanks.

>It would be nice to have a regression test for this.
>Test/C03traps.ztst has the examples, but the test should be added to
>a B*.ztst file (probably a new one).  Would you happen to have time to
>look into this?  No worries if not.

No worries, I've got time. I'll send v2 in a little while with the updates.

Thanks for the review!

Chris

^ permalink raw reply	[relevance 0%]

* Re: [BUG] Issue with set built-in in 5.8 (?)
  @ 2020-02-17  9:02  4% ` Daniel Shahaf
  2020-02-18 20:01  3%   ` dana
  0 siblings, 1 reply; 200+ results
From: Daniel Shahaf @ 2020-02-17  9:02 UTC (permalink / raw)
  To: dana; +Cc: Zsh hackers list

dana wrote on Sun, 16 Feb 2020 20:19 -0600:
> Most of these errors are useful, but i'm not sure they should unconditionally
> abort the shell.
> @Daniel, we talked about the first one before, but this particular concern
> didn't come up at the time — what do you reckon?

Considerations:

- Compatibility with what zshoptions(1) has promised would work, though
  that didn't work before 5.8.

- Consistency with POSIX.  (POSIX doesn't specify -p, but still.)

- Consistency with other shells.

- Consistency between «set» and «setopt».

- On the one hand, "Errors should never pass silently".  On the other
  hand, in the shell language there are few other cases of aborting the
  shell just because a syscall returned an error.

- Regardless of what we choose, the other behaviour is achievable: if
  we make the error fatal people can use «eval» to make it non-fatal,
  and if we make the error non-fatal people can use «… || exit 1» to
  make it fatal.

I'm not sure what these add up to, but these are the addends I have
in mind.

Cheers,

Daniel


> % sudo perl -e '$< = 1; $> = 2; exec("zsh", "-fc", "id; unsetopt privileged; echo still here");'
> uid=1(daemon) gid=1(daemon) euid=2 egid=0(wheel) groups=...
> zsh:unsetopt:1: PRIVILEGED: supplementary group list not changed due to lack of permissions: EUID=2
> zsh:unsetopt:1: can't change option: privileged
> still here
> 
> % sudo perl -e '$< = 1; $> = 2; exec("zsh", "-fc", "id; set +p; echo still here");'
> uid=1(daemon) gid=1(daemon) euid=2 egid=0(wheel) groups=...
> zsh:unsetopt:1: PRIVILEGED: supplementary group list not changed due to lack of permissions: EUID=2
> zsh:set:1: can't change option: -p
> 


^ permalink raw reply	[relevance 4%]

* Re: [BUG] Issue with set built-in in 5.8 (?)
  2020-02-17  9:02  4% ` Daniel Shahaf
@ 2020-02-18 20:01  3%   ` dana
  2020-02-19  9:37  3%     ` Peter Stephenson
  0 siblings, 1 reply; 200+ results
From: dana @ 2020-02-18 20:01 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Zsh hackers list

On 17 Feb 2020, at 03:02, Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> - Compatibility with what zshoptions(1) has promised would work, though
>   that didn't work before 5.8.

zshoptions(1) doesn't say anything about set aborting the shell, AFAIK. Only
zshbuiltins(1) does (under setopt, strangely), and it only states that a 'bad
option name' does it — nothing about any implicit errors that might occur when
changing a valid option.

On 17 Feb 2020, at 03:02, Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> - Consistency with POSIX.  (POSIX doesn't specify -p, but still.)
> - Consistency with other shells.

I can't even find where in POSIX it says that set should abort the shell. dash
and ksh93 do work that way, but bash and yash don't.

On 17 Feb 2020, at 03:02, Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> - Regardless of what we choose, the other behaviour is achievable: if
>   we make the error fatal people can use «eval» to make it non-fatal,
>   and if we make the error non-fatal people can use «… || exit 1» to
>   make it fatal.

Of the two, the latter seems more intuitive

dana


^ permalink raw reply	[relevance 3%]

* Re: [BUG] Issue with set built-in in 5.8 (?)
  2020-02-18 20:01  3%   ` dana
@ 2020-02-19  9:37  3%     ` Peter Stephenson
  2020-02-19 19:25  0%       ` dana
  0 siblings, 1 reply; 200+ results
From: Peter Stephenson @ 2020-02-19  9:37 UTC (permalink / raw)
  To: zsh-workers

On Tue, 2020-02-18 at 14:01 -0600, dana wrote:
> On 17 Feb 2020, at 03:02, Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> > 
> > - Regardless of what we choose, the other behaviour is achievable: if
> >   we make the error fatal people can use «eval» to make it non-fatal,
> >   and if we make the error non-fatal people can use «… || exit 1» to
> >   make it fatal.
> Of the two, the latter seems more intuitive

I would say so, too.  We don't have *that* many fatal errors for settings,
I don't think.

Tracing through POSIX to get an exact answer can be a bit of a pain;
some wording somewhere implies some particular case at another
point not explicitly cross-referenced etc. etc.

pws


^ permalink raw reply	[relevance 3%]

* Re: [BUG] Issue with set built-in in 5.8 (?)
  2020-02-19  9:37  3%     ` Peter Stephenson
@ 2020-02-19 19:25  0%       ` dana
  2020-02-20  9:30  3%         ` Peter Stephenson
  0 siblings, 1 reply; 200+ results
From: dana @ 2020-02-19 19:25 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: Zsh hackers list, Daniel Shahaf

On 19 Feb 2020, at 03:37, Peter Stephenson <p.stephenson@samsung.com> wrote:
> Tracing through POSIX to get an exact answer can be a bit of a pain

I found it:

  https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_01

When encountering a 'special built-in utility error', a non-interactive shell
'shall exit'. The documentation for the set built-in doesn't seem to
anticipate any errors besides those related to option-parsing, but i guess the
'letter of the law' is clear; if we were going to follow it strictly, we'd
leave set the way it is.

idk. On balance, maybe we should just let it be until someone complains (which
probably won't happen). Otherwise, with all of the weird variables in this
code path, we could be tinkering with it until next February...

dana


^ permalink raw reply	[relevance 0%]

* Re: [BUG] Issue with set built-in in 5.8 (?)
  2020-02-19 19:25  0%       ` dana
@ 2020-02-20  9:30  3%         ` Peter Stephenson
  0 siblings, 0 replies; 200+ results
From: Peter Stephenson @ 2020-02-20  9:30 UTC (permalink / raw)
  To: Zsh Hackers' List

On Wed, 2020-02-19 at 13:25 -0600, dana wrote:
> On 19 Feb 2020, at 03:37, Peter Stephenson <p.stephenson@samsung.com> wrote:
> > 
> > Tracing through POSIX to get an exact answer can be a bit of a pain
> I found it:
> 
>   https://protect2.fireeye.com/url?k=ce2973a8-93fdceec-ce28f8e7-0cc47a31381a-9cb7538620366aa8&u=https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_01
> 
> When encountering a 'special built-in utility error', a non-interactive shell
> 'shall exit'. The documentation for the set built-in doesn't seem to
> anticipate any errors besides those related to option-parsing, but i guess the
> 'letter of the law' is clear; if we were going to follow it strictly, we'd
> leave set the way it is.
> 
> idk. On balance, maybe we should just let it be until someone complains (which
> probably won't happen). Otherwise, with all of the weird variables in this
> code path, we could be tinkering with it until next February...

If this is behaviour of set that's not itself covered by POSIX, it's
definitely not so clear, and yes, I agree in practice we very likely
get away with it.

We have the possibility of covering variant behaviour with the
POSIX_BUILTINS option if it's important enough.

pws


^ permalink raw reply	[relevance 3%]

* Re: [PATCH] find RLIM_NLIMITS correctly on Cygwin
  @ 2020-02-25  9:38  4%                   ` Jun T
  0 siblings, 0 replies; 200+ results
From: Jun T @ 2020-02-25  9:38 UTC (permalink / raw)
  To: zsh-workers

Sorry for a *very* slow response; I've been quite busy these days.
Finally I found some time to work on this.

The patch below removes rlimits.{awk,h} and introduces a table of
known resources in rlimits.c. All the "#ifdef HAVE_RLIMIT_XXX"s
are now in the initialization of this table.

But removing rlimits.awk has one drawback:
Before the patch, unknown resource can be detected in the build
time (by the special make rule defined in rlimits.mdd).
With the patch, we can notice unknown resouce(s) only when running
the limit (or ulimit) builtin. We may hope that a user who sees
UNKNOWN in the output of limit/ulimit will report it to us, or
add a simple test like

limit | grep UNKNOWN || print OK

somewhere in the test (but in which .ztst file?).


 .gitignore               |   1 -
 Src/Builtins/rlimits.awk | 116 --------
 Src/Builtins/rlimits.c   | 692 ++++++++++++++++++-----------------------------
 Src/Builtins/rlimits.mdd |  15 -
 configure.ac             |   1 +
 5 files changed, 264 insertions(+), 561 deletions(-)


diff --git a/.gitignore b/.gitignore
index e46f8517e..ec2f56642 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,7 +123,6 @@ Src/Builtins/*.mdh
 Src/Builtins/*.mdhi
 Src/Builtins/*.mdhs
 Src/Builtins/*.mdh.tmp
-Src/Builtins/rlimits.h
 
 Src/Modules/Makefile.in
 Src/Modules/*.export
diff --git a/Src/Builtins/rlimits.awk b/Src/Builtins/rlimits.awk
deleted file mode 100644
index e9c576c66..000000000
--- a/Src/Builtins/rlimits.awk
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# rlimits.awk: {g,n}awk script to generate rlimits.h
-#
-# NB: On SunOS 4.1.3 - user-functions don't work properly, also \" problems
-# Without 0 + hacks some nawks compare numbers as strings
-#
-BEGIN {limidx = 0}
-
-/^[\t ]*(#[\t ]*define[\t _]*RLIMIT_[A-Z_]*[\t ]*[0-9][0-9]*|RLIMIT_[A-Z_]*,[\t ]*|_*RLIMIT_[A-Z_]*[\t ]*=[\t ]*[0-9][0-9]*,[\t ]*)/ {
-    limindex = index($0, "RLIMIT_")
-    limtail = substr($0, limindex, 80)
-    split(limtail, tmp)
-    limnam = substr(tmp[1], 8, 20)
-    limnum = tmp[2]
-    # in this case I assume GNU libc resourcebits.h
-    if (limnum == "") {
-	limnum = limidx++
-	limindex = index($0, ",")
-	limnam = substr(limnam, 1, limindex-1)
-    }
-    if (limnum == "=") {
-	if (tmp[3] ~ /^[0-9]/) {
-	    limnum = tmp[3] + 0
-	} else {
-	    limnum = limidx++
-	}
-	limindex = index($0, ",")
-	limnam = substr(limnam, 1, limindex-1)
-    }
-    limrev[limnam] = limnum
-    if (lim[limnum] == "") {
-	lim[limnum] = limnam
-	if (limnum ~ /^[0-9]*$/) {
-	    if (limnam == "AIO_MEM") { msg[limnum] = "Maiomemorylocked" }
-	    if (limnam == "AIO_OPS") { msg[limnum] = "Naiooperations" }
-	    if (limnam == "AS")      { msg[limnum] = "Maddressspace" }
-	    if (limnam == "CORE")    { msg[limnum] = "Mcoredumpsize" }
-	    if (limnam == "CPU")     { msg[limnum] = "Tcputime" }
-	    if (limnam == "DATA")    { msg[limnum] = "Mdatasize" }
-	    if (limnam == "FSIZE")   { msg[limnum] = "Mfilesize" }
-	    if (limnam == "LOCKS")   { msg[limnum] = "Nmaxfilelocks" }
-	    if (limnam == "MEMLOCK") { msg[limnum] = "Mmemorylocked" }
-	    if (limnam == "NOFILE")  { msg[limnum] = "Ndescriptors" }
-	    if (limnam == "NPROC")   { msg[limnum] = "Nmaxproc" }
-	    if (limnam == "NTHR")    { msg[limnum] = "Nmaxpthreads" }
-	    if (limnam == "OFILE")   { msg[limnum] = "Ndescriptors" }
-	    if (limnam == "PTHREAD") { msg[limnum] = "Nmaxpthreads" }
-	    if (limnam == "RSS")     { msg[limnum] = "Mresident" }
-	    if (limnam == "SBSIZE")  { msg[limnum] = "Msockbufsize" }
-	    if (limnam == "STACK")   { msg[limnum] = "Mstacksize" }
-	    if (limnam == "TCACHE")  { msg[limnum] = "Ncachedthreads" }
-	    if (limnam == "VMEM")    { msg[limnum] = "Mvmemorysize" }
-	    if (limnam == "SIGPENDING") { msg[limnum] = "Nsigpending" }
-	    if (limnam == "MSGQUEUE") { msg[limnum] = "Nmsgqueue" }
-	    if (limnam == "NICE") { msg[limnum] = "Nnice" }
-	    if (limnam == "RTPRIO") { msg[limnum] = "Nrt_priority" }
-	    if (limnam == "RTTIME") { msg[limnum] = "Urt_time" }
-	    if (limnam == "POSIXLOCKS") { msg[limnum] = "Nposixlocks" }
-	    if (limnam == "NPTS")    { msg[limnum] = "Npseudoterminals" }
-	    if (limnam == "SWAP")    { msg[limnum] = "Mswapsize" }
-	    if (limnam == "KQUEUES") { msg[limnum] = "Nkqueues" }
-	    if (limnam == "UMTXP")   { msg[limnum] = "Numtxp" }
-        }
-    }
-}
-/^[\t ]*#[\t ]*define[\t _]*RLIM_NLIMITS[\t ]*[0-9][0-9]*/ {
-    limindex = index($0, "RLIM_")
-    limtail = substr($0, limindex, 80)
-    split(limtail, tmp)
-    nlimits = tmp[2]
-}
-# in case of GNU libc
-/^[\t ]*RLIM_NLIMITS[\t ]*=[\t ]*RLIMIT_NLIMITS/ {
-    if(!nlimits) { nlimits = limidx }
-}
-/^[\t _]*RLIM(IT)?_NLIMITS[\t ]*=[\t ]*[0-9][0-9]*/ {
-    limindex = index($0, "=")
-    limtail = substr($0, limindex, 80)
-    split(limtail, tmp)
-    nlimits = tmp[2]
-}
-
-END {
-    if (limrev["MEMLOCK"] != "") {
-        irss = limrev["RSS"]
-        msg[irss] = "Mmemoryuse"
-    }
-    ps = "%s"
-
-    printf("%s\n%s\n\n", "/** rlimits.h                              **/", "/** architecture-customized limits for zsh **/")
-    printf("#define ZSH_NLIMITS %d\n\nstatic char const *recs[ZSH_NLIMITS] = {\n", 0 + nlimits)
-
-    for (i = 0; i < 0 + nlimits; i++)
-	if (msg[i] == "")
-            printf("\t%c%s%c,\n", 34, lim[i], 34)
-	else
-	    printf("\t%c%s%c,\n", 34, substr(msg[i], 2, 30), 34)
-    print "};"
-    print ""
-    print "static int limtype[ZSH_NLIMITS] = {"
-    for (i = 0; i < 0 + nlimits; i++) {
-	if (msg[i] == "")
-	    limtype = "UNKNOWN"
-	else {
-	    limtype = substr(msg[i], 1, 1)
-	    if(limtype == "M") { limtype = "MEMORY" }
-	    if(limtype == "N") { limtype = "NUMBER" }
-	    if(limtype == "T") { limtype = "TIME" }
-	    if(limtype == "U") { limtype = "MICROSECONDS" }
-	}
-	printf("\tZLIMTYPE_%s,\n", limtype)
-    }
-    print "};"
-
-    exit(0)
-}
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 6b552f3a9..e9fa77152 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -32,20 +32,7 @@
 
 #if defined(HAVE_GETRLIMIT) && defined(RLIM_INFINITY)
 
-#if defined(HAVE_RLIMIT_POSIXLOCKS) && !defined(HAVE_RLIMIT_LOCKS)
-#  define RLIMIT_LOCKS		RLIMIT_POSIXLOCKS
-#  define HAVE_RLIMIT_LOCKS     1
-#endif
-
-#if defined(HAVE_RLIMIT_NTHR) && !defined(HAVE_RLIMIT_PTHREAD)
-#  define RLIMIT_PTHREAD	RLIMIT_NTHR
-#  define HAVE_RLIMIT_PTHREAD   1
-#  define THREAD_FMT            "-T: threads                         "
-#else
-#  define THREAD_FMT            "-T: threads per process             "
-#endif
-
-enum {
+enum zlimtype {
     ZLIMTYPE_MEMORY,
     ZLIMTYPE_NUMBER,
     ZLIMTYPE_TIME,
@@ -53,11 +40,214 @@ enum {
     ZLIMTYPE_UNKNOWN
 };
 
-/* Generated rec array containing limits required for the limit builtin.     *
- * They must appear in this array in numerical order of the RLIMIT_* macros. */
+typedef struct {
+    int	res;		/* RLIMIT_XXX */
+    char* name;		/* used by limit builtin */
+    enum zlimtype type;
+    int unit;		/* 1, 512, or 1024 */
+    char opt;		/* option character */
+    char* descr;	/* used by ulimit builtin */
+} resinfo_t;
+
+/* table of known resources */
+static resinfo_t known_resources[] = {
+    {RLIMIT_CPU, "cputime", ZLIMTYPE_TIME, 1,
+		't', "cpu time (seconds)"},
+    {RLIMIT_FSIZE, "filesize", ZLIMTYPE_MEMORY, 512,
+		'f', "file size (blocks)"},
+    {RLIMIT_DATA, "datasize", ZLIMTYPE_MEMORY, 1024,
+		'd', "data seg size (kbytes)"},
+    {RLIMIT_STACK, "stacksize", ZLIMTYPE_MEMORY, 1024,
+		's', "stack size (kbytes)"},
+    {RLIMIT_CORE, "coredumpsize", ZLIMTYPE_MEMORY, 512,
+		'c', "core file size (blocks)"},
+# ifdef HAVE_RLIMIT_NOFILE
+    {RLIMIT_NOFILE, "descriptors", ZLIMTYPE_NUMBER, 1,
+		'n', "file descriptors"},
+# endif
+# if defined(HAVE_RLIMIT_AS) && !defined(RLIMIT_VMEM_IS_AS)
+    {RLIMIT_AS, "addressspace", ZLIMTYPE_MEMORY, 1024,
+		'v', "address space (kbytes)"},
+# endif
+# if defined(HAVE_RLIMIT_RSS) && !defined(RLIMIT_VMEM_IS_RSS) && !defined(RLIMIT_RSS_IS_AS)
+    {RLIMIT_RSS, "resident", ZLIMTYPE_MEMORY, 1024,
+		'm', "resident set size (kbytes)"},
+# endif
+# if defined(HAVE_RLIMIT_VMEM)
+    {RLIMIT_VMEM,
+#  if defined(RLIMIT_VMEM_IS_RSS)
+		 "resident", ZLIMTYPE_MEMORY, 1024,
+		 'm', "memory size (kbytes)"},
+#  else
+		 "vmemorysize", ZLIMTYPE_MEMORY, 1024,
+		 'v', "virtual memory size (kbytes)"},
+#  endif
+# endif
+# ifdef HAVE_RLIMIT_NPROC
+    {RLIMIT_NPROC, "maxproc", ZLIMTYPE_NUMBER, 1,
+		'u', "processes"},
+# endif
+# ifdef HAVE_RLIMIT_MEMLOCK
+    {RLIMIT_MEMLOCK, "memorylocked", ZLIMTYPE_MEMORY, 1024,
+		'l', "locked-in-memory size (kbytes)"},
+# endif
+    /* Linux */
+# ifdef HAVE_RLIMIT_LOCKS
+    {RLIMIT_LOCKS, "maxfilelocks", ZLIMTYPE_NUMBER, 1,
+		'x', "file locks"},
+# endif
+# ifdef HAVE_RLIMIT_SIGPENDING
+    {RLIMIT_SIGPENDING, "sigpending", ZLIMTYPE_NUMBER, 1,
+		'i', "pending signals"},
+# endif
+# ifdef HAVE_RLIMIT_MSGQUEUE
+    {RLIMIT_MSGQUEUE, "msgqueue", ZLIMTYPE_NUMBER, 1,
+		'q', "bytes in POSIX msg queues"},
+# endif
+# ifdef HAVE_RLIMIT_NICE
+    {RLIMIT_NICE, "nice", ZLIMTYPE_NUMBER, 1,
+		'e', "max nice"},
+# endif
+# ifdef HAVE_RLIMIT_RTPRIO
+    {RLIMIT_RTPRIO, "rt_priority", ZLIMTYPE_NUMBER, 1,
+		'r', "max rt priority"},
+# endif
+# ifdef HAVE_RLIMIT_RTTIME
+    {RLIMIT_RTTIME, "rt_time", ZLIMTYPE_MICROSECONDS, 1,
+		'N', "rt cpu time (microseconds)"},
+# endif
+    /* BSD */
+# ifdef HAVE_RLIMIT_SBSIZE
+    {RLIMIT_SBSIZE, "sockbufsize", ZLIMTYPE_MEMORY, 1,
+		'b', "socket buffer size (bytes)"},
+# endif
+# ifdef HAVE_RLIMIT_KQUEUES /* FreeBSD */
+    {RLIMIT_KQUEUES, "kqueues", ZLIMTYPE_NUMBER, 1,
+		'k', "kqueues"},
+# endif
+# ifdef HAVE_RLIMIT_NPTS    /* FreeBSD */
+    {RLIMIT_NPTS, "pseudoterminals", ZLIMTYPE_NUMBER, 1,
+		'p', "pseudo-terminals"},
+# endif
+# ifdef HAVE_RLIMIT_SWAP    /* FreeBSD */
+    {RLIMIT_SWAP, "swapsize", ZLIMTYPE_MEMORY, 1024,
+		'w', "swap size (kbytes)"},
+# endif
+# ifdef HAVE_RLIMIT_UMTXP   /* FreeBSD */
+    {RLIMIT_UMTXP, "umtxp", ZLIMTYPE_NUMBER, 1,
+		'o', "umtx shared locks"},
+# endif
+
+# ifdef HAVE_RLIMIT_POSIXLOCKS	/* DragonFly */
+    {RLIMIT_POSIXLOCKS, "posixlocks", ZLIMTYPE_NUMBER, 1,
+		'x', "number of POSIX locks"},
+# endif
+# if defined(HAVE_RLIMIT_NTHR) && !defined(HAVE_RLIMIT_RTPRIO) /* Net/OpenBSD */
+    {RLIMIT_NTHR, "maxpthreads", ZLIMTYPE_NUMBER, 1,
+		'r', "threads"},
+# endif
+    /* others */
+# if defined(HAVE_RLIMIT_PTHREAD) && !defined(HAVE_RLIMIT_NTHR)	/* IRIX ? */
+    {RLIMIT_PTHREAD, "maxpthreads", ZLIMTYPE_NUMBER, 1,
+		'T', "threads per process"},
+# endif
+# ifdef HAVE_RLIMIT_AIO_MEM /* HP-UX ? */
+    {RLIMIT_AIO_MEM, "aiomemorylocked", ZLIMTYPE_MEMORY, 1024,
+		'N', "AIO locked-in-memory (kbytes)"},
+# endif
+# ifdef HAVE_RLIMIT_AIO_OPS /* HP-UX ? */
+    {RLIMIT_AIO_OPS, "aiooperations", ZLIMTYPE_NUMBER, 1,
+		'N', "AIO operations"},
+# endif
+# ifdef HAVE_RLIMIT_TCACHE  /* HP-UX ? */
+    {RLIMIT_TCACHE, "cachedthreads", ZLIMTYPE_NUMBER, 1,
+		'N', "cached threads"},
+# endif
+};
 
-# include "rlimits.h"
+/* resinfo[RLIMIT_XXX] points to the corresponding entry
+ * in known_resources[] */
+static resinfo_t **resinfo;
 
+/**/
+static void
+set_resinfo(void)
+{
+    int i;
+
+    resinfo = (resinfo_t **)zshcalloc(RLIM_NLIMITS*sizeof(resinfo_t *));
+
+    for (i=0; i<sizeof(known_resources)/sizeof(resinfo_t); ++i) {
+	resinfo[known_resources[i].res] = &known_resources[i];
+    }
+    for (i=0; i<RLIM_NLIMITS; ++i) {
+	if (!resinfo[i]) {
+	    /* unknown resource */
+	    resinfo_t *info = (resinfo_t *)zshcalloc(sizeof(resinfo_t));
+	    char *buf = (char *)zalloc(12);
+	    snprintf(buf, 12, "UNKNOWN-%d", i);
+	    info->res = - 1;	/* negative value indicates "unknown" */
+	    info->name = buf;
+	    info->type = ZLIMTYPE_UNKNOWN;
+	    info->unit = 1;
+	    info->opt = 'N';
+	    info->descr = buf;
+	    resinfo[i] = info;
+	}
+    }
+}
+
+/**/
+static void
+free_resinfo(void)
+{
+    int i;
+    for (i=0; i<RLIM_NLIMITS; ++i) {
+	if (resinfo[i]->res < 0) {  /* unknown resource */
+	    free(resinfo[i]->name);
+	    free(resinfo[i]);
+	}
+    }
+    free(resinfo);
+    resinfo = NULL;
+}
+
+/* Find resource by its option character */
+
+/**/
+static int
+find_resource(char c)
+{
+    int i;
+    for (i=0; i<RLIM_NLIMITS; ++i) {
+	if (resinfo[i]->opt == c)
+	    return i;
+    }
+    return -1;
+}
+
+/* Print a value of type rlim_t */
+
+/**/
+static void
+printrlim(rlim_t val, const char *unit)
+{
+# ifdef RLIM_T_IS_QUAD_T
+	printf("%qd%s", val, unit);
+# else
+#  ifdef RLIM_T_IS_LONG_LONG
+	printf("%lld%s", val, unit);
+#  else
+#   ifdef RLIM_T_IS_UNSIGNED
+	printf("%lu%s", (unsigned long)val, unit);
+#   else
+	printf("%ld%s", (long)val, unit);
+#   endif /* RLIM_T_IS_UNSIGNED */
+#  endif /* RLIM_T_IS_LONG_LONG */
+# endif /* RLIM_T_IS_QUAD_T */
+}
+
+/**/
 static rlim_t
 zstrtorlimt(const char *s, char **t, int base)
 {
@@ -97,8 +287,8 @@ static void
 showlimitvalue(int lim, rlim_t val)
 {
     /* display limit for resource number lim */
-    if (lim < ZSH_NLIMITS)
-	printf("%-16s", recs[lim]);
+    if (lim < RLIM_NLIMITS)
+	printf("%-16s", resinfo[lim]->name);
     else
     {
 	/* Unknown limit, hence unknown units. */
@@ -106,81 +296,25 @@ showlimitvalue(int lim, rlim_t val)
     }
     if (val == RLIM_INFINITY)
 	printf("unlimited\n");
-    else if (lim >= ZSH_NLIMITS)
-    {
-# ifdef RLIM_T_IS_QUAD_T
-	printf("%qd\n", val);
-# else
-#  ifdef RLIM_T_IS_LONG_LONG
-	printf("%lld\n", val);
-#  else
-#   ifdef RLIM_T_IS_UNSIGNED
-	printf("%lu\n", (unsigned long)val);
-#   else
-	printf("%ld\n", (long)val);
-#   endif /* RLIM_T_IS_UNSIGNED */
-#  endif /* RLIM_T_IS_LONG_LONG */
-# endif /* RLIM_T_IS_QUAD_T */
-    }
-    else if (limtype[lim] == ZLIMTYPE_TIME) {
+    else if (lim >= RLIM_NLIMITS)
+	printrlim(val, "\n");
+    else if (resinfo[lim]->type == ZLIMTYPE_TIME) {
 	/* time-type resource -- display as hours, minutes and
 	   seconds. */
 	printf("%d:%02d:%02d\n", (int)(val / 3600),
 	       (int)(val / 60) % 60, (int)(val % 60));
-    } else if (limtype[lim] == ZLIMTYPE_MICROSECONDS) {
-	/* microseconds */
-# ifdef RLIM_T_IS_QUAD_T
-	printf("%qdus\n", val);
-# else
-#  ifdef RLIM_T_IS_LONG_LONG
-	printf("%lldus\n", val);
-#  else
-#   ifdef RLIM_T_IS_UNSIGNED
-	printf("%luus\n", (unsigned long)val);
-#   else
-	printf("%ldus\n", (long)val);
-#   endif /* RLIM_T_IS_UNSIGNED */
-#  endif /* RLIM_T_IS_LONG_LONG */
-# endif /* RLIM_T_IS_QUAD_T */
-    } else if (limtype[lim] == ZLIMTYPE_NUMBER ||
-	       limtype[lim] == ZLIMTYPE_UNKNOWN) {
-	/* pure numeric resource */
-# ifdef RLIM_T_IS_QUAD_T
-	printf("%qd\n", val);
-# else
-#  ifdef RLIM_T_IS_LONG_LONG
-	printf("%lld\n", val);
-#  else
-#   ifdef RLIM_T_IS_UNSIGNED
-	printf("%lu\n", (unsigned long)val);
-#   else
-	printf("%ld\n", (long)val);
-#   endif /* RLIM_T_IS_UNSIGNED */
-#  endif /* RLIM_T_IS_LONG_LONG */
-# endif /* RLIM_T_IS_QUAD_T */
-    } else if (val >= 1024L * 1024L)
-	/* memory resource -- display with `K' or `M' modifier */
-# ifdef RLIM_T_IS_QUAD_T
-	printf("%qdMB\n", val / (1024L * 1024L));
-    else
-	printf("%qdkB\n", val / 1024L);
-# else
-#  ifdef RLIM_T_IS_LONG_LONG
-	printf("%lldMB\n", val / (1024L * 1024L));
-    else
-	printf("%lldkB\n", val / 1024L);
-#  else
-#   ifdef RLIM_T_IS_UNSIGNED
-    printf("%luMB\n", (unsigned long)(val / (1024L * 1024L)));
-    else
-	printf("%lukB\n", (unsigned long)(val / 1024L));
-#   else
-    printf("%ldMB\n", (long)val / (1024L * 1024L));
-    else
-	printf("%ldkB\n", (long)val / 1024L);
-#   endif /* RLIM_T_IS_UNSIGNED */
-#  endif /* RLIM_T_IS_LONG_LONG */
-# endif /* RLIM_T_IS_QUAD_T */
+    } else if (resinfo[lim]->type == ZLIMTYPE_MICROSECONDS)
+	printrlim(val, "us\n");	/* microseconds */
+    else if (resinfo[lim]->type == ZLIMTYPE_NUMBER ||
+	       resinfo[lim]->type == ZLIMTYPE_UNKNOWN)
+	printrlim(val, "\n");	/* pure numeric resource */
+    else {
+	/* memory resource -- display with `k' or `M' modifier */
+	if (val >= 1024L * 1024L)
+	    printrlim(val/(1024L * 1024L), "MB\n");
+	else
+	    printrlim(val/1024L, "kB\n");
+    }
 }
 
 /* Display resource limits.  hard indicates whether `hard' or `soft'  *
@@ -193,7 +327,7 @@ showlimits(char *nam, int hard, int lim)
 {
     int rt;
 
-    if (lim >= ZSH_NLIMITS)
+    if (lim >= RLIM_NLIMITS)
     {
 	/*
 	 * Not configured into the shell.  Ask the OS
@@ -215,7 +349,7 @@ showlimits(char *nam, int hard, int lim)
     else
     {
 	/* main loop over resource types */
-	for (rt = 0; rt != ZSH_NLIMITS; rt++)
+	for (rt = 0; rt != RLIM_NLIMITS; rt++)
 	    showlimitvalue(rt, (hard) ? limits[rt].rlim_max :
 			   limits[rt].rlim_cur);
     }
@@ -234,7 +368,7 @@ printulimit(char *nam, int lim, int hard, int head)
     rlim_t limit;
 
     /* get the limit in question */
-    if (lim >= ZSH_NLIMITS)
+    if (lim >= RLIM_NLIMITS)
     {
 	struct rlimit vals;
 
@@ -248,199 +382,25 @@ printulimit(char *nam, int lim, int hard, int head)
     else
 	limit = (hard) ? limits[lim].rlim_max : limits[lim].rlim_cur;
     /* display the appropriate heading */
-    switch (lim) {
-    case RLIMIT_CORE:
-	if (head)
-	    printf("-c: core file size (blocks)         ");
-	if (limit != RLIM_INFINITY)
-	    limit /= 512;
-	break;
-    case RLIMIT_DATA:
-	if (head)
-	    printf("-d: data seg size (kbytes)          ");
-	if (limit != RLIM_INFINITY)
-	    limit /= 1024;
-	break;
-    case RLIMIT_FSIZE:
-	if (head)
-	    printf("-f: file size (blocks)              ");
-	if (limit != RLIM_INFINITY)
-	    limit /= 512;
-	break;
-# ifdef HAVE_RLIMIT_SIGPENDING
-    case RLIMIT_SIGPENDING:
-	if (head)
-	    printf("-i: pending signals                 ");
-	break;
-# endif
-# ifdef HAVE_RLIMIT_MEMLOCK
-    case RLIMIT_MEMLOCK:
-	if (head)
-	    printf("-l: locked-in-memory size (kbytes)  ");
-	if (limit != RLIM_INFINITY)
-	    limit /= 1024;
-	break;
-# endif /* HAVE_RLIMIT_MEMLOCK */
-/* If RLIMIT_VMEM and RLIMIT_RSS are defined and equal, avoid *
- * duplicate case statement.  Observed on QNX Neutrino 6.1.0. */
-# if defined(HAVE_RLIMIT_RSS) && !defined(RLIMIT_VMEM_IS_RSS) && !defined(RLIMIT_RSS_IS_AS)
-    case RLIMIT_RSS:
-	if (head)
-	    printf("-m: resident set size (kbytes)      ");
-	if (limit != RLIM_INFINITY)
-	    limit /= 1024;
-	break;
-# endif /* HAVE_RLIMIT_RSS */
-# if defined(HAVE_RLIMIT_VMEM) && defined(HAVE_RLIMIT_RSS) && defined(RLIMIT_VMEM_IS_RSS)
-    case RLIMIT_VMEM:
-	if (head)
-	    printf("-m: memory size (kbytes)            ");
-	if (limit != RLIM_INFINITY)
-	    limit /= 1024;
-	break;
-# endif /* HAVE_RLIMIT_VMEM */
-# ifdef HAVE_RLIMIT_NOFILE
-    case RLIMIT_NOFILE:
-	if (head)
-	    printf("-n: file descriptors                ");
-	break;
-# endif /* HAVE_RLIMIT_NOFILE */
-# ifdef HAVE_RLIMIT_MSGQUEUE
-    case RLIMIT_MSGQUEUE:
-	if (head)
-	    printf("-q: bytes in POSIX msg queues       ");
-	break;
-# endif
-    case RLIMIT_STACK:
-	if (head)
-	    printf("-s: stack size (kbytes)             ");
-	if (limit != RLIM_INFINITY)
-	    limit /= 1024;
-	break;
-    case RLIMIT_CPU:
-	if (head)
-	    printf("-t: cpu time (seconds)              ");
-	break;
-# ifdef HAVE_RLIMIT_NPROC
-    case RLIMIT_NPROC:
-	if (head)
-	    printf("-u: processes                       ");
-	break;
-# endif /* HAVE_RLIMIT_NPROC */
-# if defined(HAVE_RLIMIT_VMEM) && (!defined(HAVE_RLIMIT_RSS) || !defined(RLIMIT_VMEM_IS_RSS))
-    case RLIMIT_VMEM:
-	if (head)
-	    printf("-v: virtual memory size (kbytes)    ");
-	if (limit != RLIM_INFINITY)
-	    limit /= 1024;
-	break;
-# endif /* HAVE_RLIMIT_VMEM */
-# if defined HAVE_RLIMIT_AS && !defined(RLIMIT_VMEM_IS_AS)
-    case RLIMIT_AS:
-	if (head)
-	    printf("-v: address space (kbytes)          ");
-	if (limit != RLIM_INFINITY)
-	    limit /= 1024;
-	break;
-# endif /* HAVE_RLIMIT_AS */
-# ifdef HAVE_RLIMIT_LOCKS
-    case RLIMIT_LOCKS:
-	if (head)
-	    printf("-x: file locks                      ");
-	break;
-# endif /* HAVE_RLIMIT_LOCKS */
-# ifdef HAVE_RLIMIT_AIO_MEM
-    case RLIMIT_AIO_MEM:
-	if (head)
-	    printf("-N %2d: AIO locked-in-memory (kbytes)", RLIMIT_AIO_MEM);
-	if (limit != RLIM_INFINITY)
-	    limit /= 1024;
-	break;
-# endif /* HAVE_RLIMIT_AIO_MEM */
-# ifdef HAVE_RLIMIT_AIO_OPS
-    case RLIMIT_AIO_OPS:
-	if (head)
-	    printf("-N %2d: AIO operations               ", RLIMIT_AIO_OPS);
-	break;
-# endif /* HAVE_RLIMIT_AIO_OPS */
-# ifdef HAVE_RLIMIT_TCACHE
-    case RLIMIT_TCACHE:
-	if (head)
-	    printf("-N %2d: cached threads               ", RLIMIT_TCACHE);
-	break;
-# endif /* HAVE_RLIMIT_TCACHE */
-# ifdef HAVE_RLIMIT_SBSIZE
-    case RLIMIT_SBSIZE:
-	if (head)
-	    printf("-b: socket buffer size (bytes)      ");
-	break;
-# endif /* HAVE_RLIMIT_SBSIZE */
-# ifdef HAVE_RLIMIT_PTHREAD
-    case RLIMIT_PTHREAD:
-	if (head)
-	    printf("%s", THREAD_FMT);
-	break;
-# endif /* HAVE_RLIMIT_PTHREAD */
-# ifdef HAVE_RLIMIT_NICE
-    case RLIMIT_NICE:
-	if (head)
-	    printf("-e: max nice                        ");
-	break;
-# endif /* HAVE_RLIMIT_NICE */
-# ifdef HAVE_RLIMIT_RTPRIO
-    case RLIMIT_RTPRIO:
-	if (head)
-	    printf("-r: max rt priority                 ");
-	break;
-# endif /* HAVE_RLIMIT_RTPRIO */
-# ifdef HAVE_RLIMIT_NPTS
-    case RLIMIT_NPTS:
-	if (head)
-	    printf("-p: pseudo-terminals                ");
-	break;
-# endif /* HAVE_RLIMIT_NPTS */
-# ifdef HAVE_RLIMIT_SWAP
-    case RLIMIT_SWAP:
-	if (head)
-	    printf("-w: swap size (kbytes)              ");
-	if (limit != RLIM_INFINITY)
-	    limit /= 1024;
-	break;
-# endif /* HAVE_RLIMIT_SWAP */
-# ifdef HAVE_RLIMIT_KQUEUES
-    case RLIMIT_KQUEUES:
-	if (head)
-	    printf("-k: kqueues                         ");
-	break;
-# endif /* HAVE_RLIMIT_KQUEUES */
-# ifdef HAVE_RLIMIT_UMTXP
-    case RLIMIT_UMTXP:
-	if (head)
-	    printf("-o: umtx shared locks               ");
-	break;
-# endif /* HAVE_RLIMIT_UMTXP */
-    default:
-	if (head)
-	    printf("-N %2d:                              ", lim);
-	break;
+    if (head) {
+	if (lim < RLIM_NLIMITS) {
+	    resinfo_t *info = resinfo[lim];
+	    if (info->opt == 'N')
+		printf("-N %2d: %-29s", lim, info->descr);
+	    else
+		printf("-%c: %-32s", info->opt, info->descr);
+	}
+	else
+	    printf("-N %2d: %-29s", lim, "");
     }
     /* display the limit */
     if (limit == RLIM_INFINITY)
 	printf("unlimited\n");
     else {
-# ifdef RLIM_T_IS_QUAD_T
-	printf("%qd\n", limit);
-# else
-#  ifdef RLIM_T_IS_LONG_LONG
-	printf("%lld\n", limit);
-#  else
-#   ifdef RLIM_T_IS_UNSIGNED
-	printf("%lu\n", (unsigned long)limit);
-#   else
-	printf("%ld\n", (long)limit);
-#   endif /* RLIM_T_IS_UNSIGNED */
-#  endif /* RLIM_T_IS_LONG_LONG */
-# endif /* RLIM_T_IS_QUAD_T */
+	if (lim < RLIM_NLIMITS)
+	    printrlim(limit/resinfo[lim]->unit, "\n");
+	else
+	    printrlim(limit, "\n");
     }
 
     return 0;
@@ -450,7 +410,7 @@ printulimit(char *nam, int lim, int hard, int head)
 static int
 do_limit(char *nam, int lim, rlim_t val, int hard, int soft, int set)
 {
-    if (lim >= ZSH_NLIMITS) {
+    if (lim >= RLIM_NLIMITS) {
 	struct rlimit vals;
 	if (getrlimit(lim, &vals) < 0)
 	{
@@ -558,8 +518,8 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
 	    lim = (int)zstrtol(s, NULL, 10);
 	}
 	else
-	    for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
-		if (!strncmp(recs[limnum], s, strlen(s))) {
+	    for (lim = -1, limnum = 0; limnum < RLIM_NLIMITS; limnum++)
+		if (!strncmp(resinfo[limnum]->name, s, strlen(s))) {
 		    if (lim != -1)
 			lim = -2;
 		    else
@@ -576,7 +536,7 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
 	/* without value for limit, display the current limit */
 	if (!(s = *argv++))
 	    return showlimits(nam, hard, lim);
-	if (lim >= ZSH_NLIMITS)
+	if (lim >= RLIM_NLIMITS)
 	{
 	    val = zstrtorlimt(s, &s, 10);
 	    if (*s)
@@ -586,7 +546,7 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
 		return 1;
 	    }
 	}
-	else if (limtype[lim] == ZLIMTYPE_TIME) {
+	else if (resinfo[lim]->type == ZLIMTYPE_TIME) {
 	    /* time-type resource -- may be specified as seconds, or minutes or *
 	     * hours with the `m' and `h' modifiers, and `:' may be used to add *
 	     * together more than one of these.  It's easier to understand from *
@@ -604,9 +564,9 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
 		    return 1;
 		}
 	    }
-	} else if (limtype[lim] == ZLIMTYPE_NUMBER ||
-		   limtype[lim] == ZLIMTYPE_UNKNOWN ||
-		   limtype[lim] == ZLIMTYPE_MICROSECONDS) {
+	} else if (resinfo[lim]->type == ZLIMTYPE_NUMBER ||
+		   resinfo[lim]->type == ZLIMTYPE_UNKNOWN ||
+		   resinfo[lim]->type == ZLIMTYPE_MICROSECONDS) {
 	    /* pure numeric resource -- only a straight decimal number is
 	    permitted. */
 	    char *t = s;
@@ -642,7 +602,7 @@ static int
 do_unlimit(char *nam, int lim, int hard, int soft, int set, int euid)
 {
     /* remove specified limit */
-    if (lim >= ZSH_NLIMITS) {
+    if (lim >= RLIM_NLIMITS) {
 	struct rlimit vals;
 	if (getrlimit(lim, &vals) < 0)
 	{
@@ -718,8 +678,8 @@ bin_unlimit(char *nam, char **argv, Options ops, UNUSED(int func))
 	    if (idigit(**argv)) {
 		lim = (int)zstrtol(*argv, NULL, 10);
 	    } else {
-		for (lim = -1, limnum = 0; limnum < ZSH_NLIMITS; limnum++)
-		    if (!strncmp(recs[limnum], *argv, strlen(*argv))) {
+		for (lim = -1, limnum = 0; limnum < RLIM_NLIMITS; limnum++)
+		    if (!strncmp(resinfo[limnum]->name, *argv, strlen(*argv))) {
 			if (lim != -1)
 			    lim = -2;
 			else
@@ -800,116 +760,14 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 		    resmask = (1 << RLIM_NLIMITS) - 1;
 		    nres = RLIM_NLIMITS;
 		    continue;
-		case 't':
-		    res = RLIMIT_CPU;
-		    break;
-		case 'f':
-		    res = RLIMIT_FSIZE;
-		    break;
-		case 'd':
-		    res = RLIMIT_DATA;
-		    break;
-		case 's':
-		    res = RLIMIT_STACK;
-		    break;
-		case 'c':
-		    res = RLIMIT_CORE;
-		    break;
-# ifdef HAVE_RLIMIT_SBSIZE
-		case 'b':
-		    res = RLIMIT_SBSIZE;
-		    break;
-# endif /* HAVE_RLIMIT_SBSIZE */
-# ifdef HAVE_RLIMIT_MEMLOCK
-		case 'l':
-		    res = RLIMIT_MEMLOCK;
-		    break;
-# endif /* HAVE_RLIMIT_MEMLOCK */
-# ifdef HAVE_RLIMIT_RSS
-		case 'm':
-		    res = RLIMIT_RSS;
-		    break;
-# endif /* HAVE_RLIMIT_RSS */
-# ifdef HAVE_RLIMIT_NOFILE
-		case 'n':
-		    res = RLIMIT_NOFILE;
-		    break;
-# endif /* HAVE_RLIMIT_NOFILE */
-# ifdef HAVE_RLIMIT_NPROC
-		case 'u':
-		    res = RLIMIT_NPROC;
-		    break;
-# endif /* HAVE_RLIMIT_NPROC */
-# if defined(HAVE_RLIMIT_VMEM) || defined(HAVE_RLIMIT_AS)
-		case 'v':
-#  ifdef HAVE_RLIMIT_VMEM
-		    res = RLIMIT_VMEM;
-#  else
-		    res = RLIMIT_AS;
-#  endif
-		    break;
-# endif /* HAVE_RLIMIT_VMEM */
-# ifdef HAVE_RLIMIT_LOCKS
-		case 'x':
-		    res = RLIMIT_LOCKS;
-		    break;
-# endif
-# ifdef HAVE_RLIMIT_SIGPENDING
-		case 'i':
-		    res = RLIMIT_SIGPENDING;
-		    break;
-# endif
-# ifdef HAVE_RLIMIT_MSGQUEUE
-		case 'q':
-		    res = RLIMIT_MSGQUEUE;
-		    break;
-# endif
-# ifdef HAVE_RLIMIT_NICE
-		case 'e':
-		    res = RLIMIT_NICE;
-		    break;
-# endif
-# ifdef HAVE_RLIMIT_RTPRIO
-		case 'r':
-		    res = RLIMIT_RTPRIO;
-		    break;
-# else
-#  ifdef HAVE_RLIMIT_NTHR
-		    /* For compatibility with sh on NetBSD */
-		case 'r':
-		    res = RLIMIT_NTHR;
-		    break;
-#  endif /* HAVE_RLIMIT_NTHR */
-# endif
-# ifdef HAVE_RLIMIT_NPTS
-		case 'p':
-		    res = RLIMIT_NPTS;
-		    break;
-# endif
-# ifdef HAVE_RLIMIT_SWAP
-		case 'w':
-		    res = RLIMIT_SWAP;
-		    break;
-# endif
-# ifdef HAVE_RLIMIT_KQUEUES
-		case 'k':
-		    res = RLIMIT_KQUEUES;
-		    break;
-# endif
-# ifdef HAVE_RLIMIT_PTHREAD
-		case 'T':
-		    res = RLIMIT_PTHREAD;
-		    break;
-# endif
-# ifdef HAVE_RLIMIT_UMTXP
-		case 'o':
-		    res = RLIMIT_UMTXP;
-		    break;
-# endif
 		default:
-		    /* unrecognised limit */
-		    zwarnnam(name, "bad option: -%c", *options);
-		    return 1;
+		    res = find_resource(*options);
+		    if (res < 0) {
+			/* unrecognised limit */
+			zwarnnam(name, "bad option: -%c", *options);
+			return 1;
+		    }
+		    break;
 		}
 		if (options[1]) {
 		    resmask |= 1 << res;
@@ -961,34 +819,8 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 		    return 1;
 		}
 		/* scale appropriately */
-		switch (res) {
-		case RLIMIT_FSIZE:
-		case RLIMIT_CORE:
-		    limit *= 512;
-		    break;
-		case RLIMIT_DATA:
-		case RLIMIT_STACK:
-# ifdef HAVE_RLIMIT_RSS
-		case RLIMIT_RSS:
-# endif /* HAVE_RLIMIT_RSS */
-# ifdef HAVE_RLIMIT_MEMLOCK
-		case RLIMIT_MEMLOCK:
-# endif /* HAVE_RLIMIT_MEMLOCK */
-/* If RLIMIT_VMEM and RLIMIT_RSS are defined and equal, avoid *
- * duplicate case statement.  Observed on QNX Neutrino 6.1.0. */
-# if defined(HAVE_RLIMIT_VMEM) && !defined(RLIMIT_VMEM_IS_RSS)
-		case RLIMIT_VMEM:
-# endif /* HAVE_RLIMIT_VMEM */
-/* ditto RLIMIT_VMEM and RLIMIT_AS */
-# if defined(HAVE_RLIMIT_AS) && !defined(RLIMIT_VMEM_IS_AS) && !defined(RLIMIT_RSS_IS_AS)
-		case RLIMIT_AS:
-# endif /* HAVE_RLIMIT_AS */
-# ifdef HAVE_RLIMIT_AIO_MEM
-		case RLIMIT_AIO_MEM:
-# endif /* HAVE_RLIMIT_AIO_MEM */
-		    limit *= 1024;
-		    break;
-		}
+		if (res < RLIM_NLIMITS)
+		    limit *= resinfo[res]->unit;
 	    }
 	    if (do_limit(name, res, limit, hard, soft, 1))
 		ret++;
@@ -1052,6 +884,7 @@ enables_(Module m, int **enables)
 int
 boot_(UNUSED(Module m))
 {
+    set_resinfo();
     return 0;
 }
 
@@ -1059,6 +892,7 @@ boot_(UNUSED(Module m))
 int
 cleanup_(Module m)
 {
+    free_resinfo();
     return setfeatureenables(m, &module_features, NULL);
 }
 
diff --git a/Src/Builtins/rlimits.mdd b/Src/Builtins/rlimits.mdd
index 9e6e9e598..06c9e9c7f 100644
--- a/Src/Builtins/rlimits.mdd
+++ b/Src/Builtins/rlimits.mdd
@@ -6,18 +6,3 @@ autofeatures="b:limit b:ulimit b:unlimit"
 autofeatures_emu="b:ulimit"
 
 objects="rlimits.o"
-
-:<<\Make
-rlimits.o rlimits..o: rlimits.h
-
-# this file will not be made if limits are unavailable
-rlimits.h: rlimits.awk @RLIMITS_INC_H@
-	$(AWK) -f $(sdir)/rlimits.awk @RLIMITS_INC_H@ /dev/null > rlimits.h
-	@if grep ZLIMTYPE_UNKNOWN rlimits.h >/dev/null; then \
-	    echo >&2 WARNING: unknown limits: mail Src/Builtins/rlimits.h to developers; \
-	else :; fi
-
-clean-here: clean.rlimits
-clean.rlimits:
-	rm -f rlimits.h
-Make
diff --git a/configure.ac b/configure.ac
index f2d65ecfc..2f1e0c41e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1928,6 +1928,7 @@ zsh_LIMIT_PRESENT(RLIMIT_SIGPENDING)
 zsh_LIMIT_PRESENT(RLIMIT_MSGQUEUE)
 zsh_LIMIT_PRESENT(RLIMIT_NICE)
 zsh_LIMIT_PRESENT(RLIMIT_RTPRIO)
+zsh_LIMIT_PRESENT(RLIMIT_RTTIME)
 zsh_LIMIT_PRESENT(RLIMIT_POSIXLOCKS)
 zsh_LIMIT_PRESENT(RLIMIT_NPTS)
 zsh_LIMIT_PRESENT(RLIMIT_SWAP)




^ permalink raw reply	[relevance 4%]

* Re: [bug] :P modifier and symlink loops
  @ 2020-03-26  0:38  3%       ` Daniel Shahaf
  0 siblings, 0 replies; 200+ results
From: Daniel Shahaf @ 2020-03-26  0:38 UTC (permalink / raw)
  To: Stephane Chazelas; +Cc: Zsh hackers list

Daniel Shahaf wrote on Sat, 21 Mar 2020 19:50 +0000:
> Patch series attached.
> 
> I ended up implementing the second option — keeping the trailing
> components verbatim — for several reasons:
> 
> 1. It's actually documented this way for :P.  (xsymlink() has other
> callers too, but I didn't check whether any of them specifically relied
> on this behaviour.)
> 
> 2. After I made the code use the realpath() wrapper function,
> chabspath(), rather than xsymlinks() (plural), that's the behaviour
> I observed, and I didn't go out of my way to change it.
> 
> I suppose we could revisit :P's behaviour on symlink loops with
> trailing components after the loop, but in the meantime, this at least
> fixes the segfault.
> 
> WDYT?

For the record, redirecting xsymlink() [singular] to use chrealpath()
rather than xsymlinks() [plural] has a side effect: it will make the
call fail immediately on systems that don't have realpath(3) available.
The following places are affected:

- setting $HOME
- 'ztie' in the gbdm module
- the :P modifier in history and parameter expansions
- the helper functions findpwd(), getnameddir(), check_autoload(),
  dircache_set()

I suppose that basically means realpath(3) is required.  It was added in
4.4BSD (1995) and has been in POSIX since 2004 if not earlier, so I'll
go ahead and push this series — but if it breaks anything, holler.

Cheers,

Daniel

^ permalink raw reply	[relevance 3%]

* Bug related to single-quoting a String
@ 2020-04-08 10:39  3% Ronald Fischer
  0 siblings, 0 replies; 200+ results
From: Ronald Fischer @ 2020-04-08 10:39 UTC (permalink / raw)
  To: zsh-workers

What I want to report can be a bugh either in the documentaion (man-page) or in the implementation, depending on which one is correct.

I am running Zsh 5.5.1 for Cygwin (which seems to be the most recent one available for this platform).

In the man-page, it says in the chapter about single-quoting a string:

    A literal ' character can be included in the string by using the \' escape.

Taken this at face value, the following should work:

    echo 'a$b\'c'

However, if I enter this in an interactive shell, Zsh reminds me that a closing single quote would be missing. The behaviour is as if \' can **not** be used to place a single quote inside a string surrounded by single quotes.

Incidentally, this behaviour is consistent with bash and dash, and the bash man-page says explicitly, that \' is not an escape inside a single-quoted string. Hence, the implementation of zsh mimics in this respect to what bash and POSIX shell are doing. 

I don't know, whether zsh was intended to behave in this way; if it is the case, the documentation is faulty. If the man page is considered right, the implementation is buggy.

(Crossposting note: I have discussed this issue at https://stackoverflow.com/questions/61095565/having-a-single-quote-inside-a-single-quoted-string?noredirect=1#comment108090871_61095565 - just in case you want to read the comments posted there, or also add something to that thread).

Ronald Fischer

^ permalink raw reply	[relevance 3%]

* Re: glob qualifier '-' doesn't work correctly on dangling symlinks
  @ 2020-04-12  7:09  4%             ` Stephane Chazelas
  2020-04-12 14:25  0%               ` Vincent Lefevre
  0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2020-04-12  7:09 UTC (permalink / raw)
  To: zsh-workers

2020-04-12 04:17:22 +0200, Vincent Lefevre:
[...]
> > What would be the glob qualifier syntax for broken symlinks?
> 
> There would need something for that. But even currently, there
> are things one cannot do with glob qualifiers, such as one does
> not have a way to know the reason why a symlink is broken, which
> can be important when one is interested in broken symlinks.
> 
> zira% ln -s /does-not-exist s1
> zira% ln -s /root/foo s2
> zira% ls -L s*
> ls: cannot access 's1': No such file or directory
> ls: cannot access 's2': Permission denied
> 
> But with glob qualifiers, there does not seem to be a way to
> distinguish these two cases.
[...]

There's:

$ zmodload zsh/system
$ ls -ld -- *(e[ERRNO=0]-e['[[ $errnos[ERRNO] = EACCES ]]'])
lrwxrwxrwx 1 chazelas chazelas 9 Apr 12 07:34 s2 -> /root/foo
$ ls -ld -- *(e[ERRNO=0]-e['[[ $errnos[ERRNO] = ENOENT ]]'])
lrwxrwxrwx 1 chazelas chazelas 15 Apr 12 07:34 s1 -> /does-not-exist

(the ERRNO=0 may not be necessary).

Note:

$ find -L . -perm -o=w
./s1
find: ‘./s2’: Permission denied

But again, *(-@) for broken symlinks is documented and widely
used, we can't break that.

So if we change "-" to exclude broken symlinks, we'd need to
special case -@. What's the scope of what should be special
cased? *(-@e['((count++))']) should probably still work as well
for instance.

How about: *(-e['((n++))']@['((brokenlinks++))'])?

And *(-@m-1) (broken links created in the last 24 hours, though
I'd expect one to write *(m-1-@) instead here)

Note that for "find -L", zsh's current behaviour is required by
POSIX (at least for links whose target can be determined not to
exist):

     -L
	  Cause the file information and file type evaluated for
	  each symbolic link encountered as a path operand on
	  the command line or encountered during the traversal
	  of a file hierarchy to be those of the file referenced
	  by the link, and not the link itself. If the
	                                        ^^^^^^
	  referenced file does not exist, the file information
	  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
	  and type shall be for the link itself.
	  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

I find some variation in behaviour though:

$ ln -s /etc/passwd/foo s3
$ gfind . -follow -perm -o=w
gfind: ‘./s3’: Not a directory
./s3
./s1
gfind: ‘./s2’: Permission denied
$ busybox find . -follow -perm -o=w
find: ./s3: Not a directory
./s1
find: ./s2: Permission denied
$ find_su3 . -follow -perm -o=w
find_su3: cannot follow symbolic link ./s3: Not a directory
find_su3: cannot follow symbolic link ./s1: No such file or directory
find_su3: cannot follow symbolic link ./s2: Permission denied

(the latter from the heirloom toolchest being not POSIX compliant)

In any case, in */*(W), or ***/*(W) or **/*(W), the cases where
directories are not readable or searchable, or symlink targets
not accessible are always silently ignored (as opposed to the
find equivalents).

-- 
Stephane

^ permalink raw reply	[relevance 4%]

* Re: glob qualifier '-' doesn't work correctly on dangling symlinks
  2020-04-12  7:09  4%             ` Stephane Chazelas
@ 2020-04-12 14:25  0%               ` Vincent Lefevre
    0 siblings, 1 reply; 200+ results
From: Vincent Lefevre @ 2020-04-12 14:25 UTC (permalink / raw)
  To: zsh-workers

On 2020-04-12 08:09:30 +0100, Stephane Chazelas wrote:
> 2020-04-12 04:17:22 +0200, Vincent Lefevre:
> > zira% ln -s /does-not-exist s1
> > zira% ln -s /root/foo s2
> > zira% ls -L s*
> > ls: cannot access 's1': No such file or directory
> > ls: cannot access 's2': Permission denied
> > 
> > But with glob qualifiers, there does not seem to be a way to
> > distinguish these two cases.
> [...]
> 
> There's:
> 
> $ zmodload zsh/system
> $ ls -ld -- *(e[ERRNO=0]-e['[[ $errnos[ERRNO] = EACCES ]]'])
> lrwxrwxrwx 1 chazelas chazelas 9 Apr 12 07:34 s2 -> /root/foo
> $ ls -ld -- *(e[ERRNO=0]-e['[[ $errnos[ERRNO] = ENOENT ]]'])
> lrwxrwxrwx 1 chazelas chazelas 15 Apr 12 07:34 s1 -> /does-not-exist
> 
> (the ERRNO=0 may not be necessary).

Well, I implicitly meant with simple glob qualifiers. Otherwise,
when allowing 'e' (to run arbitrary code), one can do almost
anything based on available information.

> Note:
> 
> $ find -L . -perm -o=w
> ./s1
> find: ‘./s2’: Permission denied
> 
> But again, *(-@) for broken symlinks is documented and widely
> used, we can't break that.

But widely used for what purpose exactly?

For instance, if the goal is to list dangling symlinks only, then
"permission denied" cases (EACCES) would yield false positives, and
existing code may be broken. So, perhaps '-' should still be kept
for dangling symlinks, but its behavior might need to be changed to
match the currently expected behavior.

And what about less common errors such as ENOMEM?

> So if we change "-" to exclude broken symlinks, we'd need to
> special case -@. What's the scope of what should be special
> cased? *(-@e['((count++))']) should probably still work as well
> for instance.
> 
> How about: *(-e['((n++))']@['((brokenlinks++))'])?
> 
> And *(-@m-1) (broken links created in the last 24 hours, though
> I'd expect one to write *(m-1-@) instead here)
> 
> Note that for "find -L", zsh's current behaviour is required by
> POSIX (at least for links whose target can be determined not to
> exist):
> 
>      -L
> 	  Cause the file information and file type evaluated for
> 	  each symbolic link encountered as a path operand on
> 	  the command line or encountered during the traversal
> 	  of a file hierarchy to be those of the file referenced
> 	  by the link, and not the link itself. If the
> 	                                        ^^^^^^
> 	  referenced file does not exist, the file information
> 	  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 	  and type shall be for the link itself.
> 	  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

At least, that's explicit, unambiguous documentation.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[relevance 0%]

* Re: glob qualifier '-' doesn't work correctly on dangling symlinks
  @ 2020-04-12 23:38  3%                   ` Vincent Lefevre
    0 siblings, 1 reply; 200+ results
From: Vincent Lefevre @ 2020-04-12 23:38 UTC (permalink / raw)
  To: zsh-workers

On 2020-04-12 18:34:48 +0100, Stephane Chazelas wrote:
> 2020-04-12 16:25:44 +0200, Vincent Lefevre:
> [...]
> > > 	  by the link, and not the link itself. If the
> > > 	                                        ^^^^^^
> > > 	  referenced file does not exist, the file information
> > > 	  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > > 	  and type shall be for the link itself.
> > > 	  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > 
> > At least, that's explicit, unambiguous documentation.
> [...]
> 
> Well, as seen in the varying interpretations made by the various
> implementations, it is not that clear. How do you determine
> whether a file exists or not? What does "exist" mean?

You necessarily know it, or that's an error case (permission denied
or whatever). The behavior may not be clear in case of error, but
one can expect at least a non-zero exit status:

  EXIT STATUS

    The following exit values shall be returned:

     0
        All path operands were traversed successfully.
    >0
        An error occurred.

As a comparison, for "test -e"

  https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html

POSIX does not say "true if file exists" but conditions the result
to the pathname resolution, so that the result is false if any error
occurs, even if the file actually exists:

  -e  pathname
    True if pathname resolves to an existing directory entry.
    False if pathname cannot be resolved.

BTW, I don't know how zsh behaves on "[[ -e pathname ]]" in case of
error other than ENOENT in the pathname resolution, but this should
be documented (and ditto for the other conditional expressions).

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[relevance 3%]

* Re: glob qualifier '-' doesn't work correctly on dangling symlinks
  @ 2020-04-14 17:59  4%                           ` Vincent Lefevre
  0 siblings, 0 replies; 200+ results
From: Vincent Lefevre @ 2020-04-14 17:59 UTC (permalink / raw)
  To: zsh-workers

On 2020-04-14 07:18:16 +0100, Stephane Chazelas wrote:
> 2020-04-13 23:41:49 +0200, Vincent Lefevre:
> [...]
> > > Which one(s) should find -L . -type l (or find . -xtype l)
> > > print?
> > 
> > /etc/passwd/foo
> > /etc/pesswd/foo
> > symloop/foo
> > 
> > (and I would expect an error message for /root/foo, such as
> > "Permission denied", in addition to a non-zero exit status).
> 
> So not that "unambiguous" after all.

Note that "unambiguous" does not necessarily mean that the
result is known.

> I could not find a single
> find implementation that agrees with your interpretation (not
> that it means that your intepretation is better or worse).
> 
> GNU find for instance only prints /etc/pesswd/foo and
> /etc/passwd/foo (but outputs an error for the latter) and
> returns non-zero for anything but /etc/pesswd/foo.

I've said that for ELOOP (here, symloop/foo), it should be fine to
regard this as "not an error". This does not mean that the opposite
is necessarily wrong. IMHO, this is just a bad choice.

IMHO, the fact that it returns non-zero for /etc/passwd/foo is a
bug.

> What should the outcome be for ESYS123 error code?

It seems non-standard, thus an error (unless the implementation
knows what it means and the consequence on the existence of the
file).

Note that in any case, an error is always possible, even when not
expected. For instance, it could be due to a network issue in case
of NFS, and more generally some hardware failure. Script must be
able to handle errors at any time.

> To me, the best approach is zsh's where *(-@) reports *all*
> broken links, broken meaning "whose target cannot be resolved".

Since zsh regards "permission denied" errors as non-matching (for
instance, on my machine, /r*/* expands to objects under /run, with
no errors, even though the /root directory is not accessible),
I think it is fine that here, EACCES errors (permission denied) be
regarded as non-existing. But IMHO, "serious" errors such as ENOMEM
should be reported as such with globbing (in general, not just for
symlink resolution), i.e. zsh should not execute the command and
should report an error instead, like with a "bad pattern" error.

[...]
> An example:
> 
> # ls -la
> total 1
> drwxr-xr-x 2 root root 2 Aug 15  2018 ./
> drwxr-xr-x 5 root root 5 Mar 18  2019 ../
> # [[ -e .zfs ]] && echo yes
> yes
> 
> No .zfs directory entry, but [[ -e .zfs ]] still returns true.
> On ZFS filesystems, the root of each dataset has a hidden
> "virtual" .zfs directory that "exists" but not as a directory
> entry. That's not unique to ZFS, netapp FSs and several
> fuse-based ones are in that case.

Is this POSIX compliant? If it is, I would say that it is a bug.

> And there's:
> 
> $ ls -a 1
> .  ..  file
> $ ls -ld 1
> dr--r--r-- 2 chazelas chazelas 3 Apr 14 07:07 1
> $ [[ -e 1/file ]] || echo no
> no
> $ (){(($#))} (#I)1/file(|)(N) && echo yes
> yes
> 
> That directory does have a "file" entry but [[ -e 1/file ]] does
> not report it (and there's a symmetric problem for a=x
> directories which don't have entries which the user can see but
> for which [[ -e ... ]] finds entries).

I would say that this is a also a bug. However, perhaps not since
how the resolution is done is not specified.

> There's also the case of case insensitive or unicode-normalizing
> file systems.

POSIX compliant?

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[relevance 4%]

* completions for c99
@ 2020-04-30 20:40  3% Vincent Lefevre
  2020-04-30 22:17  0% ` Daniel Shahaf
  0 siblings, 1 reply; 200+ results
From: Vincent Lefevre @ 2020-04-30 20:40 UTC (permalink / raw)
  To: zsh-workers

c99 should be added to the command list of
"functions/Completion/Unix/_gcc".

Note: c99 is the name of the C compiler specified by POSIX.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[relevance 3%]

* Re: completions for c99
  2020-04-30 20:40  3% completions for c99 Vincent Lefevre
@ 2020-04-30 22:17  0% ` Daniel Shahaf
  2020-05-01  0:15  3%   ` Vincent Lefevre
  0 siblings, 1 reply; 200+ results
From: Daniel Shahaf @ 2020-04-30 22:17 UTC (permalink / raw)
  To: Vincent Lefevre, zsh-workers

Vincent Lefevre wrote on Thu, 30 Apr 2020 20:40 +00:00:
> c99 should be added to the command list of
> "functions/Completion/Unix/_gcc".

That would cause several hundred options to be offered, but on some
systems c99(1) supports far fewer options:

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/c99.html
https://www.freebsd.org/cgi/man.cgi?query=c99&manpath=FreeBSD+12.1-RELEASE

We probably need a bit of $service-specific code, to see whether c99(1) is
in fact gcc or clang, in which case we can fall back to offering all
their options, or not, in which case we should offer just the handful of
options that's in fact supported.

Anyone interested in writing the patch?

> Note: c99 is the name of the C compiler specified by POSIX.

^ permalink raw reply	[relevance 0%]

* Re: completions for c99
  2020-04-30 22:17  0% ` Daniel Shahaf
@ 2020-05-01  0:15  3%   ` Vincent Lefevre
  0 siblings, 0 replies; 200+ results
From: Vincent Lefevre @ 2020-05-01  0:15 UTC (permalink / raw)
  To: zsh-workers

On 2020-04-30 22:17:43 +0000, Daniel Shahaf wrote:
> Vincent Lefevre wrote on Thu, 30 Apr 2020 20:40 +00:00:
> > c99 should be added to the command list of
> > "functions/Completion/Unix/_gcc".
> 
> That would cause several hundred options to be offered, but on some
> systems c99(1) supports far fewer options:
> 
> https://pubs.opengroup.org/onlinepubs/9699919799/utilities/c99.html

Note that POSIX lists the minimum that needs to be supported.

> https://www.freebsd.org/cgi/man.cgi?query=c99&manpath=FreeBSD+12.1-RELEASE
> 
> We probably need a bit of $service-specific code, to see whether c99(1) is
> in fact gcc or clang, in which case we can fall back to offering all
> their options, or not, in which case we should offer just the handful of
> options that's in fact supported.

I've noticed that cc is in the list, and on some platforms, it may
also support fewer options. This can even include Debian machines,
where cc is (at least) either gcc or tcc, depending on the system
configuration.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)

^ permalink raw reply	[relevance 3%]

* Re: Feature request: ZSH_XTRACEFD variable
  @ 2020-05-03  7:07  4%                   ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2020-05-03  7:07 UTC (permalink / raw)
  To: Daniel Shahaf, Bart Schaefer, Timothée Mazzucotelli,
	Peter Stephenson, zsh-workers

2020-05-03 07:01:13 +0100, Stephane Chazelas:
[...]
> SHELLOPTS=xtrace BASH_XTRACEFD=7 some-command
[...]
> I don't think we want to go there with zsh.
[...]

I meant: we probably don't want to have options set via the
environment like with bash's SHELLOPTS and BASHOPTS, as that's
quite dangerous and does cause quite a few problems with bash

See for instance:

$ (SHELLOPTS= exec -a sh bash -c 'bash -c "printenv SHELLOPTS"')
braceexpand:hashall:interactive-comments:posix

See how all those POSIX compliance options have been enabled for
*all* bash invocations, just because bash was invoked as sh in
an environment with a SHELLOPTS variable.

But for ZSH_XTRACEFD, IMO it makes sense to import it as we do
already import PS4.

Note that BASHOPTS, SHELLOPTS and PS4 are in sudo's env var
blacklist, but BASH_XTRACEFD is not (maybe it should)

-- 
Stephane

^ permalink raw reply	[relevance 4%]

* Re: local_traps doesn't restore traps set from functions
  @ 2020-05-07 21:21  5% ` Daniel Shahaf
    0 siblings, 1 reply; 200+ results
From: Daniel Shahaf @ 2020-05-07 21:21 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: Zsh hackers list

Roman Perepelitsa wrote on Wed, 06 May 2020 15:31 +0200:
> Is this working as intended?
> 
>   ❯ zsh -f
>   adam% () { trap '' INT }
>   adam% () { emulate -L zsh; trap 'echo INT' INT }
>   adam% kill -INT $$
>   INT
>   adam%
> 

I'd say it doesn't, since the trap _is_ set before the second function is called —
.
    % () { trap '' INT }
    % kill -INT $$
    % 
.
— and the documentation is quite clear about the semantics in this case:

       LOCAL_TRAPS <K>
              If this option is set when a signal trap is set inside a
              function, then the previous status of the trap for that signal
              will be restored when the function exits.

> It appears that local_traps doesn't restore traps that were originally
> set from functions.

Precisely.

The following patch fixes it:

[[[
diff --git a/Src/signals.c b/Src/signals.c
index 4adf03202..3e4fdf370 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -1154,6 +1154,29 @@ endtrapscope(void)
 	}
     }
 
+    /* Fixup locallevel of signals. */
+    {
+	int i;
+	for (i = 0; i < VSIGCOUNT; ++i) {
+	    int locallevel_of_signal = (sigtrapped[i] >> ZSIG_SHIFT);
+	    if (locallevel_of_signal > locallevel) {
+		DPUTS3(locallevel_of_signal != locallevel + 1,
+		    "BUG: signal %s's locallevel unexpected: %d>>ZSIG_SHIFT seen, v. %d+1 expected",
+		    sigs[i], sigtrapped[i], locallevel);
+
+		/* 
+		 * Decrement the locallevel embedded in sigtrapped[i].
+		 *
+		 * Keep the low ZSIG_SHIFT bits unchanged.
+		 */
+		sigtrapped[i] =
+		    (sigtrapped[i] & ((1u << ZSIG_SHIFT) - 1u))
+		    |
+		    (locallevel << ZSIG_SHIFT);
+	    }
+	}
+    }
+
     if (exittr) {
 	/*
 	 * We already made sure this wasn't set as a POSIX exit trap.
diff --git a/Src/zsh.h b/Src/zsh.h
index 1f2d774a1..71ba6e6e0 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -2940,8 +2940,8 @@ struct heap {
 #define ZSIG_FUNC	(1<<2)	/* Trap is a function, not an eval list */
 /* Mask to get the above flags */
 #define ZSIG_MASK	(ZSIG_TRAPPED|ZSIG_IGNORED|ZSIG_FUNC)
-/* No. of bits to shift local level when storing in sigtrapped */
 #define ZSIG_ALIAS	(1<<3)  /* Trap is stored under an alias */
+/* No. of bits to shift locallevel when storing in sigtrapped */
 #define ZSIG_SHIFT	4
 
 /*
]]]

[[[
% () { trap '' INT }
% () { emulate -L zsh; trap 'echo INT' INT }
% kill -INT $$
% 
]]]

However, I don't know this area of the code well enough to be sure the
patch doesn't break anything.  Reviews would be appreciated.

Also, if someone could write a regression test for this, that'd be great.

Cheers,

Daniel

^ permalink raw reply	[relevance 5%]

* [PATCH] return status 126 for execution failures other than 'not found'
@ 2020-05-08 20:36  2% Martijn Dekker
  2020-05-08 20:54  3% ` Bart Schaefer
  2020-05-09 15:35  0% ` Daniel Shahaf
  0 siblings, 2 replies; 200+ results
From: Martijn Dekker @ 2020-05-08 20:36 UTC (permalink / raw)
  To: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 1677 bytes --]

When the argument list to an external command is too long (exceeds 
ARG_MAX), zsh returns exit status 127. This is incorrect because status 
127 means the command was not found.

POSIX says in "2.8.2 Exit Status for Commands":
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_02
| If a command is not found, the exit status shall be 127. If the
| command name is found, but it is not an executable utility, the exit
| status shall be 126.

Now, the phrasing "it is not an executable utility" is a little vague, 
but the current versions of all other shells return 126 upon any failure 
to execute a utility that was found, so they seem to interpret that 
phrase as meaning "it could not be executed for whatever reason (other 
than 'not found')". In any case, 126 is better than any alternative, as 
all other exit codes potentially conflict with other meanings.

Currently, the execute() function in Src/exec.c only returns status 126 
if the error code is EACCES (permission denied) or ENOEXEC (exec format 
error). In all other cases, 127 is returned.

That logic is not right, because 127 is the specific case: it is only to 
be used if the command was not found. Any failure to execute after the 
command is found should yield status 126.

The attached patch changes that logic to return status 127 if there is 
no error number (which happens if a PATH search does not find a command) 
or if the error number is ENOENT (no such file or directory). In all 
other cases it now returns 126.

I've also added a test for the "argument list too long" case.

- Martijn

-- 
modernish -- harness the shell
https://github.com/modernish/modernish

[-- Attachment #2: status126.patch --]
[-- Type: text/plain, Size: 1466 bytes --]

diff --git a/Src/exec.c b/Src/exec.c
index 2b8e2167f..be949049f 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -726,7 +726,7 @@ execute(LinkList args, int flags, int defpath)
 		(arg0[0] == '.' && (arg0 + 1 == s ||
 				    (arg0[1] == '.' && arg0 + 2 == s)))) {
 		zerr("%e: %s", lerrno, arg0);
-		_exit((lerrno == EACCES || lerrno == ENOEXEC) ? 126 : 127);
+		_exit((lerrno == 0 || lerrno == ENOENT) ? 127 : 126);
 	    }
 	    break;
 	}
@@ -805,7 +805,7 @@ execute(LinkList args, int flags, int defpath)
 	_realexit();
     else
 	zerr("command not found: %s", arg0);
-    _exit((eno == EACCES || eno == ENOEXEC) ? 126 : 127);
+    _exit((eno == 0 || eno == ENOENT) ? 127 : 126);
 }
 
 #define RET_IF_COM(X) { if (iscom(X)) return docopy ? dupstring(X) : arg0; }
diff --git a/Test/A05execution.ztst b/Test/A05execution.ztst
index edc561582..64fdd8288 100644
--- a/Test/A05execution.ztst
+++ b/Test/A05execution.ztst
@@ -394,3 +394,13 @@ F:anonymous function, and a descriptor leak when backgrounding a pipeline
 >127
 # TBD: the 0 above is believed to be bogus and should also be turned
 # into 127 when the ccorresponding bug is fixed in the main shell.
+
+# Test for exit status 126 when an external command's argument list is too long.
+  v=foo_bar_baz_quux_lorem_ipsum_dolor_sit_amet
+  while :; do
+    v=$v$v$v$v$v$v$v$v
+    env true $v || { print $?; break; }
+  done
+0:exit status when argument list too long
+>126
+?(eval):4: argument list too long: env

^ permalink raw reply	[relevance 2%]

* Re: [PATCH] return status 126 for execution failures other than 'not found'
  2020-05-08 20:36  2% [PATCH] return status 126 for execution failures other than 'not found' Martijn Dekker
@ 2020-05-08 20:54  3% ` Bart Schaefer
  2020-05-09 15:35  0% ` Daniel Shahaf
  1 sibling, 0 replies; 200+ results
From: Bart Schaefer @ 2020-05-08 20:54 UTC (permalink / raw)
  To: Martijn Dekker; +Cc: Zsh hackers list

On Fri, May 8, 2020 at 1:37 PM Martijn Dekker <martijn@inlv.org> wrote:
>
> Currently, the execute() function in Src/exec.c only returns status 126
> if the error code is EACCES (permission denied) or ENOEXEC (exec format
> error). In all other cases, 127 is returned.
>
> That logic is not right, because 127 is the specific case: it is only to
> be used if the command was not found. Any failure to execute after the
> command is found should yield status 126.

Aside from the "zsh is not POSIX unless invoked as sh" standpoint ...

> The attached patch changes that logic to return status 127 if there is
> no error number (which happens if a PATH search does not find a command)
> or if the error number is ENOENT (no such file or directory). In all
> other cases it now returns 126.

... shouldn't EPERM also be in that list?  A file that's not
executable or otherwise not reachable seems to fall into the "not
found" category.

^ permalink raw reply	[relevance 3%]

* Re: [PATCH] return status 126 for execution failures other than 'not found'
  2020-05-08 20:36  2% [PATCH] return status 126 for execution failures other than 'not found' Martijn Dekker
  2020-05-08 20:54  3% ` Bart Schaefer
@ 2020-05-09 15:35  0% ` Daniel Shahaf
  1 sibling, 0 replies; 200+ results
From: Daniel Shahaf @ 2020-05-09 15:35 UTC (permalink / raw)
  To: Martijn Dekker; +Cc: Zsh hackers list

Martijn Dekker wrote on Fri, 08 May 2020 21:36 +0100:
> When the argument list to an external command is too long (exceeds 
> ARG_MAX), zsh returns exit status 127. This is incorrect because status 
> 127 means the command was not found.
> 
> POSIX says in "2.8.2 Exit Status for Commands":
> https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_02
> | If a command is not found, the exit status shall be 127. If the
> | command name is found, but it is not an executable utility, the exit
> | status shall be 126.
> 

In the test case you added, I think the answers to "Was «env» found?"
and "Is «env» an executable utility?" are both "yes".  Thus, neither of
the sentence quoted from §2.8.2 applies: the exit status in that case is
neither required to be 127 nor required to be 126.

That would appear to be a lacuna: the standard does not specify what the
exit status shall be when the command _is_ found and _is_ an "executable
utility", but execution fails for some other reason, such as an argument
being too long.

Furthermore, the standard doesn't _forbid_ the exit status of the test
you added from being either 126 or 127.  (That's the plain meaning of
quoted sentences: they say "if A then B", not "A iff B".)

It _would_ be useful for error codes to only be caused by specific
failure modes, in order to enable scripts to handle error codes
selectively.  However, the incumbent code has this property too:
status 126 has only two specific causes.

We could still make the change you propose, of course, but it's not
a black and white case of the incumbent behaviour being "incorrect".
Therefore, in particular, we should consider not only whether we'd be
compatible with other shells, but also whether the proposed change might
constitute a regression for people upgrading from older zsh to newer
zsh.  For example, might some zsh users be relying on the incumbent
"status 126 can only be caused by EACCES and ENOEXEC" semantics?

To be clear, I'm not objecting to the change; I'm only trying to ensure
all viewpoints are taken into account.

Thanks, Martijn.

Daniel

> Now, the phrasing "it is not an executable utility" is a little vague, 
> but the current versions of all other shells return 126 upon any failure 
> to execute a utility that was found, so they seem to interpret that 
> phrase as meaning "it could not be executed for whatever reason (other 
> than 'not found')". In any case, 126 is better than any alternative, as 
> all other exit codes potentially conflict with other meanings.
> 
> Currently, the execute() function in Src/exec.c only returns status 126 
> if the error code is EACCES (permission denied) or ENOEXEC (exec format 
> error). In all other cases, 127 is returned.
> 
> That logic is not right, because 127 is the specific case: it is only to 
> be used if the command was not found. Any failure to execute after the 
> command is found should yield status 126.
> 
> The attached patch changes that logic to return status 127 if there is 
> no error number (which happens if a PATH search does not find a command) 
> or if the error number is ENOENT (no such file or directory). In all 
> other cases it now returns 126.
> 
> I've also added a test for the "argument list too long" case.

^ permalink raw reply	[relevance 0%]

* Re: local_traps doesn't restore traps set from functions
  @ 2020-05-09 19:51  4%       ` Daniel Shahaf
  0 siblings, 0 replies; 200+ results
From: Daniel Shahaf @ 2020-05-09 19:51 UTC (permalink / raw)
  To: Zsh hackers list

Daniel Shahaf wrote on Sat, 09 May 2020 15:42 +0000:
> Peter Stephenson wrote on Sat, 09 May 2020 16:24 +0100:
> > > On 07 May 2020 at 22:21 Daniel Shahaf <d.s@daniel.shahaf.name> wrote:
> > > Roman Perepelitsa wrote on Wed, 06 May 2020 15:31 +0200:    
> > > > It appears that local_traps doesn't restore traps that were originally
> > > > set from functions.    
> > > 
> > > Precisely.
> > > 
> > > The following patch fixes it:    
> > 
> > OK, so the point here is that in this case the trap we're fiddling with
> > is not going out of scope, nor having something else restored over it,
> > it just happens to have been created inside a function.  At this
> > point, that fact becomes irrelevant, so we simply massage the information
> > to make it behave as if it had been created at the current (new) function
> > depth.
> > 
> > That seems fine --- my only comment would be it would probably be sensible
> > to insert some edited version of the previous paragraph as I for one
> > am almost certain to wonder what's going on if I encounter this code
> > in the future.  
> 
> Will do.  Thanks for the review!

Here's an interdiff, to be applied on top of 45790.

Test will follow separately.

diff --git a/Src/signals.c b/Src/signals.c
index 5d0bae7f5..56b3071de 100644
--- a/Src/signals.c
+++ b/Src/signals.c
@@ -30,8 +30,16 @@
 #include "zsh.mdh"
 #include "signals.pro"
  
-/* Array describing the state of each signal: an element contains *
- * 0 for the default action or some ZSIG_* flags ored together.   */
+/* 
+ * Array describing the state of each signal: an element contains
+ * 0 for the default action or some ZSIG_* flags ored together.
+ *
+ * The high bits (see ZSIG_SHIFT) identify the locallevel that owns the trap.
+ * Whenever execution returns to that locallevel (which is never larger/deeper
+ * than the current locallevel) from deeper levels, any function-scoped traps
+ * that shadow this trap will have gone out of scope.  See the LOCALTRAPS
+ * condition in removetrap().
+ */
 
 /**/
 mod_export int sigtrapped[VSIGCOUNT];
@@ -997,9 +1005,12 @@ removetrap(int sig)
     queue_signals();
     trapped = sigtrapped[sig];
     /*
+     * Save the trap if we should restore it at the end of the current function.
+     *
      * Note that we save the trap here even if there isn't an existing
-     * one, to aid in removing this one.  However, if there's
-     * already one at the current locallevel we just overwrite it.
+     * one, to aid in removing this one.  We also save the trap if it's owned
+     * by someone up the callstack.  However, if the trap is owned by the
+     * current locallevel we just overwrite it.
      *
      * Note we save EXIT traps based on the *current* setting of
      * POSIXTRAPS --- so if there is POSIX EXIT trap set but
@@ -1062,6 +1073,16 @@ removetrap(int sig)
     return NULL;
 }
 
+/*
+ * Return the value of ARG after setting all but the N least significant bits
+ * to zero.
+ */
+static unsigned int
+low_N_bits_of(unsigned int arg, unsigned char N)
+{
+    return arg & ((1u << N)-1u);
+}
+
 /**/
 void
 starttrapscope(void)
@@ -1114,6 +1135,9 @@ endtrapscope(void)
 	sigtrapped[SIGEXIT] = 0;
     }
 
+    /*
+     * Restore traps from savetraps to sigtrapped.
+     */
     if (savetraps) {
 	while ((ln = firstnode(savetraps)) &&
 	       (st = (struct savetrap *) ln->dat) &&
@@ -1122,6 +1146,8 @@ endtrapscope(void)
 
 	    remnode(savetraps, ln);
 
+	    /* ### This doesn't mask ZSIG_IGNORED out of the boolean check,
+	     * ### presumably because checking st->list is sufficient? */
 	    if (st->flags && (st->list != NULL)) {
 		/* prevent settrap from saving this */
 		dontsavetrap++;
@@ -1154,7 +1180,11 @@ endtrapscope(void)
 	}
     }
 
-    /* Fixup locallevel of signals. */
+    /*
+     * If the function that's in the process of returning set a global trap,
+     * that trap is now owned by that function's caller.  Update sigtrapped
+     * accordingly.
+     */
     {
 	int i;
 	for (i = 0; i < VSIGCOUNT; ++i) {
@@ -1170,7 +1200,7 @@ endtrapscope(void)
 		 * Keep the low ZSIG_SHIFT bits unchanged.
 		 */
 		sigtrapped[i] =
-		    (sigtrapped[i] & ((1u << ZSIG_SHIFT) - 1u))
+		    low_N_bits_of(sigtrapped[i], ZSIG_SHIFT)
 		    |
 		    (locallevel << ZSIG_SHIFT);
 	    }

^ permalink raw reply	[relevance 4%]

* Re: [PATCH?] Re: Autocorrect for commands with a hyphen (dash) in the name
  @ 2020-05-25 16:35  3%                       ` Peter Stephenson
  0 siblings, 0 replies; 200+ results
From: Peter Stephenson @ 2020-05-25 16:35 UTC (permalink / raw)
  To: zsh-workers

On Sun, 2020-05-24 at 22:46 -0700, Bart Schaefer wrote:
> > spckword() modifies the original input string in place, to return the
> > best guess.  In both lex.c and zle_tricky.c, that result string is
> > then used in later code, which means if it goes in tokenized it ought
> > to come back out that way as well.
> 
> Maybe this is just the best way to deal with this mess.

I think this might be a good approach, given all the complexities
the other way, but I'd suggest a dupstring if we find a Dash ---
three aren't so many cases that need it, mostly limited to
idiotic parsing requirements in POSIX-compliant contexts,
but I don't think we can guarantee they don't intersect
with cases where spell checking is live.

pws

> diff --git a/Src/utils.c b/Src/utils.c
> index 5158a70..118e132 100644
> --- a/Src/utils.c
> +++ b/Src/utils.c
> @@ -3151,8 +3151,12 @@ spckword(char **s, int hist, int cmd, int ask)
>      if (*t == Tilde || *t == Equals || *t == String)
>      t++;
>      for (; *t; t++)
> -    if (itok(*t))
> -        return;
> +    if (itok(*t)) {
> +        if (*t == Dash)
> +        *t = '-';
> +        else
> +        return;
> +    }
>      best = NULL;
>      for (t = *s; *t; t++)
>      if (*t == '/')


^ permalink raw reply	[relevance 3%]

* [PATCH v2 0/1] Run pipeline command in subshell in sh mode
@ 2020-06-05  1:53  4% brian m. carlson
  2020-06-05  1:53  3% ` [PATCH v2] exec: run final pipeline command in a " brian m. carlson
  0 siblings, 1 reply; 200+ results
From: brian m. carlson @ 2020-06-05  1:53 UTC (permalink / raw)
  To: zsh-workers

POSIX sh implementations run each command in a pipeline in a subshell,
although zsh (and AT&T ksh) do not: instead, they run the final command
in the main shell.  This leads to very different behavior when the final
command is a shell function which modifies variables.

zsh is starting to be used in some cases as /bin/sh, such as on macOS
Catalina.  Consequently, it makes sense to emulate the POSIX behavior as
much as possible when emulating sh, since that's the least surprising
behavior.  This patch does exactly that.

With this patch, using "zsh --emulate sh" passes the Git testsuite.  I
expect that it will also be fully functional as /bin/sh on Debian,
although I have not tested.

This patch was sent before, but didn't get picked up.  In hopes of
aiding reviewers, I've resent it with a significantly expanded commit
message so that it is easier to reason about.

I'm not subscribed to the list, so please CC me if you have questions or
comments.

brian m. carlson (1):
  exec: run final pipeline command in a subshell in sh mode

 Src/exec.c           | 10 ++++++----
 Test/B07emulate.ztst | 22 ++++++++++++++++++++++
 2 files changed, 28 insertions(+), 4 deletions(-)


^ permalink raw reply	[relevance 4%]

* [PATCH v2] exec: run final pipeline command in a subshell in sh mode
  2020-06-05  1:53  4% [PATCH v2 0/1] Run pipeline command in subshell in sh mode brian m. carlson
@ 2020-06-05  1:53  3% ` brian m. carlson
  2020-06-05 10:21  4%   ` Mikael Magnusson
  0 siblings, 1 reply; 200+ results
From: brian m. carlson @ 2020-06-05  1:53 UTC (permalink / raw)
  To: zsh-workers

zsh typically runs the final command in a pipeline in the main shell
instead of a subshell.  However, POSIX requires that all commands in a
pipeline run in a subshell, but permits zsh's behavior as an extension.

Since zsh may be used as /bin/sh in some cases (such as macOS Catalina),
it makes sense to have the POSIX behavior when emulating sh, so do that
by checking for being the final item of a multi-item pipeline and
creating a subshell in that case.

From the comment above execpline(), we know the following:

  last1 is a flag that this command is the last command in a shell that
  is about to exit, so we can exec instead of forking.  It gets passed
  all the way down to execcmd() which actually makes the decision.  A 0
  is always passed if the command is not the last in the pipeline. […]
  If last1 is zero but the command is at the end of a pipeline, we pass
  2 down to execcmd().

So there are three cases to consider in this code:

• last1 is 0, which means we are not at the end of a pipeline, in which
  case we should not change behavior.
• last1 is 1, which means we are effectively running in a subshell,
  because nothing that happens due to the exec is going to affect the
  actual shell, since it will have been replaced.  So there is nothing
  to do here.
• last1 is 2, which means our command is at the end of the pipeline, so
  in sh mode we should create a subshell by forking.

input is nonzero if the input to this process is a pipe that we've
opened.  At the end of a multi-stage pipeline, it will necessarily be
nonzero.

Note that several of the tests may appear bizarre, since most developers
do not place useless variable assignments directly at the end of a
pipeline.  However, as the function tests demonstrate, there are cases
where assignments may occur when a shell function is used at the end of
a command.  The remaining assignment tests simply test additional cases,
such as the use of local, that would otherwise be untested.
---
 Src/exec.c           | 10 ++++++----
 Test/B07emulate.ztst | 22 ++++++++++++++++++++++
 2 files changed, 28 insertions(+), 4 deletions(-)

diff --git a/Src/exec.c b/Src/exec.c
index 29f4fc5ca..f2650f311 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -2866,11 +2866,13 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 	    pushnode(args, dupstring("fg"));
     }
 
-    if ((how & Z_ASYNC) || output) {
+    if ((how & Z_ASYNC) || output ||
+	(last1 == 2 && input && EMULATION(EMULATE_SH))) {
 	/*
-	 * If running in the background, or not the last command in a
-	 * pipeline, we don't need any of the rest of this function to
-	 * affect the state in the main shell, so fork immediately.
+	 * If running in the background, not the last command in a
+	 * pipeline, or the last command in a multi-stage pipeline
+	 * in sh mode, we don't need any of the rest of this function
+	 * to affect the state in the main shell, so fork immediately.
 	 *
 	 * In other cases we may need to process the command line
 	 * a bit further before we make the decision.
diff --git a/Test/B07emulate.ztst b/Test/B07emulate.ztst
index 7b1592fa9..45c39b51d 100644
--- a/Test/B07emulate.ztst
+++ b/Test/B07emulate.ztst
@@ -276,3 +276,25 @@ F:Some reserved tokens are handled in alias expansion
 0:--emulate followed by other options
 >yes
 >no
+
+  emulate sh -c '
+  foo () {
+    VAR=foo &&
+    echo $VAR | bar &&
+    echo "$VAR"
+  }
+  bar () {
+    tr f b &&
+    VAR="$(echo bar | tr r z)" &&
+    echo "$VAR"
+  }
+  foo
+  '
+  emulate sh -c 'func() { echo | local def="abc"; echo $def;}; func'
+  emulate sh -c 'abc="def"; echo | abc="ghi"; echo $abc'
+0:emulate sh uses subshell for last pipe entry
+>boo
+>baz
+>foo
+>
+>def

^ permalink raw reply	[relevance 3%]

* Re: [PATCH v2] exec: run final pipeline command in a subshell in sh mode
  2020-06-05  1:53  3% ` [PATCH v2] exec: run final pipeline command in a " brian m. carlson
@ 2020-06-05 10:21  4%   ` Mikael Magnusson
  2020-06-05 20:41  5%     ` brian m. carlson
  0 siblings, 1 reply; 200+ results
From: Mikael Magnusson @ 2020-06-05 10:21 UTC (permalink / raw)
  To: brian m. carlson; +Cc: zsh-workers

On 6/5/20, brian m. carlson <sandals@crustytoothpaste.net> wrote:
> zsh typically runs the final command in a pipeline in the main shell
> instead of a subshell.  However, POSIX requires that all commands in a
> pipeline run in a subshell, but permits zsh's behavior as an extension.

What POSIX actually says is:
"each command of a multi-command pipeline is in a subshell
environment; as an extension, however, any or all commands in a
pipeline may be executed in the current environment"
Ie, it does not say "shall", so it doesn't require a subshell all, in
fact it explicitly does permit not using one as you also say. The
patch is possibly useful (seems unlikely to me), but to say it is
required by POSIX is not true. If someone depends on every command in
a pipeline being a subshell, they should fix their code, for example
by adding ( ) around it (the command(s) or the whole pipeline).

-- 
Mikael Magnusson

^ permalink raw reply	[relevance 4%]

* Re: [PATCH v2] exec: run final pipeline command in a subshell in sh mode
  2020-06-05 10:21  4%   ` Mikael Magnusson
@ 2020-06-05 20:41  5%     ` brian m. carlson
  2020-06-06  4:33  5%       ` [PATCH v2] exec: run final pipeline command in a subshell in sh modeZZ Daniel Shahaf
  0 siblings, 1 reply; 200+ results
From: brian m. carlson @ 2020-06-05 20:41 UTC (permalink / raw)
  To: Mikael Magnusson; +Cc: zsh-workers

[-- Attachment #1: Type: text/plain, Size: 2606 bytes --]

On 2020-06-05 at 10:21:41, Mikael Magnusson wrote:
> On 6/5/20, brian m. carlson <sandals@crustytoothpaste.net> wrote:
> > zsh typically runs the final command in a pipeline in the main shell
> > instead of a subshell.  However, POSIX requires that all commands in a
> > pipeline run in a subshell, but permits zsh's behavior as an extension.
> 
> What POSIX actually says is:
> "each command of a multi-command pipeline is in a subshell
> environment; as an extension, however, any or all commands in a
> pipeline may be executed in the current environment"
> Ie, it does not say "shall", so it doesn't require a subshell all, in
> fact it explicitly does permit not using one as you also say. The
> patch is possibly useful (seems unlikely to me), but to say it is
> required by POSIX is not true. If someone depends on every command in
> a pipeline being a subshell, they should fix their code, for example
> by adding ( ) around it (the command(s) or the whole pipeline).

POSIX makes a declarative statement about the behavior of a pipeline.
It is true that it doesn't explicitly use the word "shall" in this case,
since such a statement would explicitly prohibit the inclusion of an
extension at all and make it explicitly non-conforming.

What POSIX does say is that one “shall define an environment in which an
application can be run with the behavior specified by POSIX.1-2017.”
I'm proposing that "zsh --emulate sh" implement the POSIX behavior for
that reason.

I will tell you that as a practical matter, nobody writing code for sh
expects the last command not to be run in a subshell and consequently
lots of code is practically broken in this case with zsh as /bin/sh.
The Git Project is very fastidious about writing portable shell, as is
Debian, and I can tell you from experience that both are broken with zsh
as sh with the current behavior, even if they should not have made that
assumption.

zsh is a very popular interactive shell, and allowing it to be used as a
portable sh on systems where the system sh is less capable would be
really beneficial.  I would also like to see macOS users who decide to
use zsh as /bin/sh have a good experience with existing code that
overwhelmingly does make this assumption.

If your objection is to the wording, I'm happy to revise it to remove
the word "requires", but I do think this provides a lot of benefits for
the sh scripting case while not impacting users who are expecting
different behavior for the zsh case.
-- 
brian m. carlson: Houston, Texas, US
OpenPGP: https://keybase.io/bk2204

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 263 bytes --]

^ permalink raw reply	[relevance 5%]

* Re: [PATCH v2] exec: run final pipeline command in a subshell in sh modeZZ
  2020-06-05 20:41  5%     ` brian m. carlson
@ 2020-06-06  4:33  5%       ` Daniel Shahaf
  2020-06-06 16:28  5%         ` brian m. carlson
  0 siblings, 1 reply; 200+ results
From: Daniel Shahaf @ 2020-06-06  4:33 UTC (permalink / raw)
  To: brian m. carlson; +Cc: Mikael Magnusson, zsh-workers

brian m. carlson wrote on Fri, 05 Jun 2020 20:41 +0000:
> On 2020-06-05 at 10:21:41, Mikael Magnusson wrote:
> > On 6/5/20, brian m. carlson <sandals@crustytoothpaste.net> wrote:  
> > > zsh typically runs the final command in a pipeline in the main shell
> > > instead of a subshell.  However, POSIX requires that all commands in a
> > > pipeline run in a subshell, but permits zsh's behavior as an extension.  
> > 
> > What POSIX actually says is:
> > "each command of a multi-command pipeline is in a subshell
> > environment; as an extension, however, any or all commands in a
> > pipeline may be executed in the current environment"

That's quoted from https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12.

The part Brian quotes below is from https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap02.html#tag_02_01_01.

> > Ie, it does not say "shall", so it doesn't require a subshell all, in
> > fact it explicitly does permit not using one as you also say. The

This interpretation is analogous to how conforming C programs must
assume neither that «char» is signed nor that it is unsigned.

> > patch is possibly useful (seems unlikely to me), but to say it is
> > required by POSIX is not true. If someone depends on every command in
> > a pipeline being a subshell, they should fix their code, for example
> > by adding ( ) around it (the command(s) or the whole pipeline).  
> 
> POSIX makes a declarative statement about the behavior of a pipeline.
> It is true that it doesn't explicitly use the word "shall" in this case,
> since such a statement would explicitly prohibit the inclusion of an
> extension at all and make it explicitly non-conforming.
> 

The sentence preceding the one you quoted reads:
.
    Non-standard extensions, when used, may change the behavior of
    utilities, functions, or facilities defined by POSIX.1-2017.

I take this to mean non-standard extensions aren't bound by "shall"s.

As to why the passage Mikael quoted doesn't use the word "shall"… well,
presumably it doesn't use the word "shall" because it doesn't describe
"a feature or behavior that is mandatory"¹.

> What POSIX does say is that one “shall define an environment in which an
> application can be run with the behavior specified by POSIX.1-2017.”
> I'm proposing that "zsh --emulate sh" implement the POSIX behavior for
> that reason.

What Mikael's saying is that zsh's incumbent behaviour is already
POSIX-conforming, but POSIX-conforming implementations have some leeway:
have a range of possible behaviours to choose from, just like conforming
C compilers can choose what signedness to give to «char».

The passage Mikael quoted specifies that running the last command in
a pipeline in a subshell by default is permitted in certain cases,
outlined by the phrases "as an extension" and "may".

The definition of "may"¹ says it's used to describe "optional" behaviours,
and that conforming applications should tolerate both presence and
absence of that behaviour.

As to that behaviour's being an extension, the sentence you quoted, once
put back in context (link given above), requires conforming implementations
to document how to disable non-standard extensions where they change
POSIX-documented behaviour.  I don't think the passage Mikael quoted is
covered by that: it _is_ an extension, of course, but I question whether
it's a "non-standard extension"².  After all, it is specified in the
standard.

To summarize, I don't see why behaviour specified with the phrases "as
an extension" and "may" should be off by default in a POSIX-conforming
mode.  Would you elaborate on this?

(On the other hand, I'm not sure why they bothered to write the words
"as an extension" there.  They don't seem to change the meaning one way
or the other.)

> I will tell you that as a practical matter, nobody writing code for sh
> expects the last command not to be run in a subshell and consequently
> lots of code is practically broken in this case with zsh as /bin/sh.
> The Git Project is very fastidious about writing portable shell, as is
> Debian, and I can tell you from experience that both are broken with zsh
> as sh with the current behavior, even if they should not have made that
> assumption.
> 
> I would also like to see macOS users who decide to use zsh as /bin/sh
> have a good experience with existing code that overwhelmingly does
> make this assumption.

Well, perhaps there is something we can do to make their lives easier.

Continuing the analogy to C, gcc(1) has -fsigned-char/-funsigned-char
flags to help unportable programs.  However, I hesitate to propose
adding an option just for this: adding options is always easy to
suggest, but not always a good idea.

Since zsh already incorporates a parser for sh scripts, perhaps we could
write a tool that automatically adds parentheses to the last element in
every pipeline.  That's not such a crazy idea: it already exists (in a
much more general form) for C: http://coccinelle.lip6.fr/

[Between these two, an extra option is definitely the lower-hanging
fruit, of course.]

> zsh is a very popular interactive shell, and allowing it to be used as a
> portable sh on systems where the system sh is less capable would be
> really beneficial.

How would it be beneficial?

> If your objection is to the wording, I'm happy to revise it to remove
> the word "requires", but I do think this provides a lot of benefits for
> the sh scripting case while not impacting users who are expecting
> different behavior for the zsh case.

The patch would constitute a backwards-incompatible change to anyone who
uses zsh as sh today and relies on the current behaviour of pipelines.

This might have been acceptable if it were a question of changing
a non-conforming behaviour to a conforming behaviour.  However, the
current behaviour does appear to be conforming.

Furthermore, if the patch is accepted, those who rely on the incumbent
behaviour won't have an easy workaround to get it behaviour back,
something comparable to the "add parentheses around every element of
every pipeline" strategy that can be used given the incumbent
implementation to get the patch's semantics.

Cheers,

Daniel

¹ https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap01.html

² If POSIX didn't want to make this distinction, it would have written
just "extension", unqualified.

^ permalink raw reply	[relevance 5%]

* Re: Any way to allow clobbering empty files when noclobber is set?
  @ 2020-06-06 12:48  4%                 ` Roman Perepelitsa
  2020-06-06 15:24  3%                   ` Bart Schaefer
  0 siblings, 1 reply; 200+ results
From: Roman Perepelitsa @ 2020-06-06 12:48 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: Zsh hackers list

On Sat, Jun 6, 2020 at 1:58 PM Peter Stephenson
<p.w.stephenson@ntlworld.com> wrote:
>
> My understanding of the semantics (feel free to put me right, of
> course), is that as any use of fstat() would be on a non-clobbering
> open, you can't stop anyone else writing to the file (the same file,
> rahter than a newly created one with the same one) at any point.  Worse,
> you're now claiming you've clobbered that open file, and you haven't.
> So now you have Roman's nightmare scneario where you're claiming you've
> opened a new file because the old one was empty, but actually two
> processes have written to it.  Not only have you not fixed a race,
> you've created a new one.

Yep. Having unspecified file content that is the result of interleaved
writes seems pretty bad.

> Assuming both processes are actually doing clobbering opens (O_CREAT |
> O_EXCL), you can't get into that position.  You can't guarantee that
> someone else hasn't written to a file of that name, but you can
> guarantee that two so-called clobbering opens actually are that.

Let me put this in code:

  { print hello >file } &
  hello_pid=$!

  { print bye >file } &
  bye_pid=$!

  { print -n >file } &
  empty_pid=$!

  wait $hello_pid
  print "print hello => $?"

  wait $bye_pid
  print "print bye => $?"

  wait $empty_pid
  print "print -n => $?"

  print "file => $(<file)"

This program concurrently writes "hello", "bye" and "" (empty) to the
same file. Any implementation of clobber_empty that uses just POSIX C
API will allow multiple writes to succeed, resulting in this output:

  print hello => 0
  print bye => 0

This output implies that one command has overwritten the output of
another and the user has received no indication to this effect.

In addition, no implementation restricted to POSIX C API can guarantee
that the last line of the output is one of these:

  file => hello
  file => bye

Different implementations will violate this expectation in different
ways. Some will allow abominations such as "byelo" or "hellobye". If I
understand Peter's point correctly, his preferred implementation will
guarantee that the file exists and contains either "hello", "bye" or
"" (empty). The possibility of an empty file is unfortunate. In
addition, this implementation requires unlinking files (when the file
exists prior to a write and is empty), which may be prohibited while
writes to the file are allowed. Unlinking also introduces the
possibility of losing files, together with their permissions and
ownership, which could've been informative. This happens when one of
the writes gets interrupted or fails to create a file after unlinking.

All these issues can be fixed through the use of advisory locks on
systems that support them.

Roman.

^ permalink raw reply	[relevance 4%]

* Re: Any way to allow clobbering empty files when noclobber is set?
  2020-06-06 12:48  4%                 ` Roman Perepelitsa
@ 2020-06-06 15:24  3%                   ` Bart Schaefer
  0 siblings, 0 replies; 200+ results
From: Bart Schaefer @ 2020-06-06 15:24 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: Peter Stephenson, Zsh hackers list

On Sat, Jun 6, 2020 at 5:49 AM Roman Perepelitsa
<roman.perepelitsa@gmail.com> wrote:
>
> Having unspecified file content that is the result of interleaved
> writes seems pretty bad.
>
> [... code example ...]
>
> This output implies that one command has overwritten the output of
> another and the user has received no indication to this effect.

This returns to my point that this isn't the intended use case for
either NO_CLOBBER or CLOBBER_EMPTY.  That code example always yields
unspecified file contents in a POSIX shell if the empty file already
exists and is writable (the only case you couldn't have is the file
empty at the end).

If you're going to use zsh + noclobber for concurrent programming,
then the implementation of clobberempty is entirely irrelevant,
because your correct program won't use it.

If you're attempting to use zsh + noclobber defensively, that won't
work either because any other process not written in zsh won't pay
attention to it.

Consequently we should be looking at this as an entirely interactive
user DWIM feature, and choosing how to implement (or not to) based on
that.

^ permalink raw reply	[relevance 3%]

* Re: [PATCH v2] exec: run final pipeline command in a subshell in sh modeZZ
  2020-06-06  4:33  5%       ` [PATCH v2] exec: run final pipeline command in a subshell in sh modeZZ Daniel Shahaf
@ 2020-06-06 16:28  5%         ` brian m. carlson
  2020-06-07 11:29  4%           ` Daniel Shahaf
  0 siblings, 1 reply; 200+ results
From: brian m. carlson @ 2020-06-06 16:28 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Mikael Magnusson, zsh-workers

[-- Attachment #1: Type: text/plain, Size: 9241 bytes --]

On 2020-06-06 at 04:33:50, Daniel Shahaf wrote:
> brian m. carlson wrote on Fri, 05 Jun 2020 20:41 +0000:
> > On 2020-06-05 at 10:21:41, Mikael Magnusson wrote:
> > > On 6/5/20, brian m. carlson <sandals@crustytoothpaste.net> wrote:
> > > > zsh typically runs the final command in a pipeline in the main shell
> > > > instead of a subshell.  However, POSIX requires that all commands in a
> > > > pipeline run in a subshell, but permits zsh's behavior as an extension.
> > > 
> > > What POSIX actually says is:
> > > "each command of a multi-command pipeline is in a subshell
> > > environment; as an extension, however, any or all commands in a
> > > pipeline may be executed in the current environment"
> 
> That's quoted from https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12.
> 
> The part Brian quotes below is from https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap02.html#tag_02_01_01.
> 
> > > Ie, it does not say "shall", so it doesn't require a subshell all, in
> > > fact it explicitly does permit not using one as you also say. The
> 
> This interpretation is analogous to how conforming C programs must
> assume neither that «char» is signed nor that it is unsigned.

Right.  That term in C is "implementation defined."  POSIX has that term
as well, and it is not used here.  That term means that the
implementation may pick a behavior, but must document its choice.

> The sentence preceding the one you quoted reads:
> .
>     Non-standard extensions, when used, may change the behavior of
>     utilities, functions, or facilities defined by POSIX.1-2017.
> 
> I take this to mean non-standard extensions aren't bound by "shall"s.
> 
> As to why the passage Mikael quoted doesn't use the word "shall"… well,
> presumably it doesn't use the word "shall" because it doesn't describe
> "a feature or behavior that is mandatory"¹.

Sure, but if the standard didn't want that behavior to be specified
somehow, then it wouldn't have mentioned it.  Why wouldn't POSIX have
just omitted that statement and said nothing about it?

POSIX also says[0] that "[w]hen data is transmitted over the network, it
is sent as a sequence of octets (8-bit unsigned values)" and "16 and
32-bit values can be converted using the htonl(), htons(), ntohl(), and
ntohs() functions."  I don't think we can argue that POSIX permits one
to use 8-bit signed values or 9-bit values or that the implementation
can fail to make those functions work this way just because they didn't
use "shall".  The word "shall" is omitted (and "is" used) all over the
shell definitions to describe syntax forms, and one isn't permitted to
substitute some other syntax form in place of the standard one.

> > What POSIX does say is that one “shall define an environment in which an
> > application can be run with the behavior specified by POSIX.1-2017.”
> > I'm proposing that "zsh --emulate sh" implement the POSIX behavior for
> > that reason.
> 
> What Mikael's saying is that zsh's incumbent behaviour is already
> POSIX-conforming, but POSIX-conforming implementations have some leeway:
> have a range of possible behaviours to choose from, just like conforming
> C compilers can choose what signedness to give to «char».

I don't agree.  That behavior is implementation defined, and that has a
specific meaning.  Certainly implementations can implement additional
extensions, provided they don't conflict with the behavior specified in
POSIX.

> The passage Mikael quoted specifies that running the last command in
> a pipeline in a subshell by default is permitted in certain cases,
> outlined by the phrases "as an extension" and "may".
> 
> The definition of "may"¹ says it's used to describe "optional" behaviours,
> and that conforming applications should tolerate both presence and
> absence of that behaviour.

It says that an "application should not rely on the existence of the
feature or behavior."  It doesn't say that we can't rely on the absence
of that feature in a conforming environment.

> To summarize, I don't see why behaviour specified with the phrases "as
> an extension" and "may" should be off by default in a POSIX-conforming
> mode.  Would you elaborate on this?

Because the behavior materially differs between the behavior specified
declaratively (albeit without "shall") and the extension.  If we were
talking about situations where the behavior was a choice between
producing an error (that is, just failing) and producing a useful
output, then clearly nobody would care: just don't rely on the program
failing if you give it the syntax specified in an extension.

For example, the shell is permitted to recognize additional arithmetic
expressions as an extension.  It would be permissible for the shell to
understand the legacy C-style expressions like =* (instead of *=), but
when in POSIX mode, the following would need to print -4:

  sh -c 'x=2; : $((x =- 4)); echo $x'

For behaviors where there is no conflict, such as =*, then we could
always print 8 here, even in a POSIX mode:

  sh -c 'x=2; : $((x =* 4)); echo $x'

> (On the other hand, I'm not sure why they bothered to write the words
> "as an extension" there.  They don't seem to change the meaning one way
> or the other.)

In general, we have to assume standards authors (and legislators) wrote
the text for a reason and not to be wasteful with words.  Therefore, we
should assume there is a relevant difference in meaning.

> Well, perhaps there is something we can do to make their lives easier.
> 
> Continuing the analogy to C, gcc(1) has -fsigned-char/-funsigned-char
> flags to help unportable programs.  However, I hesitate to propose
> adding an option just for this: adding options is always easy to
> suggest, but not always a good idea.
> 
> Since zsh already incorporates a parser for sh scripts, perhaps we could
> write a tool that automatically adds parentheses to the last element in
> every pipeline.  That's not such a crazy idea: it already exists (in a
> much more general form) for C: http://coccinelle.lip6.fr/

I think if your goal is for people to change their code to work around
this when zsh is sh, they will simply not do so, even if that's an
option, because it doesn't work by default.  In Git alone, there are
over 240,000 lines of shell between code and tests.  Debian must contain
tens of millions more.  It's just not going to be achievable to get all
of those lines changed to work this way.

If I were to add an option that were off by default for sh and on for
zsh, then that would meet my needs, and I'd be happy to implement that.
You seem to be unexcited about that possibility, though.

> > zsh is a very popular interactive shell, and allowing it to be used as a
> > portable sh on systems where the system sh is less capable would be
> > really beneficial.
> 
> How would it be beneficial?

It's already present on a lot of those systems and it avoids the need to
build one shell for interactive use and another for portable scripting.
zsh is also appealing as a portable sh because it has a pleasant
interactive mode, whereas many sh implementations (e.g., dash) do not.

> > If your objection is to the wording, I'm happy to revise it to remove
> > the word "requires", but I do think this provides a lot of benefits for
> > the sh scripting case while not impacting users who are expecting
> > different behavior for the zsh case.
> 
> The patch would constitute a backwards-incompatible change to anyone who
> uses zsh as sh today and relies on the current behaviour of pipelines.

The thing is, I don't believe anyone does, except for the possibility of
macOS[1].  I have tried zsh as sh on Debian and many things are broken
(including debconf).  I'm not aware of any other supported operating
systems[2] where a user using zsh as /bin/sh is permitted as an option.

I should also point out that when people write "emulate sh" that they
probably very much want to emulate the behavior of /bin/sh on their
system.  I'm not aware of any supported system in existence where the
default /bin/sh (or the default POSIX sh, when /bin/sh is not
POSIX-compatible) has the zsh behavior; they all run all pipeline stages
in a subshell.

I want to be clear that I don't want to change the behavior of the zsh
mode, where I agree a change would be undesirable and people are almost
certainly relying on the current behavior.

> This might have been acceptable if it were a question of changing
> a non-conforming behaviour to a conforming behaviour.  However, the
> current behaviour does appear to be conforming.

I'm not in agreement that a shell which provides only zsh's behavior is
conforming in this case.

[0] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html
[1] And macOS users are not relying on this behavior from zsh as sh
    because bash and dash are also valid sh options.
[2] That is, operating systems in versions which still receive security
    support from their vendor.
-- 
brian m. carlson: Houston, Texas, US
OpenPGP: https://keybase.io/bk2204

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 263 bytes --]

^ permalink raw reply	[relevance 5%]

* Re: [PATCH v2] exec: run final pipeline command in a subshell in sh modeZZ
  2020-06-06 16:28  5%         ` brian m. carlson
@ 2020-06-07 11:29  4%           ` Daniel Shahaf
  0 siblings, 0 replies; 200+ results
From: Daniel Shahaf @ 2020-06-07 11:29 UTC (permalink / raw)
  To: brian m. carlson; +Cc: Mikael Magnusson, zsh-workers

brian m. carlson wrote on Sat, 06 Jun 2020 16:28 +0000:
> On 2020-06-06 at 04:33:50, Daniel Shahaf wrote:
> > brian m. carlson wrote on Fri, 05 Jun 2020 20:41 +0000:  
> > > On 2020-06-05 at 10:21:41, Mikael Magnusson wrote:  
> > > > On 6/5/20, brian m. carlson <sandals@crustytoothpaste.net> wrote:  
> > > > > zsh typically runs the final command in a pipeline in the main shell
> > > > > instead of a subshell.  However, POSIX requires that all commands in a
> > > > > pipeline run in a subshell, but permits zsh's behavior as an extension.  
> > > > 
> > > > What POSIX actually says is:
> > > > "each command of a multi-command pipeline is in a subshell
> > > > environment; as an extension, however, any or all commands in a
> > > > pipeline may be executed in the current environment"  
> > 
> > That's quoted from https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12.
> > 
> > The part Brian quotes below is from https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap02.html#tag_02_01_01.
> >   
> > > > Ie, it does not say "shall", so it doesn't require a subshell all, in
> > > > fact it explicitly does permit not using one as you also say. The  
> > 
> > This interpretation is analogous to how conforming C programs must
> > assume neither that «char» is signed nor that it is unsigned.  
> 
> Right.  That term in C is "implementation defined."  POSIX has that term
> as well, and it is not used here.  That term means that the
> implementation may pick a behavior, but must document its choice.
> 
> > The sentence preceding the one you quoted reads:
> > .
> >     Non-standard extensions, when used, may change the behavior of
> >     utilities, functions, or facilities defined by POSIX.1-2017.
> > 
> > I take this to mean non-standard extensions aren't bound by "shall"s.
> > 
> > As to why the passage Mikael quoted doesn't use the word "shall"… well,
> > presumably it doesn't use the word "shall" because it doesn't describe
> > "a feature or behavior that is mandatory"¹.  
> 
> Sure, but if the standard didn't want that behavior to be specified
> somehow, then it wouldn't have mentioned it.  Why wouldn't POSIX have
> just omitted that statement and said nothing about it?

Perhaps because POSIX tries to first describe how an abstract or common
implementation behaves, and then proceeds to describe a set of
alternative behaviours known to be used by some implementations.

For example, IIRC C doesn't specify the signedness of «char» because, at
the time C was standardized, some platforms used signed chars and other
used unsigned chars, and it was desired to make both kinds of platforms
conformant.

> POSIX also says[0] that "[w]hen data is transmitted over the network, it
> is sent as a sequence of octets (8-bit unsigned values)" and "16 and
> 32-bit values can be converted using the htonl(), htons(), ntohl(), and
> ntohs() functions."  I don't think we can argue that POSIX permits one
> to use 8-bit signed values or 9-bit values or that the implementation
> can fail to make those functions work this way just because they didn't
> use "shall".  The word "shall" is omitted (and "is" used) all over the
> shell definitions to describe syntax forms, and one isn't permitted to
> substitute some other syntax form in place of the standard one.

So you're saying that wherever POSIX says "is" it is to be read as
"shall", if I understand correctly?  That's a fair argument, but I'm not
sure whether I agree.

> > > What POSIX does say is that one “shall define an environment in which an
> > > application can be run with the behavior specified by POSIX.1-2017.”
> > > I'm proposing that "zsh --emulate sh" implement the POSIX behavior for
> > > that reason.  
> > 
> > What Mikael's saying is that zsh's incumbent behaviour is already
> > POSIX-conforming, but POSIX-conforming implementations have some leeway:
> > have a range of possible behaviours to choose from, just like conforming
> > C compilers can choose what signedness to give to «char».  
> 
> I don't agree.  That behavior is implementation defined, and that has a
> specific meaning.  Certainly implementations can implement additional
> extensions, provided they don't conflict with the behavior specified in
> POSIX.
> 

Could you please clarify what exactly is implementation-defined here,
according to your reading?  What decision in this are implementors
supposed to make for themselves and document for their users?

In any case, our readings of the standards differ.  How can we figure
out what the correct interpretation is?  Is there background information
on Austin Group's bug tracker or mailing lists, for example?  Or can we
just ask them?

> > The passage Mikael quoted specifies that running the last command in
> > a pipeline in a subshell by default is permitted in certain cases,
> > outlined by the phrases "as an extension" and "may".
> > 
> > The definition of "may"¹ says it's used to describe "optional" behaviours,
> > and that conforming applications should tolerate both presence and
> > absence of that behaviour.  
> 
> It says that an "application should not rely on the existence of the
> feature or behavior."  It doesn't say that we can't rely on the absence
> of that feature in a conforming environment.

If "may" describes a feature on whose _absence_ conforming applications
may rely, then what's the difference between "may" and "shall not"?  And
between their respective opposites, "need not" and "shall"?

For example, consider this bit from [2.3.1]: "Implementations also may
provide predefined valid aliases that are in effect when the shell is
invoked."  If conforming applications can rely on the absence of
predefined aliases, that would imply that conforming implementations
must not predefine aliases.

[2.3.1] https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_03_01

> > To summarize, I don't see why behaviour specified with the phrases "as
> > an extension" and "may" should be off by default in a POSIX-conforming
> > mode.  Would you elaborate on this?  
> 
> Because the behavior materially differs between the behavior specified
> declaratively (albeit without "shall") and the extension.  If we were
> talking about situations where the behavior was a choice between
> producing an error (that is, just failing) and producing a useful
> output, then clearly nobody would care: just don't rely on the program
> failing if you give it the syntax specified in an extension.
> 
> For example, the shell is permitted to recognize additional arithmetic
> expressions as an extension.  It would be permissible for the shell to
> understand the legacy C-style expressions like =* (instead of *=), but
> when in POSIX mode, the following would need to print -4:
> 
>   sh -c 'x=2; : $((x =- 4)); echo $x'
> 
> For behaviors where there is no conflict, such as =*, then we could
> always print 8 here, even in a POSIX mode:
> 
>   sh -c 'x=2; : $((x =* 4)); echo $x'

Thanks.  I understand your argument; not sure yet whether I agree with it.

> > (On the other hand, I'm not sure why they bothered to write the words
> > "as an extension" there.  They don't seem to change the meaning one way
> > or the other.)  
> 
> In general, we have to assume standards authors (and legislators) wrote
> the text for a reason and not to be wasteful with words.  Therefore, we
> should assume there is a relevant difference in meaning.

Agreed.

That's exactly why I questioned whether the behaviour in question was
a "non-standard extension": I was trying to interpret the term
'non-standard' within the phrase 'non-standard extension' as
non-superfluous.

> > Well, perhaps there is something we can do to make their lives easier.
> > 
> > Continuing the analogy to C, gcc(1) has -fsigned-char/-funsigned-char
> > flags to help unportable programs.  However, I hesitate to propose
> > adding an option just for this: adding options is always easy to
> > suggest, but not always a good idea.
> > 
> > Since zsh already incorporates a parser for sh scripts, perhaps we could
> > write a tool that automatically adds parentheses to the last element in
> > every pipeline.  That's not such a crazy idea: it already exists (in a
> > much more general form) for C: http://coccinelle.lip6.fr/  
> 
> I think if your goal is for people to change their code to work around
> this when zsh is sh, they will simply not do so, even if that's an
> option, because it doesn't work by default.  In Git alone, there are
> over 240,000 lines of shell between code and tests.  Debian must contain
> tens of millions more.  It's just not going to be achievable to get all
> of those lines changed to work this way.
> 
> If I were to add an option that were off by default for sh and on for
> zsh, then that would meet my needs, and I'd be happy to implement that.
> You seem to be unexcited about that possibility, though.

I'm not sure you understood my point of view precisely.

What I was saying [in my previous message, based on my understanding at
the time, not taking into account your latest reply] was:

- The long-term solution is for people to add parentheses around their
  pipeline elements.

- That solution can be implemented mechanically.

- As a stopgap measure, we can consider enabling the patch's behaviour
  in sh mode _as an opt-in_.
  
  Notwithstanding the opt-in aspect, I'm sure we can figure out a way to
  arrange things so random third party code that runs /bin/sh will be
  served by zsh in sh emulation mode with the patch's behaviour already
  on, if that's what the sysadmin or third-party maintainer want.

> > > zsh is a very popular interactive shell, and allowing it to be used as a
> > > portable sh on systems where the system sh is less capable would be
> > > really beneficial.  
> > 
> > How would it be beneficial?  
> 
> It's already present on a lot of those systems and it avoids the need to
> build one shell for interactive use and another for portable scripting.
> zsh is also appealing as a portable sh because it has a pleasant
> interactive mode, whereas many sh implementations (e.g., dash) do not.
> 

Thanks.

> > > If your objection is to the wording, I'm happy to revise it to remove
> > > the word "requires", but I do think this provides a lot of benefits for
> > > the sh scripting case while not impacting users who are expecting
> > > different behavior for the zsh case.  
> > 
> > The patch would constitute a backwards-incompatible change to anyone who
> > uses zsh as sh today and relies on the current behaviour of pipelines.  
> 
> The thing is, I don't believe anyone does, except for the possibility of
> macOS[1].

https://en.wikipedia.org/wiki/No_true_Scotsman

> I have tried zsh as sh on Debian and many things are broken
> (including debconf).  I'm not aware of any other supported operating
> systems[2] where a user using zsh as /bin/sh is permitted as an option.

And I'm not aware of any regulars on this list who have symlinked
/bin/sh to zsh independently of their OS vendor's configuration options.

> I should also point out that when people write "emulate sh" that they
> probably very much want to emulate the behavior of /bin/sh on their
> system.

Personally, when I write «emulate sh» I would expect to get, not what
bash does as sh or what dash does as sh, but what POSIX specifies sh
should do.

> I'm not aware of any supported system in existence where the
> default /bin/sh (or the default POSIX sh, when /bin/sh is not
> POSIX-compatible) has the zsh behavior; they all run all pipeline stages
> in a subshell.
> 
> I want to be clear that I don't want to change the behavior of the zsh
> mode, where I agree a change would be undesirable and people are almost
> certainly relying on the current behavior.

Thanks for clarifying this.

> > This might have been acceptable if it were a question of changing
> > a non-conforming behaviour to a conforming behaviour.  However, the
> > current behaviour does appear to be conforming.  
> 
> I'm not in agreement that a shell which provides only zsh's behavior is
> conforming in this case.

Okay, so see above re how to resolve our differing interpretations.

Cheers,

Daniel

> [0] https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html
> [1] And macOS users are not relying on this behavior from zsh as sh
>     because bash and dash are also valid sh options.
> [2] That is, operating systems in versions which still receive security
>     support from their vendor.


^ permalink raw reply	[relevance 4%]

* Re: bad math expression: illegal character: "
  @ 2020-06-17 23:21  3%   ` Bart Schaefer
  2020-06-18 11:19  3%     ` Mikael Magnusson
  0 siblings, 1 reply; 200+ results
From: Bart Schaefer @ 2020-06-17 23:21 UTC (permalink / raw)
  To: zsh-workers

On Fri, Jun 5, 2020 at 2:02 PM Lawrence Velázquez <vq@larryv.me> wrote:
>
> > On Jun 5, 2020, at 2:18 PM, Artur Renault <Artur.Renault@microsoft.com>
> >
> > while (( "$#" )); do
> >
> > repro.sh:1: bad math expression: illegal character: "
>
> For what it's worth, dash also rejects this. I assume that it and
> zsh are just more strict about what constitutes a math expression.

Does anybody know if this is something we should change for sh
emulation, or is it merely another unspecified POSIX corner-case?

^ permalink raw reply	[relevance 3%]

* Re: bad math expression: illegal character: "
  2020-06-17 23:21  3%   ` Bart Schaefer
@ 2020-06-18 11:19  3%     ` Mikael Magnusson
  2020-06-19  6:38  3%       ` Lawrence Velázquez
  0 siblings, 1 reply; 200+ results
From: Mikael Magnusson @ 2020-06-18 11:19 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: zsh-workers

On 6/18/20, Bart Schaefer <schaefer@brasslantern.com> wrote:
> On Fri, Jun 5, 2020 at 2:02 PM Lawrence Velázquez <vq@larryv.me> wrote:
>>
>> > On Jun 5, 2020, at 2:18 PM, Artur Renault <Artur.Renault@microsoft.com>
>> >
>> > while (( "$#" )); do
>> >
>> > repro.sh:1: bad math expression: illegal character: "
>>
>> For what it's worth, dash also rejects this. I assume that it and
>> zsh are just more strict about what constitutes a math expression.
>
> Does anybody know if this is something we should change for sh
> emulation, or is it merely another unspecified POSIX corner-case?

If this is a POSIX requirement, it must have been very specific about
when quotes should be allowed (bash):

$ (( "0" )); echo $?
1
$ echo $(( "0" ))
bash: "0" : syntax error: operand expected (error token is ""0" ")


-- 
Mikael Magnusson

^ permalink raw reply	[relevance 3%]

* Re: bad math expression: illegal character: "
  2020-06-18 11:19  3%     ` Mikael Magnusson
@ 2020-06-19  6:38  3%       ` Lawrence Velázquez
  2020-06-19 15:35  0%         ` Sebastian Gniazdowski
  2020-06-19 15:55  7%         ` Peter Stephenson
  0 siblings, 2 replies; 200+ results
From: Lawrence Velázquez @ 2020-06-19  6:38 UTC (permalink / raw)
  To: Mikael Magnusson; +Cc: Bart Schaefer, zsh-workers

> On Jun 18, 2020, at 7:19 AM, Mikael Magnusson <mikachu@gmail.com> wrote:
> 
>> On 6/18/20, Bart Schaefer <schaefer@brasslantern.com> wrote:
>> 
>>> On Fri, Jun 5, 2020 at 2:02 PM Lawrence Velázquez <vq@larryv.me> wrote:
>>> 
>>>> On Jun 5, 2020, at 2:18 PM, Artur Renault <Artur.Renault@microsoft.com>
>>>> 
>>>> while (( "$#" )); do
>>>> 
>>>> repro.sh:1: bad math expression: illegal character: "
>>> 
>>> For what it's worth, dash also rejects this. I assume that it and
>>> zsh are just more strict about what constitutes a math expression.
>> 
>> Does anybody know if this is something we should change for sh
>> emulation, or is it merely another unspecified POSIX corner-case?
> 
> If this is a POSIX requirement, it must have been very specific about
> when quotes should be allowed (bash):
> 
> $ (( "0" )); echo $?
> 1
> $ echo $(( "0" ))
> bash: "0" : syntax error: operand expected (error token is ""0" ")

Looks like the bash behavior changed at some point?

    % /bin/bash --version | head -n 1
    GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
    % /bin/bash -c '(( "0" )); echo $?'
    1
    % /bin/bash -c 'echo $(( "0" ))'
    /bin/bash: "0" : syntax error: operand expected (error token is ""0" ")

    % /opt/local/bin/bash --version | head -n 1
    GNU bash, version 5.0.17(1)-release (x86_64-apple-darwin18.7.0)
    % /opt/local/bin/bash -c '(( "0" )); echo $?'
    1
    % /opt/local/bin/bash -c 'echo $(( "0" ))'
    0

Anyway, unless I'm missing something, POSIX seems pretty clear about
this, as far as $((...)) goes:

    The expression shall be treated as if it were in double-quotes,
    except that a double-quote inside the expression is not treated
    specially. The shell shall expand all tokens in the expression
    for parameter expansion, command substitution, and **quote
    removal**. [Emphasis mine.]

    Next, the shell shall treat this as an arithmetic expression
    and substitute the value of the expression.

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_04

vq

^ permalink raw reply	[relevance 3%]

* Re: bad math expression: illegal character: "
  2020-06-19  6:38  3%       ` Lawrence Velázquez
@ 2020-06-19 15:35  0%         ` Sebastian Gniazdowski
  2020-06-19 15:55  7%         ` Peter Stephenson
  1 sibling, 0 replies; 200+ results
From: Sebastian Gniazdowski @ 2020-06-19 15:35 UTC (permalink / raw)
  To: Lawrence Velázquez; +Cc: Mikael Magnusson, Bart Schaefer, Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 2662 bytes --]

Maybe a bash emulation could be added? Equal to:

    emulate zsh -o noshglob -o braceexpand -o kshglob

+ the quotes inside (( ))? emulate already accepts "bash" as the emulation
level.

On Fri, 19 Jun 2020 at 08:39, Lawrence Velázquez <vq@larryv.me> wrote:

> > On Jun 18, 2020, at 7:19 AM, Mikael Magnusson <mikachu@gmail.com> wrote:
> >
> >> On 6/18/20, Bart Schaefer <schaefer@brasslantern.com> wrote:
> >>
> >>> On Fri, Jun 5, 2020 at 2:02 PM Lawrence Velázquez <vq@larryv.me>
> wrote:
> >>>
> >>>> On Jun 5, 2020, at 2:18 PM, Artur Renault <
> Artur.Renault@microsoft.com>
> >>>>
> >>>> while (( "$#" )); do
> >>>>
> >>>> repro.sh:1: bad math expression: illegal character: "
> >>>
> >>> For what it's worth, dash also rejects this. I assume that it and
> >>> zsh are just more strict about what constitutes a math expression.
> >>
> >> Does anybody know if this is something we should change for sh
> >> emulation, or is it merely another unspecified POSIX corner-case?
> >
> > If this is a POSIX requirement, it must have been very specific about
> > when quotes should be allowed (bash):
> >
> > $ (( "0" )); echo $?
> > 1
> > $ echo $(( "0" ))
> > bash: "0" : syntax error: operand expected (error token is ""0" ")
>
> Looks like the bash behavior changed at some point?
>
>     % /bin/bash --version | head -n 1
>     GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
>     % /bin/bash -c '(( "0" )); echo $?'
>     1
>     % /bin/bash -c 'echo $(( "0" ))'
>     /bin/bash: "0" : syntax error: operand expected (error token is ""0" ")
>
>     % /opt/local/bin/bash --version | head -n 1
>     GNU bash, version 5.0.17(1)-release (x86_64-apple-darwin18.7.0)
>     % /opt/local/bin/bash -c '(( "0" )); echo $?'
>     1
>     % /opt/local/bin/bash -c 'echo $(( "0" ))'
>     0
>
> Anyway, unless I'm missing something, POSIX seems pretty clear about
> this, as far as $((...)) goes:
>
>     The expression shall be treated as if it were in double-quotes,
>     except that a double-quote inside the expression is not treated
>     specially. The shell shall expand all tokens in the expression
>     for parameter expansion, command substitution, and **quote
>     removal**. [Emphasis mine.]
>
>     Next, the shell shall treat this as an arithmetic expression
>     and substitute the value of the expression.
>
>
> https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_04
>
> vq



-- 
Sebastian Gniazdowski
News: https://twitter.com/ZdharmaI
IRC: https://kiwiirc.com/client/chat.freenode.net:+6697/#zinit
Blog: http://zdharma.org

^ permalink raw reply	[relevance 0%]

* Re: bad math expression: illegal character: "
  2020-06-19  6:38  3%       ` Lawrence Velázquez
  2020-06-19 15:35  0%         ` Sebastian Gniazdowski
@ 2020-06-19 15:55  7%         ` Peter Stephenson
  2020-06-23 23:51  3%           ` Bart Schaefer
  1 sibling, 1 reply; 200+ results
From: Peter Stephenson @ 2020-06-19 15:55 UTC (permalink / raw)
  To: Lawrence Velázquez, Mikael Magnusson; +Cc: Bart Schaefer, zsh-workers

> On 19 June 2020 at 07:38 Lawrence Velázquez <vq@larryv.me> wrote:
> Anyway, unless I'm missing something, POSIX seems pretty clear about
> this, as far as $((...)) goes:
> 
>     The expression shall be treated as if it were in double-quotes,
>     except that a double-quote inside the expression is not treated
>     specially. The shell shall expand all tokens in the expression
>     for parameter expansion, command substitution, and **quote
>     removal**. [Emphasis mine.]
> 
>     Next, the shell shall treat this as an arithmetic expression
>     and substitute the value of the expression.
> 
> https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_04

It's straightforward just to treat double quotes as if they're a
random spacing character.  Not sure the error is actually useful?
There was a test for it but that was simply to make sure we did
something with them.

pws

diff --git a/Src/math.c b/Src/math.c
index 905b910ec..b57ba42d4 100644
--- a/Src/math.c
+++ b/Src/math.c
@@ -831,6 +831,8 @@ zzlex(void)
 	case ' ': /* Fall through! */
 	case '\t':
 	case '\n':
+	case '"': /* POSIX says ignore these */
+	case Dnull:
 	    break;
 	default:
 	    if (idigit(*--ptr) || *ptr == '.')
diff --git a/Test/C01arith.ztst b/Test/C01arith.ztst
index 419f45292..d0092fefa 100644
--- a/Test/C01arith.ztst
+++ b/Test/C01arith.ztst
@@ -180,9 +180,10 @@
 1:bases beyond 36 don't work
 ?(eval):1: invalid base (must be 2 to 36 inclusive): 37
 
+  fail=39
   print $(( 3 + "fail" ))
-1:parse failure in arithmetic
-?(eval):1: bad math expression: operand expected at `"fail" '
+0:Double quotes are not treated specially in arithmetic
+>42
 
   alias 3=echo
   print $(( 3 + "OK"); echo "Worked")
@@ -487,3 +488,8 @@
   let noexist==0 )
 1:Arithmetic, NO_UNSET part 3
 ?(eval):2: noexist: parameter not set
+
+  print $(( "6+2" / "1+3" ))
+0:Double quotes are not treated specially in arithmetic (POSIX)
+# and do not do grouping!  this is 6 + (2/1) + 3
+>11

^ permalink raw reply	[relevance 7%]

* support for POSIX job control terminal management in zsh
@ 2020-06-23  1:39  9% Godmar Back
  2020-06-23  1:53 10% ` Bart Schaefer
  0 siblings, 1 reply; 200+ results
From: Godmar Back @ 2020-06-23  1:39 UTC (permalink / raw)
  To: zsh-workers

Hi,

I noticed that zsh does not implement POSIX.1 when it comes to
saving/restoring the terminal state of processes suspended with
SIGTSTP.  (The same applies to bash, btw, but not to ksh.)  Shells
like tcsh or ash/dash also do not implement it.

POSIX.1 asks that:

"When a foreground (not background) job stops, the shell must sample
and remember the current terminal settings so that it can restore them
later when it continues the stopped job in the foreground (via the
tcgetattr( ) and tcsetattr( ) functions)."

Consequently, a program such as the one attached below should not fail
any assertions.
However, it does not appear to work when run under zsh.

I've had an illuminating discussion with Chet Ramey, the author of
bash, regarding bash's behavior. He pointed out that applications
historically could never rely on the shell provided job control in a
manner that they could be oblivious to being suspended/resumed, and
therefore always had to handle the saving/restoring of the terminal
state upon suspend/restore themselves, rather than relying on the job
control shell as POSIX asks.

Still, at least one shell (ksh) does appear to implement it.

Hence my question, out of curiosity, is: why does zsh not follow
POSIX.1 semantics in this regard?

Thanks.

 - Godmar

// ts_test.c
/* Test that terminal state is properly restored when a process is
stopped and restored. */
#include <stdio.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <signal.h>

#define CTRL_D  4
#define CTRL_E  5

int
main()
{
    int terminal_fd = open(ctermid(NULL), O_RDWR);
    assert (terminal_fd != -1);

    // Step 1. make a change to the terminal state:
    // change VEOF from Ctrl-D to Ctrl-E
    struct termios saved_tty_state;
    int rc = tcgetattr(terminal_fd, &saved_tty_state);
    assert (rc == 0);

    assert (saved_tty_state.c_cc[VEOF] == CTRL_D);       // ^D
    saved_tty_state.c_cc[VEOF] = CTRL_E;                 // ^E
    rc = tcsetattr(terminal_fd, TCSANOW, &saved_tty_state);
    assert (rc == 0);

    // Step 2.  Suspend and let user resume
    printf("This job should now stop, please run 'fg' to continue it\n");
    raise(SIGTSTP);
    printf("Job now continuing...\n");

    // Step 3.
    // Expect that job control shell saved the terminal state
    rc = tcgetattr(terminal_fd, &saved_tty_state);
    assert (rc == 0);
    if (saved_tty_state.c_cc[VEOF] != CTRL_E) {
        printf("I expected a POSIX job control shell to preserve my
terminal settings\n");
        printf("VEOF was not saved, it is %d...\n", saved_tty_state.c_cc[VEOF]);
    }

    assert (saved_tty_state.c_cc[VEOF] == CTRL_E);       // ^E
}

^ permalink raw reply	[relevance 9%]

* Re: support for POSIX job control terminal management in zsh
  2020-06-23  1:39  9% support for POSIX job control terminal management in zsh Godmar Back
@ 2020-06-23  1:53 10% ` Bart Schaefer
  0 siblings, 0 replies; 200+ results
From: Bart Schaefer @ 2020-06-23  1:53 UTC (permalink / raw)
  To: Godmar Back; +Cc: zsh-workers

On Mon, Jun 22, 2020 at 6:40 PM Godmar Back <godmar@gmail.com> wrote:
>
> Hence my question, out of curiosity, is: why does zsh not follow
> POSIX.1 semantics in this regard?

The direct answer is because zsh didn't even begin to claim any level
of POSIX compliance until those constraints began to be added to the
"sh" emulation mode, more than 10 years after the shell was written.

The indirect answer is because this is a pretty dark corner case, and
there is no organized effort to examine the entire POSIX spec for
items that haven't yet been included.  What effort there has been has
been focused on shell scripting more than interactive use.

^ permalink raw reply	[relevance 10%]

* Re: bad math expression: illegal character: "
  2020-06-19 15:55  7%         ` Peter Stephenson
@ 2020-06-23 23:51  3%           ` Bart Schaefer
  0 siblings, 0 replies; 200+ results
From: Bart Schaefer @ 2020-06-23 23:51 UTC (permalink / raw)
  To: Peter Stephenson; +Cc: Lawrence Velázquez, Mikael Magnusson, zsh-workers

Sorry for the very delayed response on this, given that it has already
been committed, but:

On Fri, Jun 19, 2020 at 8:55 AM Peter Stephenson
<p.w.stephenson@ntlworld.com> wrote:
>
> It's straightforward just to treat double quotes as if they're a
> random spacing character.

Doesn't the POSIX requirement say "not treated specially"?  Treating
them as spaces rather than as themselves seems like "special"
treatment to me.  The original error might have been more in the
spirit of the directions.  On the other hand they're meaningless as
themselves, except to cause that very error, so I don't really have an
objection.

^ permalink raw reply	[relevance 3%]

* command -p should enable builtins not in path
@ 2020-08-20 11:28  3% Vincent Lefevre
  2020-08-21 13:03  0% ` Peter Stephenson
  2020-08-21 15:47  3% ` Martijn Dekker
  0 siblings, 2 replies; 200+ results
From: Vincent Lefevre @ 2020-08-20 11:28 UTC (permalink / raw)
  To: zsh-workers

Though zsh isn't meant to conform to POSIX, it should follow its
requirements when they make sense.

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html

says:

  The following options shall be supported:

  -p
    Perform the command search using a default value for PATH that is
    guaranteed to find all of the standard utilities.
                       ^^^

So, in particular, the standard utility "cd" must be found. Note that
the above sentence does not mean that the utility must be somewhere
in $PATH, just that the used PATH value allows the shell to find the
utility; in case of a builtin utility (like "cd"), this will just run
the utility without needing PATH.

This works with all usual shells, except zsh:

zira% sh -c "command -p cd /tmp; pwd"
/tmp
zira% bash -c "command -p cd /tmp; pwd"
/tmp
zira% ksh -c "command -p cd /tmp; pwd"
/tmp
zira% zsh -c "command -p cd /tmp; pwd"
zsh:1: command not found: cd
/home/vinc17
zira% echo $ZSH_VERSION
5.8

Setting the POSIX_BUILTINS option allows "cd" to be found, but with
the drawback that it will not disable builtins that are in $PATH.
So this option is a bad solution when using "zmodload zsh/files"
without -F, for instance.

Note: "command -p cd ..." is used by Intel's script to set up
environment variables for its compiler.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)


^ permalink raw reply	[relevance 3%]

* Re: command -p should enable builtins not in path
  2020-08-20 11:28  3% command -p should enable builtins not in path Vincent Lefevre
@ 2020-08-21 13:03  0% ` Peter Stephenson
    2020-08-21 15:47  3% ` Martijn Dekker
  1 sibling, 1 reply; 200+ results
From: Peter Stephenson @ 2020-08-21 13:03 UTC (permalink / raw)
  To: zsh-workers

> On 20 August 2020 at 12:28 Vincent Lefevre <vincent@vinc17.net> wrote:
> 
> 
> Though zsh isn't meant to conform to POSIX, it should follow its
> requirements when they make sense.
> 
> https://pubs.opengroup.org/onlinepubs/9699919799/utilities/command.html
> 
> says:
> 
>   The following options shall be supported:
> 
>   -p
>     Perform the command search using a default value for PATH that is
>     guaranteed to find all of the standard utilities.
>                        ^^^
> 
> So, in particular, the standard utility "cd" must be found. Note that
> the above sentence does not mean that the utility must be somewhere
> in $PATH, just that the used PATH value allows the shell to find the
> utility; in case of a builtin utility (like "cd"), this will just run
> the utility without needing PATH.
> 
> This works with all usual shells, except zsh:

I tend to agree there's not a lot of point in this incompatibility,
given where the "-p" flag originates --- it's a relatively late
addition to zsh.

Here's one possible fix.

pws

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 4b91db1fe..17ebba2ac 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -318,10 +318,15 @@ item(tt(command) [ tt(-pvV) ] var(simple command))(
 The simple command argument is taken as an external command instead of
 a function or builtin and is executed. If the tt(POSIX_BUILTINS) option
 is set, builtins will also be executed but certain special properties
-of them are suppressed. The tt(-p) flag causes a default path to be
-searched instead of that in tt($path). With the tt(-v) flag, tt(command)
-is similar to tt(whence) and with tt(-V), it is equivalent to tt(whence
--v).
+of them are suppressed.
+
+The tt(-p) flag causes a default path to be searched instead of that in
+tt($path).  The intention of the option is that all standard commands
+may be found, so builtins are also checked, but not other types of
+command such as functions.
+
+With the tt(-v) flag, tt(command) is similar to tt(whence) and with
+tt(-V), it is equivalent to tt(whence -v).
 
 See also ifzman(the section `Precommand Modifiers' in zmanref(zshmisc))\
 ifnzman(noderef(Precommand Modifiers)).
diff --git a/Src/exec.c b/Src/exec.c
index ecad923de..f18513703 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -3165,8 +3165,19 @@ execcmd_exec(Estate state, Execcmd_params eparams,
 		}
 	    }
 	    hn = NULL;
-	    if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS))
+	    if ((cflags & BINF_COMMAND) && unset(POSIXBUILTINS)) {
+		if (use_defpath && nonempty(preargs)) {
+		    /*
+		     * command -p can find builtins as these are
+		     * in a standard location.  So check.
+		     */
+		    if ((hn = builtintab->getnode(builtintab,
+						  (char *) peekfirst(preargs)))) {
+			checked = is_builtin = 1;
+		    }
+		}
 		break;
+	    }
 	    if (!nonempty(preargs))
 		execcmd_getargs(preargs, args, eparams->htok);
 	}
diff --git a/Test/A01grammar.ztst b/Test/A01grammar.ztst
index 35a04e7d5..e62fb531b 100644
--- a/Test/A01grammar.ztst
+++ b/Test/A01grammar.ztst
@@ -200,6 +200,17 @@
 >cat is /*/cat
 >echo is a shell builtin
 
+  (mkdir testdir_for_command-p
+   command -p cd testdir_for_command-p
+   print ${PWD##*/})
+0:command -p finds builtins
+>testdir_for_command-p
+
+  func_not_searched() { This was not found; }
+  command -p func_not_searched
+127:command -p does not find functions
+?(eval):2: command not found: func_not_searched
+
   cd() { echo Not cd at all; }
   builtin cd . && unfunction cd
 0:`builtin' precommand modifier


^ permalink raw reply	[relevance 0%]

* Re: command -p should enable builtins not in path
  2020-08-20 11:28  3% command -p should enable builtins not in path Vincent Lefevre
  2020-08-21 13:03  0% ` Peter Stephenson
@ 2020-08-21 15:47  3% ` Martijn Dekker
  1 sibling, 0 replies; 200+ results
From: Martijn Dekker @ 2020-08-21 15:47 UTC (permalink / raw)
  To: zsh-workers

Op 20-08-20 om 12:28 schreef Vincent Lefevre:
> Though zsh isn't meant to conform to POSIX, it should follow its
> requirements when they make sense.


Just to clarify, 'zsh --emulate sh' *is* meant to conform to POSIX, correct?

[...]
> Setting the POSIX_BUILTINS option allows "cd" to be found, but with
> the drawback that it will not disable builtins that are in $PATH.
> So this option is a bad solution when using "zmodload zsh/files"
> without -F, for instance.
> 
> Note: "command -p cd ..." is used by Intel's script to set up
> environment variables for its compiler.


If it wasn't written for native zsh, it probably has other 
incompatibilities as well. Wouldn't 'sticky emulation' allow you to 
seamlessly mix that code with native zsh code?

- M.

-- 
||	modernish -- harness the shell
||	https://github.com/modernish/modernish
||
||	KornShell lives!
||	https://github.com/ksh93/ksh


^ permalink raw reply	[relevance 3%]

* Re: command -p should enable builtins not in path
  @ 2020-08-21 16:35  3%       ` Martijn Dekker
  0 siblings, 0 replies; 200+ results
From: Martijn Dekker @ 2020-08-21 16:35 UTC (permalink / raw)
  To: Peter Stephenson, zsh-workers

Op 21-08-20 om 17:08 schreef Peter Stephenson:
> However, "command" with no option is a very long-standing piece of zsh
> syntax to pick an external command, so that's certainly not going to change.

Of course. So what I'm trying to suggest is that this change to -p is 
maybe not a very good idea either. There is a long-standing expectation 
that 'command' runs an external command in native zsh mode, so why 
should that be different just because you're searching the default path?

Code that expects POSIX behaviour, such as that Intel build script, 
shouldn't be run in native zsh mode anyway. If it needs to cooperate 
with native zsh code, it can be loaded with sticky emulation.

- M.

-- 
||	modernish -- harness the shell
||	https://github.com/modernish/modernish
||
||	KornShell lives!
||	https://github.com/ksh93/ksh


^ permalink raw reply	[relevance 3%]

* Re: command -p should enable builtins not in path
    @ 2020-08-24 18:30  3%     ` Vincent Lefevre
  2020-08-24 19:59  0%       ` Martijn Dekker
  1 sibling, 1 reply; 200+ results
From: Vincent Lefevre @ 2020-08-24 18:30 UTC (permalink / raw)
  To: zsh-workers

On 2020-08-21 16:49:06 +0100, Martijn Dekker wrote:
> Op 21-08-20 om 14:03 schreef Peter Stephenson:
> > I tend to agree there's not a lot of point in this incompatibility,
> > given where the "-p" flag originates --- it's a relatively late
> > addition to zsh.
> > 
> > Here's one possible fix.
> 
> It seems inconsistent/unexpected if 'command -p cd' works, but 'command cd'
> doesn't.

However, "command cd" is not guaranteed to work in a POSIX shell,
e.g. if $PATH has changed.

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)


^ permalink raw reply	[relevance 3%]

* Re: command -p should enable builtins not in path
  2020-08-24 18:30  3%     ` Vincent Lefevre
@ 2020-08-24 19:59  0%       ` Martijn Dekker
  2020-10-02 13:17  3%         ` Vincent Lefevre
  0 siblings, 1 reply; 200+ results
From: Martijn Dekker @ 2020-08-24 19:59 UTC (permalink / raw)
  To: zsh-workers

Op 24-08-20 om 19:30 schreef Vincent Lefevre:
> However, "command cd" is not guaranteed to work in a POSIX shell,
> e.g. if $PATH has changed.

That's actually not true, 'cd' is intrinsically a built-in utility (it 
couldn't possibly function otherwise, as it must affect the current 
environment) and it is also explicitly exempt from a $PATH search by XCU 
2.9.1.4(d)[*] (not that most shells follow that absurd $PATH search rule 
for builtins anyway, but that's a different rant). Point being, POSIXly, 
'command cd' is safe.

- Martijn

[*] 
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01

-- 
||	modernish -- harness the shell
||	https://github.com/modernish/modernish
||
||	KornShell lives!
||	https://github.com/ksh93/ksh


^ permalink raw reply	[relevance 0%]

* Re: command -p should enable builtins not in path
  2020-08-24 19:59  0%       ` Martijn Dekker
@ 2020-10-02 13:17  3%         ` Vincent Lefevre
  0 siblings, 0 replies; 200+ results
From: Vincent Lefevre @ 2020-10-02 13:17 UTC (permalink / raw)
  To: zsh-workers

On 2020-08-24 20:59:52 +0100, Martijn Dekker wrote:
> Op 24-08-20 om 19:30 schreef Vincent Lefevre:
> > However, "command cd" is not guaranteed to work in a POSIX shell,
> > e.g. if $PATH has changed.
> 
> That's actually not true, 'cd' is intrinsically a built-in utility (it
> couldn't possibly function otherwise, as it must affect the current
> environment)

Well, AFAIK, some OS's provide a way for processes to affect the
environment of their parent. So an implementation could theoretically
choose to provide cd as an external utility.

> and it is also explicitly exempt from a $PATH search by XCU
> 2.9.1.4(d)[*] (not that most shells follow that absurd $PATH search
> rule for builtins anyway, but that's a different rant). Point being,
> POSIXly, 'command cd' is safe.

OK, I agree.

So, shouldn't this be changed to match the POSIX behavior for the
utility list from "Command Search and Execution"?

  https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01

I'm wondering whether any zsh user relies on an external program
for such utilities. Or, if there is any issue with such a change,
add another option, similar to POSIX_BUILTINS, but restricted to
this list.

FYI, I've just found that the "command -p" was added in Intel's scripts
because of a bug report from a user who defined a function named cd:

  https://posts663.rssing.com/chan-20519098/all_p8.html

("command" would have been sufficient as you said, but there would
be the same problem in zsh).

-- 
Vincent Lefèvre <vincent@vinc17.net> - Web: <https://www.vinc17.net/>
100% accessible validated (X)HTML - Blog: <https://www.vinc17.net/blog/>
Work: CR INRIA - computer arithmetic / AriC project (LIP, ENS-Lyon)


^ permalink raw reply	[relevance 3%]

* Re: [patch] Avoid race in zf_mkdir
  @ 2020-10-09 20:35  4%   ` Roman Perepelitsa
  2020-10-09 20:47  0%     ` Bart Schaefer
  0 siblings, 1 reply; 200+ results
From: Roman Perepelitsa @ 2020-10-09 20:35 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: zsh-workers

On Fri, Oct 9, 2020 at 10:25 PM Bart Schaefer <schaefer@brasslantern.com> wrote:
>
> Er, sorry, this doesn't actually avoid the race, it just prevents the error message from being shown by whichever shell loses the race.

I think this is the expected behavior. It's prescribed by POSIX for mkdir.

> ISometimes you might want to know that the directory was NOT created BY the current shell?

In this case you would invoke zf_mkdir without -p.

> In neither variation are we checking that the existing directory actually has the requested mode.

POSIX says this is how it should be.

Roman.

P.S.

The patch is incorrect for a different reason. If `zf_mkdir -p foo` is
racing with another process that's doing `mkdir foo && rmdir foo`, the
zf_mkdir call must never fail but with this patch it can fail.


^ permalink raw reply	[relevance 4%]

* Re: [patch] Avoid race in zf_mkdir
  2020-10-09 20:35  4%   ` Roman Perepelitsa
@ 2020-10-09 20:47  0%     ` Bart Schaefer
  2020-10-09 20:53  0%       ` Matthew Martin
  0 siblings, 1 reply; 200+ results
From: Bart Schaefer @ 2020-10-09 20:47 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: zsh-workers

[-- Attachment #1: Type: text/plain, Size: 967 bytes --]

On Fri, Oct 9, 2020 at 1:35 PM Roman Perepelitsa <
roman.perepelitsa@gmail.com> wrote:

> On Fri, Oct 9, 2020 at 10:25 PM Bart Schaefer <schaefer@brasslantern.com>
> wrote:
> >
> > Er, sorry, this doesn't actually avoid the race, it just prevents the
> error message from being shown by whichever shell loses the race.
>
> I think this is the expected behavior. It's prescribed by POSIX for mkdir.
>
[...]

>
> The patch is incorrect for a different reason. If `zf_mkdir -p foo` is
> racing with another process that's doing `mkdir foo && rmdir foo`, the
> zf_mkdir call must never fail but with this patch it can fail.
>

Hm ... in that case the code shouldn't call stat() unless the mkdir() gives
EEXIST?  And then ignore ENOENT from stat()?

What if another process is doing "touch foo && rm foo"?  How is it possible
to distinguish that from mkdir+rmdir ?

Or are we confusing the requirements for mkdir(2) from those for mkdir(1)
?  I don't have the spec handy.

[-- Attachment #2: Type: text/html, Size: 1604 bytes --]

^ permalink raw reply	[relevance 0%]

* Re: [patch] Avoid race in zf_mkdir
  2020-10-09 20:47  0%     ` Bart Schaefer
@ 2020-10-09 20:53  0%       ` Matthew Martin
    0 siblings, 1 reply; 200+ results
From: Matthew Martin @ 2020-10-09 20:53 UTC (permalink / raw)
  To: zsh-workers

On Fri, Oct 09, 2020 at 01:47:49PM -0700, Bart Schaefer wrote:
> On Fri, Oct 9, 2020 at 1:35 PM Roman Perepelitsa <
> roman.perepelitsa@gmail.com> wrote:
> 
> > On Fri, Oct 9, 2020 at 10:25 PM Bart Schaefer <schaefer@brasslantern.com>
> > wrote:
> > >
> > > Er, sorry, this doesn't actually avoid the race, it just prevents the
> > error message from being shown by whichever shell loses the race.
> >
> > I think this is the expected behavior. It's prescribed by POSIX for mkdir.
> >
> [...]
> 
> >
> > The patch is incorrect for a different reason. If `zf_mkdir -p foo` is
> > racing with another process that's doing `mkdir foo && rmdir foo`, the
> > zf_mkdir call must never fail but with this patch it can fail.
> >
> 
> Hm ... in that case the code shouldn't call stat() unless the mkdir() gives
> EEXIST?  And then ignore ENOENT from stat()?
> 
> What if another process is doing "touch foo && rm foo"?  How is it possible
> to distinguish that from mkdir+rmdir ?
> 
> Or are we confusing the requirements for mkdir(2) from those for mkdir(1)
> ?  I don't have the spec handy.

https://pubs.opengroup.org/onlinepubs/9699919799/utilities/mkdir.html

I suppose we could stat before and after if after mkdir errno = EEXIST.


^ permalink raw reply	[relevance 0%]

* Re: [patch] Avoid race in zf_mkdir
  @ 2020-10-09 21:27  0%           ` Bart Schaefer
  0 siblings, 0 replies; 200+ results
From: Bart Schaefer @ 2020-10-09 21:27 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: Zsh hackers list

[-- Attachment #1: Type: text/plain, Size: 481 bytes --]

On Fri, Oct 9, 2020 at 2:22 PM Roman Perepelitsa <
roman.perepelitsa@gmail.com> wrote:

> Perhaps something like this?
>

That would potentially cover it, except for one thing.  Back to an earlier
message:

> > In neither variation are we checking that the existing directory
actually has the requested mode.
>
> POSIX says this is how it should be.

The document linked by Matthew asserts that "mkdir -m mode" should behave
"as if" chmod() is called after creating the directory.

[-- Attachment #2: Type: text/html, Size: 897 bytes --]

^ permalink raw reply	[relevance 0%]

* Re: cannot `cd $_` to $_ containing spaces
  @ 2020-10-14  7:25  4% ` Roman Perepelitsa
  2020-10-14 20:49  0%   ` Daniel Shahaf
  0 siblings, 1 reply; 200+ results
From: Roman Perepelitsa @ 2020-10-14  7:25 UTC (permalink / raw)
  To: Soren Roth; +Cc: Zsh hackers list

On Tue, Oct 13, 2020 at 9:31 PM Soren Roth <sorenoid@gmail.com> wrote:
> I made a directory with a space in the name, and tried to cd into it using $_, and failed.

You probably have the SH_WORD_SPLIT option enabled. This option causes
field splitting to be performed on unquoted parameter expansions, like
in POSIX sh. You can turn it off by adding `unsetopt SH_WORD_SPLIT` to
~/.zshrc. Otherwise you need to quote parameter expansions to prevent
field splitting (again, like in POSIX sh).

    cd "$_"

Roman.


^ permalink raw reply	[relevance 4%]

* Re: cannot `cd $_` to $_ containing spaces
  2020-10-14  7:25  4% ` Roman Perepelitsa
@ 2020-10-14 20:49  0%   ` Daniel Shahaf
  0 siblings, 0 replies; 200+ results
From: Daniel Shahaf @ 2020-10-14 20:49 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: Soren Roth, Zsh hackers list

Roman Perepelitsa wrote on Wed, 14 Oct 2020 09:25 +0200:
> On Tue, Oct 13, 2020 at 9:31 PM Soren Roth <sorenoid@gmail.com> wrote:
> > I made a directory with a space in the name, and tried to cd into it using $_, and failed.  
> 
> You probably have the SH_WORD_SPLIT option enabled. This option causes
> field splitting to be performed on unquoted parameter expansions, like
> in POSIX sh. You can turn it off by adding `unsetopt SH_WORD_SPLIT` to
> ~/.zshrc. Otherwise you need to quote parameter expansions to prevent
> field splitting (again, like in POSIX sh).
> 
>     cd "$_"

The SH_WORD_SPLIT option defaults to being disabled, so if you have it
enabled, you might wish to check why that's the case in the first
place, rather than simply to append an «unsetopt» to your configuration.


^ permalink raw reply	[relevance 0%]

* Re: [bug] Special parameters $PATH and $path aren't kept in sync after emulating sh
  @ 2020-11-09 10:07  3% ` Peter Stephenson
  0 siblings, 0 replies; 200+ results
From: Peter Stephenson @ 2020-11-09 10:07 UTC (permalink / raw)
  To: zsh-workers

> On 09 November 2020 at 07:27 Brice Waegeneire <brice@waegenei.re> wrote:
> Yesterday I stumbled upon a bug where setting $PATH when emulating
> sh would would break the variable synchronization between the string
> ($PATH) and the array ($path):

sh mode doesn't have the special variable path; you need to rely exclusively
on PATH within sh emulation.  The basic idea of sh emulation is to track
POSIX as closely as possible to make sure all the basics work --- it's
not designed as an all-singing-all-dancing smart shell in the same
way normal zsh mode is.  In this case, that means we try to make sure variables
people might use unsuspectingly in sh don't get overloaded with special
behaviour.  (Having said that, zsh is written in such a way there are various
places where we can't make sh emulation work properly, but that's going rather
beyond your issue.)

Hooking up normal zsh behaviour and sh behaviour together can be a bit
of a nuisance exactly because, by design, they work differently.  So if
you've got a workaround, I'm afraid that may be as good as it gets.

pws


^ permalink raw reply	[relevance 3%]

* [PATCH] builtin: trivial cleanup
@ 2020-11-12 17:38 12% Felipe Contreras
  0 siblings, 0 replies; 200+ results
From: Felipe Contreras @ 2020-11-12 17:38 UTC (permalink / raw)
  To: zsh-workers; +Cc: Felipe Contreras

The flags were being set in exactly the same way in both branches of the
condition.

Signed-off-by: Felipe Contreras <felipe.contreras@gmail.com>
---
 Src/builtin.c | 17 ++++++-----------
 1 file changed, 6 insertions(+), 11 deletions(-)

diff --git a/Src/builtin.c b/Src/builtin.c
index 09eb3728c..5d780e4ca 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2231,17 +2231,12 @@ typeset_single(char *cname, char *pname, Param pm, UNUSED(int func),
 		    arrfixenv(pm->node.nam, x);
 	    }
 	}
-	if (usepm == 2)		/* do not change the PM_UNSET flag */
-	    pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~off;
-	else {
-	    /*
-	     * Keep unset if using readonly in POSIX mode.
-	     */
-	    if (!(on & PM_READONLY) || !isset(POSIXBUILTINS))
-		off |= PM_UNSET;
-	    pm->node.flags = (pm->node.flags |
-			      (on & ~PM_READONLY)) & ~off;
-	}
+	/*
+	 * Keep unset if using readonly in POSIX mode unless specified otherwise.
+	 */
+	if ((usepm != 2) && !((on & PM_READONLY) && isset(POSIXBUILTINS)))
+	    off |= PM_UNSET;
+	pm->node.flags = (pm->node.flags | (on & ~PM_READONLY)) & ~off;
 	if (on & (PM_LEFT | PM_RIGHT_B | PM_RIGHT_Z)) {
 	    if (typeset_setwidth(cname, pm, ops, on, 0))
 		return NULL;
-- 
2.29.2



^ permalink raw reply	[relevance 12%]

* [PATCHv1] [long] improvements to limit/ulimit API and doc
@ 2020-11-23 21:49  6% Stephane Chazelas
  2020-11-25  0:35  0% ` Daniel Shahaf
                   ` (2 more replies)
  0 siblings, 3 replies; 200+ results
From: Stephane Chazelas @ 2020-11-23 21:49 UTC (permalink / raw)
  To: Zsh hackers list

Hello,

I found a small issue in the "limit" builtin in that for instance
"limit msgqueue 1M" sets that limit to 1 byte instead of 1MiB
(that M suffix is silently ignored) and that "limit msgqueue 10"
was not following documentation and tried to have a go at
addressing it, but ended up finding a few more small issues,
went down the rabbit hole, also trying to address the problem
(not specific to zsh) that you never know what unit those
resource limits are meant to be expressed as.

Please see the details in commit log below.

Though I think it could be applied as is (I don't think I've
broken backward compatibility), there are a few things I'm not
completely happy or sure about so I'd appreciate some input.

The few remaining issues I've not really addressed here:

- ulimit output rounds down the values (some of them anyway) so
  we lose information. Is it worth addressing? (like with a
  "raw" option)?

- Some might disapprove the switch to kibibyte/mebibyte KiK/MiB
  (and the MB meaning 1,000,000).

- Is it worth accepting floating point values like:
  limit filesize 1.2G

- I've only tested it on Ubuntu, FreeBSD and Cygwin. I suspect
  my test cases will fail on 32bit systems. They're skipped on
  Cygwin which doesn't let you set any limit AFAICT. I don't
  have much coverage in those two tests, but with limits being
  very system specific, I'm not sure how to tackle it.

- ulimit still outputs the limits set for children processes. I
  think that's probably best. So it outputs the limits set by
  limit, ulimit or ulimit -s, even if strictly speaking, it
  doesn't give you what's returned by getrlimit() like in other
  shells (that only ever becomes visible if you use "limit"
  anyway which is not in those other shells.

- there are some corner case issues that could surprise users
  and may be worth documenting like:
  (limit filesize 1k; (print {1..2000} > file)) still creates a
  8K file because the fork for the (print...) was optimised out
  so the limits are not applied.

- I've made it so "limit -s filesize" reports the shell's own
  limit (-s is for "set" initially, but it could also be "shell"
  or "self"). But while "limit" outputs all children limits,
  "limit -s" still copies those children limits to the shell's
  and there's no way to print *all* self limits. That doesn't
  make for a very intuitive API.

BTW, POSIX is currently looking at extending the "ulimit"
builtin specification
(https://www.austingroupbugs.net/view.php?id=1418) which is what
got me looking in the first place. I think zsh is mostly
compliant with their currently proposed resolution.


From 5c1971d40b238edb1a745b7298b0169cff2b8053 Mon Sep 17 00:00:00 2001
From: Stephane Chazelas <stephane@chazelas.org>
Date: Mon, 23 Nov 2020 17:31:02 +0000
Subject: [PATCH] improve limit/ulimit API and documentation

A few issues addressed:

* msgqueue limit specifies a number of bytes but was classified as
  ZLIMTYPE_NUMBER which meant k/m suffixes were not allowed and the
  default unit for `limit` was 1 (byte) instead of the 1024 (KiB)
  specified by documentation.

  -> turns out tcsh's `limit` handled that limit (called `maxmessage`
     there) the same way and `bash`, `bosh` and `mksh`'s `ulimit` (not
     ksh93 which takes kibibytes there) also expect a number of bytes
     there.

     So, the type was changed to ZLIMTYPE_MEMORY, but the unit remains
     bytes for both `limit` and `ulimit` as a (now documented) special
     quirk for backward compatibility.

* unit suffixes, where not supported (like for ZLIMTYPE_NUMBER) are
  silently ignored.

  -> now return an error on unexpected or unrecognised suffixes.

* limit output using ambigous kB, MB units. These days, those tend to
  imply decimal scaling factors (1000B, 1000000B).

  -> changed output to use KiB, MiB, the ISO 80000 unambiguous ones
     which are now more or less mainstream.
  -> those, extended to KMGTPE..iB are also accepted on input while
     KB, MB... are interpreted as decimal. (K, M, G remain binary).
  -> documentation updated to avoid kilobyte, megabyte and use
     unambiguous kibibyte, mebibyte... instead.

-> rt_time limit is now `ulimit -R` like in bash or bosh.

* "nice" limit description ("max nice") was misleading as it's more
  linked to the *minimum* niceness the process can get.

  -> Changed to "max nice priority" matching the Linux kernel's own
     description.

* documentation for both `limit` and `ulimit` missing a few limits ->
  added

* time limits are output as h:mm:ss but that's not accepted on input.
  1:00:00 silently truncated to 1:00 (one minute instead of one hour).

  -> accept [[hh:]mm:]ss[.usec]

* limit and ulimit output truncate precision (1000B limit represented as
  0kB and 1 respectively)

  -> addressed in `limit` but not `ulimit` (where
     compliance/compatibility with ksh matters). By only using scaling
     factors when the limit can be represented in an integer number of
     them (using B, as in 1000B above when none qualify).

* some limits can't be set to arbitrary values (like that 1000 above for
  filesize) and it's not always obvious what the default unit should be.

  -> recognise the B suffix on input for "memory" type limits and
     "s"/"ms"/"ns" for time/microsecond type units so the user can input
     values unambiguously.
  -> those suffixes are now recognised by both limit and ulimit. Parsing
     code factorised into `zstrtorlimt`.

* `limit` and `unlimit` are disabled when zsh is started in csh
  emulation even though those builtins come from there.

  -> changed the features_emu in rlimits.mdd (only used there) and
    mkbltnmlst.sh to features_posix for those to only be disabled
    in sh/ksh emulation.

* `limit` changes the limits for children, `ulimit` for the shell,
  but both report limits for children, and it's not possible to
  retrieve the shell's own limits.

  -> ulimit not changed as it's probably better that way.
  -> `limit -s somelimit` changed so it reports the somelimit value
     for the shell process

-> documentation improved.
---
 Doc/Zsh/builtins.yo      | 129 +++++++++----
 Doc/Zsh/expn.yo          |  18 +-
 Doc/Zsh/mod_zpty.yo      |   4 +-
 Doc/Zsh/params.yo        |  10 +-
 NEWS                     |   7 +
 Src/Builtins/rlimits.c   | 390 +++++++++++++++++++++++++++------------
 Src/Builtins/rlimits.mdd |   8 +-
 Src/mkbltnmlst.sh        |  10 +-
 Test/B12limit.ztst       | 129 +++++++++++++
 9 files changed, 530 insertions(+), 175 deletions(-)

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index ebb29f632..f794abf1a 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -1116,15 +1116,18 @@ cindex(resource limits)
 cindex(limits, resource)
 item(tt(limit) [ tt(-hs) ] [ var(resource) [ var(limit) ] ] ...)(
 Set or display resource limits.  Unless the tt(-s) flag is given,
-the limit applies only the children of the shell.  If tt(-s) is
-given without other arguments, the resource limits of the current
-shell is set to the previously set resource limits of the children.
+the limit applies only to child processes and executed commands, not the
+current shell process itself.
 
-If var(limit) is not specified, print the current limit placed
-on var(resource), otherwise
-set the limit to the specified value.  If the tt(-h) flag
-is given, use hard limits instead of soft limits.
-If no var(resource) is given, print all limits.
+If tt(-s) is given without other arguments, the resource limits of the
+current shell is set to the previously set resource limits of the
+children.
+
+If var(limit) is not specified, print the current limit placed on
+var(resource) (of the shell with tt(-s), of children without), otherwise
+set the limit to the specified value.  If the tt(-h) flag is given, use
+hard limits instead of soft limits. If no var(resource) is given, print
+all limits.
 
 When looping over multiple resources, the shell will abort immediately if
 it detects a badly formed argument.  However, if it fails to set a limit
@@ -1143,18 +1146,23 @@ sitem(tt(datasize))(Maximum data size (including stack) for each process.)
 sitem(tt(descriptors))(Maximum value for a file descriptor.)
 sitem(tt(filesize))(Largest single file allowed.)
 sitem(tt(kqueues))(Maximum number of kqueues allocated.)
+sitem(tt(maxfilelocks))(Maximum number of file locks.)
 sitem(tt(maxproc))(Maximum number of processes.)
-sitem(tt(maxpthreads))(Maximum number of threads per process.)
+sitem(tt(maxpthreads))(Maximum number of threads per uid (NetBSD/OpenBSD) or per process.)
 sitem(tt(memorylocked))(Maximum amount of memory locked in RAM.)
 sitem(tt(memoryuse))(Maximum resident set size.)
 sitem(tt(msgqueue))(Maximum number of bytes in POSIX message queues.)
+sitem(tt(nice))(Maximum nice priority.)
 sitem(tt(posixlocks))(Maximum number of POSIX locks per user.)
 sitem(tt(pseudoterminals))(Maximum number of pseudo-terminals.)
 sitem(tt(resident))(Maximum resident set size.)
+sitem(tt(rt_priority))(Maximum realtime priority.)
+sitem(tt(rt_time))(Maximum realtime CPU time slice.)
 sitem(tt(sigpending))(Maximum number of pending signals.)
 sitem(tt(sockbufsize))(Maximum size of all socket buffers.)
 sitem(tt(stacksize))(Maximum stack size for each process.)
 sitem(tt(swapsize))(Maximum amount of swap used.)
+sitem(tt(umtxp))(Maximum number of umtx shared locks.)
 sitem(tt(vmemorysize))(Maximum amount of virtual memory.)
 endsitem()
 
@@ -1169,18 +1177,46 @@ the limit anyway, and will report an error if this fails.  As the shell
 does not store such resources internally, an attempt to set the limit will
 fail unless the tt(-s) option is present.
 
-var(limit) is a number, with an optional scaling factor, as follows:
+If var(limit) is a decimal integer number without suffix, then for
+historical reason and compatibility with csh where that command comes from,
+the unit will depend on the type of limit: microsecond for tt(rt_time),
+second for all other time limits, kibibytes (1024 bytes) for all limits that
+express a number of bytes (except tt(msgqueue)).
+
+Instead, to avoid confusion, a suffix (case insensitive) may be appended to
+the (integer only) decimal number to specify the unit:
 
 startsitem()
-sitem(var(n)tt(h))(hours)
-sitem(var(n)tt(k))(kilobytes (default))
-sitem(var(n)tt(m))(megabytes or minutes)
-sitem(var(n)tt(g))(gigabytes)
-sitem([var(mm)tt(:)]var(ss))(minutes and seconds)
+sitem(time limits)(
+startsitem()
+sitem(tt(h))(hours)
+sitem(tt(m))(minutes)
+sitem(tt(s))(seconds)
+sitem(tt(ms))(milliseconds)
+sitem(tt(us))(microseconds)
+sitem(tt([[var(h):]var(m):]var(s)[.var(usec)]))(all combined)
+endsitem()
+
+For instance a 2 millisecond limit of tt(rt_time), can be expressed as
+tt(2ms), tt(2000us), tt(0.002), tt(0:0:0.002) or tt(2000) without unit.)
+sitem(memory limits)(
+startsitem()
+sitem(tt(k, kib))(kibibytes (1024 bytes))
+sitem(tt(m, mib))(mibibytes)
+sitem(...)(and so on with tt(g), tt(t), tt(p), tt(e) for gibibytes,
+tebibytes, pebibytes and exbibytes respectively)
+sitem(tt(kb))(kilobytes (1,000 bytes))
+sitem(tt(mb))(megabytes (1,000,000 bytes))
+sitem(...)(and so on with tt(g), tt(t), tt(p), tt(e) for gigabytes,
+terabytes, petabytes and exabytes respectively.)
+sitem(tt(b))(em(byte) to set the limit with arbitrary precisions)
+endsitem())
+sitem(other limits)(other limits are assumed to be numerical and ony
+a decimal integer number is accepted.)
 endsitem()
 
 The tt(limit) command is not made available by default when the
-shell starts in a mode emulating another shell.  It can be made available
+shell starts in sh or ksh emulation mode.  It can be made available
 with the command `tt(zmodload -F zsh/rlimits b:limit)'.
 )
 findex(local)
@@ -2250,8 +2286,8 @@ together with the tt(-H) flag set both hard and soft limits.
 If no options are used, the file size limit (tt(-f)) is assumed.
 
 If var(limit) is omitted the current value of the specified resources are
-printed.  When more than one resource value is printed, the limit name and
-unit is printed before each value.
+printed (rounded down to the corresponding unit).  When more than one resource
+value is printed, the limit name and unit is printed before each value.
 
 When looping over multiple resources, the shell will abort immediately if
 it detects a badly formed argument.  However, if it fails to set a limit
@@ -2260,30 +2296,45 @@ for some other reason it will continue trying to set the remaining limits.
 Not all the following resources are supported on all systems.  Running
 tt(ulimit -a) will show which are supported.
 
+Whilst tt(limit) is the BSD/csh-style command, tt(ulimit) is the SysV/Korn
+shell equivalent (added later to zsh for compatibility with ksh). While
+tt(limit) in zsh sets the limits for child processes by default, tt(ulimit)
+sets them for both the shell and child processes, and reports the ones
+set for child processes.
+
+The same suffixes are recognised as for tt(limit), but bear in mind that
+default units when no suffix is specified varry between the two.
+
+Below, the corresponding tt(limit) keyword for each tt(ulimit) option is
+shown in parenthesis for reference:
+
 startsitem()
 sitem(tt(-a))(Lists all of the current resource limits.)
-sitem(tt(-b))(Socket buffer size in bytes LPAR()N.B. not kilobytes+RPAR())
-sitem(tt(-c))(512-byte blocks on the size of core dumps.)
-sitem(tt(-d))(Kilobytes on the size of the data segment.)
-sitem(tt(-f))(512-byte blocks on the size of files written.)
-sitem(tt(-i))(The number of pending signals.)
-sitem(tt(-k))(The number of kqueues allocated.)
-sitem(tt(-l))(Kilobytes on the size of locked-in memory.)
-sitem(tt(-m))(Kilobytes on the size of physical memory.)
-sitem(tt(-n))(open file descriptors.)
-sitem(tt(-p))(The number of pseudo-terminals.)
-sitem(tt(-q))(Bytes in POSIX message queues.)
+sitem(tt(-b))(Socket buffer size in bytes LPAR()N.B. not kibibytes+RPAR() (tt(sockbufsize)).)
+sitem(tt(-c))(512-byte blocks on the size of core dumps (tt(coredumpsize)).)
+sitem(tt(-d))(Kibibytes on the size of the data segment (tt(datasize)).)
+sitem(tt(-e))(nice priority (tt(nice)).)
+sitem(tt(-f))(512-byte blocks on the size of files written (tt(filesize)).)
+sitem(tt(-i))(The number of pending signals (tt(sigpending)).)
+sitem(tt(-k))(The number of kqueues allocated (tt(kqueues)).)
+sitem(tt(-l))(Kibibytes on the size of locked-in memory (tt(memorylocked)).)
+sitem(tt(-m))(Kibibytes on the size of physical memory (tt(resident)).)
+sitem(tt(-n))(open file descriptors (tt(descriptors)).)
+sitem(tt(-o))(umtx shared locks (tt(umtxp)).)
+sitem(tt(-p))(The number of pseudo-terminals (tt(pseudoterminals)).)
+sitem(tt(-q))(Bytes in POSIX message queues (tt(msgqueue)).)
 sitem(tt(-r))(Maximum real time priority.  On some systems where this
 is not available, such as NetBSD, this has the same effect as tt(-T)
-for compatibility with tt(sh).)
-sitem(tt(-s))(Kilobytes on the size of the stack.)
-sitem(tt(-T))(The number of simultaneous threads available to the user.)
-sitem(tt(-t))(CPU seconds to be used.)
-sitem(tt(-u))(The number of processes available to the user.)
-sitem(tt(-v))(Kilobytes on the size of virtual memory.  On some systems this
-refers to the limit called `address space'.)
-sitem(tt(-w))(Kilobytes on the size of swapped out memory.)
-sitem(tt(-x))(The number of locks on files.)
+for compatibility with tt(sh) (tt(rt_priority) / tt(maxpthreads)).)
+sitem(tt(-R))(realtime CPU time slice (tt(rt_time)).)
+sitem(tt(-s))(Kibibytes on the size of the stack (tt(stacksize)).)
+sitem(tt(-T))(The number of simultaneous threads available to the user (tt(maxpthreads)).)
+sitem(tt(-t))(CPU seconds to be used (tt(cputime)).)
+sitem(tt(-u))(The number of processes available to the user (tt(maxproc)).)
+sitem(tt(-v))(Kibibytes on the size of virtual memory.  On some systems this
+refers to the limit called em(address space) (tt(vmemorysize) / tt(addressspace)).)
+sitem(tt(-w))(Kibibytes on the size of swapped out memory (tt(swapsize)).)
+sitem(tt(-x))(The number of locks on files (tt(maxfilelocks)).)
 endsitem()
 
 A resource may also be specified by integer in the form `tt(-N)
@@ -2343,7 +2394,7 @@ The resources of the shell process are only changed if the tt(-s)
 flag is given.
 
 The tt(unlimit) command is not made available by default when the
-shell starts in a mode emulating another shell.  It can be made available
+shell starts in sh or ksh emulation mode.  It can be made available
 with the command `tt(zmodload -F zsh/rlimits b:unlimit)'.
 )
 findex(unset)
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index b3396721f..588426af0 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -2837,15 +2837,15 @@ exactly var(n) bytes in length.
 
 If this flag is directly followed by a em(size specifier) `tt(k)' (`tt(K)'),
 `tt(m)' (`tt(M)'), or `tt(p)' (`tt(P)') (e.g. `tt(Lk-50)') the check is
-performed with kilobytes, megabytes, or blocks (of 512 bytes) instead.
-(On some systems additional specifiers are available for gigabytes,
-`tt(g)' or `tt(G)', and terabytes, `tt(t)' or `tt(T)'.) If a size specifier
-is used a file is regarded as "exactly" the size if the file size rounded up
-to the next unit is equal to the test size.  Hence `tt(*LPAR()Lm1+RPAR())'
-matches files from 1 byte up to 1 Megabyte inclusive.  Note also that
-the set of files "less than" the test size only includes files that would
-not match the equality test; hence `tt(*LPAR()Lm-1+RPAR())' only matches
-files of zero size.
+performed with kibibytes, mebibytes, or blocks (of 512 bytes; catch: not
+em(pebibytes)) instead. (On some systems additional specifiers are available
+for gibibytes, `tt(g)' or `tt(G)', and tebibytes, `tt(t)' or `tt(T)'.) If a
+size specifier is used, a file is regarded as "exactly" the size if the file
+size rounded up to the next unit is equal to the test size.  Hence
+`tt(*LPAR()Lm1+RPAR())' matches files from 1 byte up to 1 Mebibyte
+inclusive.  Note also that the set of files "less than" the test size only
+includes files that would not match the equality test; hence
+`tt(*LPAR()Lm-1+RPAR())' only matches files of zero size.
 )
 item(tt(^))(
 negates all qualifiers following it
diff --git a/Doc/Zsh/mod_zpty.yo b/Doc/Zsh/mod_zpty.yo
index 3ca031c01..ba59e1783 100644
--- a/Doc/Zsh/mod_zpty.yo
+++ b/Doc/Zsh/mod_zpty.yo
@@ -66,8 +66,8 @@ read matches the var(pattern), even in the non-blocking case.  The return
 status is zero if the string read matches the pattern, or if the command
 has exited but at least one character could still be read.  If the option
 tt(-m) is present, the return status is zero only if the pattern matches.
-As of this writing, a maximum of one megabyte of output can be consumed
-this way; if a full megabyte is read without matching the pattern, the
+As of this writing, a maximum of one mebibyte of output can be consumed
+this way; if a full mebibyte is read without matching the pattern, the
 return status is non-zero.
 
 In all cases, the return status is non-zero if nothing could be read, and
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 36c1ae4c2..6c305da34 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -1473,7 +1473,7 @@ is specified with no command.  Defaults to tt(more).
 vindex(REPORTMEMORY)
 item(tt(REPORTMEMORY))(
 If nonnegative, commands whose maximum resident set size (roughly
-speaking, main memory usage) in kilobytes is greater than this
+speaking, main memory usage) in kibibytes is greater than this
 value have timing statistics reported.  The format used to output
 statistics is the value of the tt(TIMEFMT) parameter, which is the same
 as for the tt(REPORTTIME) variable and the tt(time) builtin; note that
@@ -1603,12 +1603,12 @@ sitem(tt(%E))(Elapsed time in seconds.)
 sitem(tt(%P))(The CPU percentage, computed as
 100*(tt(%U)PLUS()tt(%S))/tt(%E).)
 sitem(tt(%W))(Number of times the process was swapped.)
-sitem(tt(%X))(The average amount in (shared) text space used in kilobytes.)
+sitem(tt(%X))(The average amount in (shared) text space used in kibibytes.)
 sitem(tt(%D))(The average amount in (unshared) data/stack space used in
-kilobytes.)
-sitem(tt(%K))(The total space used (tt(%X)PLUS()tt(%D)) in kilobytes.)
+kibibytes.)
+sitem(tt(%K))(The total space used (tt(%X)PLUS()tt(%D)) in kibibytes.)
 sitem(tt(%M))(The  maximum memory the process had in use at any time in
-kilobytes.)
+kibibytes.)
 sitem(tt(%F))(The number of major page faults (page needed to be brought
 from disk).)
 sitem(tt(%R))(The number of minor page faults.)
diff --git a/NEWS b/NEWS
index a8e7df80e..f5534eba0 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,13 @@ The zsh/system module's `zsystem flock` command learnt an -i option to
 set the wait interval used with -t. Additionally, -t now supports
 fractional seconds.
 
+The `limit` builtin accepts more units for resource limits, now shared
+with the `ulimit` builtin allowing to specify arbitrary limit values.
+`limit -s somelimit` now reports the shell process' own value of the limit.
+The `limit` and `unlimit` builtins are now available again in csh emulation.
+More generally, the resouce limit handling interfaces and documentation have
+been improved.
+
 Changes from 5.7.1-test-3 to 5.8
 --------------------------------
 
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 5f9c84b0f..1b2474583 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -55,7 +55,8 @@ typedef struct resinfo_T {
  * 1. Add zsh_LIMIT_PRESENT(RLIMIT_XXX) in configure.ac.
  * 2. Add an entry for RLIMIT_XXX to known_resources[].
  *    Make sure the option letter (resinto_T.opt) is unique.
- * 3. Build zsh and run the test B12rlimit.ztst.
+ * 3. Add entry in documentation for the limit and ulimit builtins
+ * 4. Build zsh and run the test B12rlimit.ztst.
  */
 static const resinfo_T known_resources[] = {
     {RLIMIT_CPU, "cputime", ZLIMTYPE_TIME, 1,
@@ -109,12 +110,23 @@ static const resinfo_T known_resources[] = {
 		'i', "pending signals"},
 # endif
 # ifdef HAVE_RLIMIT_MSGQUEUE
-    {RLIMIT_MSGQUEUE, "msgqueue", ZLIMTYPE_NUMBER, 1,
+    {RLIMIT_MSGQUEUE, "msgqueue", ZLIMTYPE_MEMORY, 1,
 		'q', "bytes in POSIX msg queues"},
 # endif
 # ifdef HAVE_RLIMIT_NICE
+    /*
+     * Not max niceness. On Linux ranges [1..40] for that RLIM translates
+     * to [-19..20] meaning that for instance setting it to 20 means
+     * processes can lower their niceness as far down as -1 and with the
+     * default of 0, unprivileged processes can't lower their niceness.
+     *
+     * Increasing niceness is always possible.
+     *
+     * "max nice priority" is the wording used in /proc/self/limits on
+     * Linux.
+     */
     {RLIMIT_NICE, "nice", ZLIMTYPE_NUMBER, 1,
-		'e', "max nice"},
+		'e', "max nice priority"},
 # endif
 # ifdef HAVE_RLIMIT_RTPRIO
     {RLIMIT_RTPRIO, "rt_priority", ZLIMTYPE_NUMBER, 1,
@@ -122,7 +134,7 @@ static const resinfo_T known_resources[] = {
 # endif
 # ifdef HAVE_RLIMIT_RTTIME
     {RLIMIT_RTTIME, "rt_time", ZLIMTYPE_MICROSECONDS, 1,
-		'N', "rt cpu time (microseconds)"},
+		'R', "rt cpu time slice (microseconds)"},
 # endif
     /* BSD */
 # ifdef HAVE_RLIMIT_SBSIZE
@@ -192,9 +204,9 @@ set_resinfo(void)
 	if (!resinfo[i]) {
 	    /* unknown resource */
 	    resinfo_T *info = (resinfo_T *)zshcalloc(sizeof(resinfo_T));
-	    char *buf = (char *)zalloc(12);
+	    char *buf = (char *)zalloc(8 /* UNKNOWN- */ + 3 /* digits */ + 1 /* '\0' */);
 	    snprintf(buf, 12, "UNKNOWN-%d", i);
-	    info->res = - 1;	/* negative value indicates "unknown" */
+	    info->res = -1;	/* negative value indicates "unknown" */
 	    info->name = buf;
 	    info->type = ZLIMTYPE_UNKNOWN;
 	    info->unit = 1;
@@ -255,38 +267,204 @@ printrlim(rlim_t val, const char *unit)
 # endif /* RLIM_T_IS_QUAD_T */
 }
 
+/*
+ * Parse a string into the corresponding value based on the limit type
+ *
+ *   ZLIMTYPE_UNKNOWN / ZLIMTYPE_NUMBER:
+ *     decimal integer without sign only: raw value
+ *
+ *   ZLIMTYPE_TIME / ZLIMTYPE_MICROSECONDS:
+ *     <decimal> only gives seconds or microseconds depending on type
+ *     or:
+ *     [[hour:]min:]sec[.usec]
+ *     or:
+ *     <decimal> with h (hour), m (min), s (sec), ms, us suffix.
+ *
+ *   ZLIMTYPE_MEMORY:
+ *     <decimal> without suffix interpreted as KiB by "limit" (except for
+ *     RLIMIT_MSGQUEUE, see below) and based on resinfo.unit by "ulimit".
+ *
+ *     K/M/G/T/P/E suffix and same with iB suffix use 1024 factor
+ *     KB/MB/GB... use 1000 factor.
+ *
+ *     B for bytes (avoids that mess about default units).
+ *
+ * All suffixes are case insensitive.
+ *
+ */
+
 /**/
 static rlim_t
-zstrtorlimt(const char *s, char **t, int base)
+zstrtorlimt(const char *s, int lim, int ulimit, char **err)
 {
     rlim_t ret = 0;
+    const char *orig = s;
+    enum zlimtype type = resinfo[lim]->type;
+    *err = NULL;
 
-    if (strcmp(s, "unlimited") == 0) {
-	if (t)
-	    *t = (char *) s + 9;
+    if (strcmp(s, "unlimited") == 0)
 	return RLIM_INFINITY;
+
+    for (; *s >= '0' && *s <= '9'; s++)
+	ret = ret * 10 + *s - '0';
+
+    if (s == orig) {
+	*err = "decimal integer expected";
+	return 0;
+    }
+
+    if (lim >= RLIM_NLIMITS ||
+	type == ZLIMTYPE_NUMBER ||
+	type == ZLIMTYPE_UNKNOWN) {
+	/*
+	 * pure numeric resource -- only a straight decimal number is
+	 * permitted.
+	 */
+	if (*s) {
+	    *err = "limit must be a decimal integer";
+	    return 0;
+	}
+    }
+    else if (type == ZLIMTYPE_TIME ||
+	     type == ZLIMTYPE_MICROSECONDS) {
+	if (*s) {
+	    int divisor = 1;
+	    int factor = 1;
+	    switch (*s++) {
+
+	    case 'm': /* m for minute or ms for milisecond */
+	    case 'M':
+		if (*s == 's' || *s == 'S') {
+		    s++;
+		    divisor = 1000;
+		}
+		else {
+		    factor = 60;
+		}
+		break;
+
+	    case 'h':
+	    case 'H':
+		factor = 3600;
+		break;
+
+	    case 's':
+	    case 'S':
+		break;
+
+	    case 'u':
+	    case 'U':
+		divisor = 1000000;
+		if (*s == 's' || *s == 'S')
+		    s++;
+		break;
+
+	    case ':':
+		do {
+		    int more = 0;
+		    while (*s >= '0' && *s <= '9') {
+			more = more * 10 + (*s - '0');
+			s++;
+		    }
+		    ret = ret * 60 + more;
+		} while (*s == ':' && ++s);
+		if (*s == '.')
+		    s++;
+		    /* fallthrough */
+		else
+		    break;
+	    case '.':
+		if (type == ZLIMTYPE_MICROSECONDS) {
+		    int frac;
+		    for (frac = 0; *s >= '0' && *s <= '9'; s++) {
+			if (divisor < 1000000) {
+			    /* ignore digits past the 6th */
+			    divisor *= 10;
+			    frac = frac * 10 + (*s - '0');
+			}
+		    }
+		    ret = ret * divisor + frac;
+		}
+		else {
+		    /* fractional part ignored */
+		    while (*s >= '0' && *s <= '9')
+			s++;
+		}
+		break;
+	    default:
+		*err = "invalid time specification";
+		return 0;
+	    }
+
+	    if (*s) {
+		*err = "invalid time specification";
+		return 0;
+	    }
+
+	    ret *= factor;
+	    if (type == ZLIMTYPE_MICROSECONDS)
+		ret *= 1000000 / divisor;
+	    else
+		ret /= divisor;
+	}
+    }
+    else {
+	/*
+	 * memory-type resource
+	 */
+	if (*s) {
+	    if (*s == 'b' || *s == 'B')
+		s++;
+	    else {
+		const char *suffix = "kKmMgGtTpPeE";
+		char *offset;
+
+		if ((offset = strchr(suffix, *s))) {
+		    s++;
+		    if (*s == 'b' || *s == 'B') {
+			/* KB == 1000 */
+			const char *p;
+			for (p = suffix; p <= offset; p += 2)
+			    ret *= 1000;
+			s++;
+		    }
+		    else {
+			/* K/KiB == 1024 */
+			if ((s[0] == 'i' || s[0] == 'I') &&
+			    (s[1] == 'b' || s[1] == 'B'))
+			    s += 2;
+			ret <<= ((offset - suffix) / 2 + 1) * 10;
+		    }
+		}
+	    }
+	    if (*s) {
+		*err = "invalid unit";
+		return 0;
+	    }
+	}
+	else {
+	    if (ulimit)
+		ret *= resinfo[lim]->unit;
+	    else
+#ifdef HAVE_RLIMIT_MSGQUEUE
+		if (lim != RLIMIT_MSGQUEUE)
+		    /*
+		     * Historical quirk. In tcsh's limit (and bash's and mksh's
+		     * ulimit, but not ksh93), that limit expects bytes instead
+		     * of kibibytes and earlier versions of zsh were treating
+		     * it as a ZLIMTYPE_NUMBER.
+		     *
+		     * We still want to treat it as ZLIMTYPE_MEMORY and accept
+		     * KMG... suffixes as it is a number of bytes.
+		     *
+		     * But we carry on taking the value as a number of *bytes*
+		     * in the "limit" builtin for backward compatibility and
+		     * compatibility with tcsh.
+		     */
+#endif
+		    ret *= 1024;
+	}
     }
-# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_LONG_LONG) || defined(RLIM_T_IS_UNSIGNED)
-    if (!base) {
-	if (*s != '0')
-	    base = 10;
-	else if (*++s == 'x' || *s == 'X')
-	    base = 16, s++;
-	else
-	    base = 8;
-    } 
-    if (base <= 10)
-	for (; *s >= '0' && *s < ('0' + base); s++)
-	    ret = ret * base + *s - '0';
-    else
-	for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
-	     || (*s >= 'A' && *s < ('A' + base - 10)); s++)
-	    ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
-    if (t)
-	*t = (char *)s;
-# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
-    ret = zstrtol(s, t, base);
-# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
     return ret;
 }
 
@@ -317,25 +495,42 @@ showlimitvalue(int lim, rlim_t val)
 	       resinfo[lim]->type == ZLIMTYPE_UNKNOWN)
 	printrlim(val, "\n");	/* pure numeric resource */
     else {
-	/* memory resource -- display with `k' or `M' modifier */
-	if (val >= 1024L * 1024L)
-	    printrlim(val/(1024L * 1024L), "MB\n");
+	/*
+	 * memory resource -- display with KiB/MiB... for exact
+	 * multiples of those units
+	 */
+	const char *units = "KMGTPE";
+	rlim_t v = val;
+	int n = 0;
+	while (units[n] && (v & 1023) == 0 && v >> 10) {
+	    n++;
+	    v >>= 10;
+	}
+	if (n) {
+	    char suffix[] = "XiB\n";
+	    *suffix = units[n-1];
+	    printrlim(v, suffix);
+	}
 	else
-	    printrlim(val/1024L, "kB\n");
+	    printrlim(val, "B\n");
     }
 }
 
-/* Display resource limits.  hard indicates whether `hard' or `soft'  *
- * limits should be displayed.  lim specifies the limit, or may be -1 *
- * to show all.                                                       */
+/*
+ * Display resource limits.  hard indicates whether `hard' or `soft'
+ * limits should be displayed.  lim specifies the limit, or may be -1
+ * to show all.  If `shell' is non-zero, the limits in place for the
+ * shell process retrieved with getrlimits() are shown.
+ */
 
 /**/
 static int
-showlimits(char *nam, int hard, int lim)
+showlimits(char *nam, int hard, int lim, int shell)
 {
     int rt;
+    int ret = 0;
 
-    if (lim >= RLIM_NLIMITS)
+    if (shell || lim >= RLIM_NLIMITS)
     {
 	/*
 	 * Not configured into the shell.  Ask the OS
@@ -357,17 +552,33 @@ showlimits(char *nam, int hard, int lim)
     else
     {
 	/* main loop over resource types */
-	for (rt = 0; rt != RLIM_NLIMITS; rt++)
-	    showlimitvalue(rt, (hard) ? limits[rt].rlim_max :
-			   limits[rt].rlim_cur);
+	for (rt = 0; rt != RLIM_NLIMITS; rt++) {
+	    struct rlimit vals, *plim;
+	    if (shell) {
+		plim = &vals;
+		if (getrlimit(rt, plim) < 0)
+		{
+		    zwarnnam(nam, "can't read \"%s\" limit: %e", resinfo[rt]->name, errno);
+		    ret = 1;
+		    continue;
+		} 
+	    }
+	    else
+		plim = &(limits[rt]);
+
+	    showlimitvalue(rt, (hard) ? plim->rlim_max :
+			   plim->rlim_cur);
+	}
     }
 
-    return 0;
+    return ret;
 }
 
-/* Display a resource limit, in ulimit style.  lim specifies which   *
- * limit should be displayed, and hard indicates whether the hard or *
- * soft limit should be displayed.                                   */
+/*
+ * Display a resource limit, in ulimit style.  lim specifies which
+ * limit should be displayed, and hard indicates whether the hard or
+ * soft limit should be displayed.
+ */
 
 /**/
 static int
@@ -394,12 +605,12 @@ printulimit(char *nam, int lim, int hard, int head)
 	if (lim < RLIM_NLIMITS) {
 	    const resinfo_T *info = resinfo[lim];
 	    if (info->opt == 'N')
-		printf("-N %2d: %-29s", lim, info->descr);
+		printf("-N %2d: %-32s ", lim, info->descr);
 	    else
-		printf("-%c: %-32s", info->opt, info->descr);
+		printf("-%c: %-35s ", info->opt, info->descr);
 	}
 	else
-	    printf("-N %2d: %-29s", lim, "");
+	    printf("-N %2d: %-32s ", lim, "");
     }
     /* display the limit */
     if (limit == RLIM_INFINITY)
@@ -511,16 +722,18 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
     rlim_t val;
     int ret = 0;
 
-    hard = OPT_ISSET(ops,'h');
-    if (OPT_ISSET(ops,'s') && !*argv)
+    hard = OPT_ISSET(ops, 'h');
+    if (OPT_ISSET(ops, 's') && !*argv)
 	return setlimits(NULL);
     /* without arguments, display limits */
     if (!*argv)
-	return showlimits(nam, hard, -1);
+	return showlimits(nam, hard, -1, OPT_ISSET(ops, 's'));
     while ((s = *argv++)) {
 	/* Search for the appropriate resource name.  When a name matches (i.e. *
 	 * starts with) the argument, the lim variable changes from -1 to the   *
 	 * number of the resource.  If another match is found, lim goes to -2.  */
+	char *err;
+
 	if (idigit(*s))
 	{
 	    lim = (int)zstrtol(s, NULL, 10);
@@ -543,61 +756,12 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
 	}
 	/* without value for limit, display the current limit */
 	if (!(s = *argv++))
-	    return showlimits(nam, hard, lim);
-	if (lim >= RLIM_NLIMITS)
-	{
-	    val = zstrtorlimt(s, &s, 10);
-	    if (*s)
-	    {
-		/* unknown limit, no idea how to scale */
-		zwarnnam(nam, "unknown scaling factor: %s", s);
-		return 1;
-	    }
-	}
-	else if (resinfo[lim]->type == ZLIMTYPE_TIME) {
-	    /* time-type resource -- may be specified as seconds, or minutes or *
-	     * hours with the `m' and `h' modifiers, and `:' may be used to add *
-	     * together more than one of these.  It's easier to understand from *
-	     * the code:                                                        */
-	    val = zstrtorlimt(s, &s, 10);
-	    if (*s) {
-		if ((*s == 'h' || *s == 'H') && !s[1])
-		    val *= 3600L;
-		else if ((*s == 'm' || *s == 'M') && !s[1])
-		    val *= 60L;
-		else if (*s == ':')
-		    val = val * 60 + zstrtorlimt(s + 1, &s, 10);
-		else {
-		    zwarnnam(nam, "unknown scaling factor: %s", s);
-		    return 1;
-		}
-	    }
-	} else if (resinfo[lim]->type == ZLIMTYPE_NUMBER ||
-		   resinfo[lim]->type == ZLIMTYPE_UNKNOWN ||
-		   resinfo[lim]->type == ZLIMTYPE_MICROSECONDS) {
-	    /* pure numeric resource -- only a straight decimal number is
-	    permitted. */
-	    char *t = s;
-	    val = zstrtorlimt(t, &s, 10);
-	    if (s == t) {
-		zwarnnam(nam, "limit must be a number");
-		return 1;
-	    }
-	} else {
-	    /* memory-type resource -- `k', `M' and `G' modifiers are *
-	     * permitted, meaning (respectively) 2^10, 2^20 and 2^30. */
-	    val = zstrtorlimt(s, &s, 10);
-	    if (!*s || ((*s == 'k' || *s == 'K') && !s[1])) {
-		if (val != RLIM_INFINITY)
-		    val *= 1024L;
-	    } else if ((*s == 'M' || *s == 'm') && !s[1])
-		val *= 1024L * 1024;
-	    else if ((*s == 'G' || *s == 'g') && !s[1])
-		val *= 1024L * 1024 * 1024;
-	    else {
-		zwarnnam(nam, "unknown scaling factor: %s", s);
-		return 1;
-	    }
+	    return showlimits(nam, hard, lim, OPT_ISSET(ops, 's'));
+
+	val = zstrtorlimt(s, lim, 0, &err);
+	if (err) {
+	    zwarnnam(nam, err);
+	    return 1;
 	}
 	if (do_limit(nam, lim, val, hard, !hard, OPT_ISSET(ops, 's')))
 	    ret++;
@@ -821,14 +985,12 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 		    limit = vals.rlim_max;
 		}
 	    } else {
-		limit = zstrtorlimt(*argv, &eptr, 10);
-		if (*eptr) {
-		    zwarnnam(name, "invalid number: %s", *argv);
+		char *err;
+		limit = zstrtorlimt(*argv, res, 1, &err);
+		if (err) {
+		    zwarnnam(name, "%s: %s", *argv, err);
 		    return 1;
 		}
-		/* scale appropriately */
-		if (res < RLIM_NLIMITS)
-		    limit *= resinfo[res]->unit;
 	    }
 	    if (do_limit(name, res, limit, hard, soft, 1))
 		ret++;
diff --git a/Src/Builtins/rlimits.mdd b/Src/Builtins/rlimits.mdd
index 06c9e9c7f..248a03e61 100644
--- a/Src/Builtins/rlimits.mdd
+++ b/Src/Builtins/rlimits.mdd
@@ -3,6 +3,12 @@ link=either
 load=yes
 
 autofeatures="b:limit b:ulimit b:unlimit"
-autofeatures_emu="b:ulimit"
+
+# limit is the csh builtin, while ulimit is the ksh/posix one.
+# Autoloading ulimit in csh emulation should be relatively
+# harmless as "ulimit" contrary to "limit" is not otherwise
+# a common English word. So we're only accomodating sh/ksh
+# emulations.
+autofeatures_posix="b:ulimit"
 
 objects="rlimits.o"
diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh
index c4611d8b3..7ebc2a751 100644
--- a/Src/mkbltnmlst.sh
+++ b/Src/mkbltnmlst.sh
@@ -37,10 +37,10 @@ for x_mod in $x_mods; do
         echo "/* non-linked-in known module \`$x_mod' */"
 	linked=no
     esac
-    unset moddeps autofeatures autofeatures_emu
+    unset moddeps autofeatures autofeatures_posix
     . $srcdir/../$modfile
     if test "x$autofeatures" != x; then
-        if test "x$autofeatures_emu" != x; then
+        if test "x$autofeatures_posix" != x; then
             echo "  {"
 	    echo "    char *zsh_features[] = { "
 	    for feature in $autofeatures; do
@@ -48,14 +48,14 @@ for x_mod in $x_mods; do
 	    done
 	    echo "      NULL"
 	    echo "    }; "
-	    echo "    char *emu_features[] = { "
-	    for feature in $autofeatures_emu; do
+	    echo "    char *posix_features[] = { "
+	    for feature in $autofeatures_posix; do
 		echo "      \"$feature\","
 	    done
 	    echo "      NULL"
 	    echo "    }; "
 	    echo "    autofeatures(\"zsh\", \"$x_mod\","
-	    echo "       EMULATION(EMULATE_ZSH) ? zsh_features : emu_features,"
+	    echo "       EMULATION(EMULATE_KSH|EMULATE_SH) ? posix_features : zsh_features,"
 	    echo "       0, 1);"
 	    echo "  }"
         else
diff --git a/Test/B12limit.ztst b/Test/B12limit.ztst
index 48d33e6e3..3665279fa 100644
--- a/Test/B12limit.ztst
+++ b/Test/B12limit.ztst
@@ -26,3 +26,132 @@ F:report this to zsh-workers mailing list.
   }
 0:check if limit option letters are unique
 
+  if sh -c 'ulimit 2048' > /dev/null 2>&1; then
+    (
+      set -o braceccl -o pipefail
+      list=(1b 1{kmgtpe}{,b,ib})
+      for cmd in "limit filesize" ulimit; do
+	for l in $list $list:u; do
+	  $=cmd $l &&
+	    limit filesize &&
+	    ulimit || exit
+	done
+      done | sed 'N;s/\n/ /;s/  */ /g'
+    )
+  else
+    ZTST_skip='Cannot set the filesize limit on this system'
+  fi
+0:filesize suffixes with limit and ulimit
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+
+  if sh -c 'ulimit -t 3600' > /dev/null 2>&1; then
+  (
+    set -o pipefail
+    list=(1h 30m 20s 30 1:23:45.123456 2:23 56.4)
+    for cmd in "limit cputime" "ulimit -t"; do
+      for l in $list ${(MU)list:#*[a-z]*}; do
+        $=cmd $l &&
+	  limit cputime &&
+	  ulimit -t || exit
+      done
+    done | sed 'N;s/\n/ /;s/  */ /g'
+  )
+  else
+    ZTST_skip='Cannot set the cputime limit on this system'
+  fi
+0:time limit formats
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
+>cputime 0:00:30 30
+>cputime 1:23:45 5025
+>cputime 0:02:23 143
+>cputime 0:00:56 56
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
+>cputime 0:00:30 30
+>cputime 1:23:45 5025
+>cputime 0:02:23 143
+>cputime 0:00:56 56
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
-- 
2.25.1



^ permalink raw reply	[relevance 6%]

* Re: evallineno not loading
  @ 2020-11-24 12:32  3%   ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2020-11-24 12:32 UTC (permalink / raw)
  To: Roman Perepelitsa; +Cc: Alexander Remus, Zsh hackers list

[...]
> evallineno is set by default, and `setopt` by default only shows the
> difference between the current options and the default. You can use
> this command to list all options:
> 
>   zmodload zsh/parameter && printf '%-20s %s\n' ${(@kv)options}
[...]

Or "set -o" or "set +o" like in any POSIX shell (except bash
which has two separate sets of options).

Note that zsh/parameter these days should be loaded
automatically when you access $options.

-- 
Stephane


^ permalink raw reply	[relevance 3%]

* Re: [PATCHv1] [long] improvements to limit/ulimit API and doc
  2020-11-23 21:49  6% [PATCHv1] [long] improvements to limit/ulimit API and doc Stephane Chazelas
@ 2020-11-25  0:35  0% ` Daniel Shahaf
  2020-11-26  6:57  6%   ` [PATCHv2 1/2] [long] improvements to limit/ulimit API and doc ((un)limit in csh emulation) Stephane Chazelas
    2020-11-26 11:19  4% ` [PATCHv1] [long] improvements to limit/ulimit API and doc Jun T
  2 siblings, 1 reply; 200+ results
From: Daniel Shahaf @ 2020-11-25  0:35 UTC (permalink / raw)
  To: Zsh hackers list

Good morning Stephane,

Stephane Chazelas wrote on Mon, Nov 23, 2020 at 21:49:42 +0000:
> BTW, POSIX is currently looking at extending the "ulimit"
> builtin specification
> (https://www.austingroupbugs.net/view.php?id=1418) which is what
> got me looking in the first place. I think zsh is mostly
> compliant with their currently proposed resolution.

Thanks for the heads-up.

> I found a small issue in the "limit" builtin in that for instance
> "limit msgqueue 1M" sets that limit to 1 byte instead of 1MiB
> (that M suffix is silently ignored) and that "limit msgqueue 10"
> was not following documentation and tried to have a go at
> addressing it, but ended up finding a few more small issues,
> went down the rabbit hole, also trying to address the problem
> (not specific to zsh) that you never know what unit those
> resource limits are meant to be expressed as.

> From 5c1971d40b238edb1a745b7298b0169cff2b8053 Mon Sep 17 00:00:00 2001
> From: Stephane Chazelas <stephane@chazelas.org>
> Date: Mon, 23 Nov 2020 17:31:02 +0000
> Subject: [PATCH] improve limit/ulimit API and documentation
> 
> A few issues addressed:
> 

This patch is over 1k lines long and makes multiple independent changes.

Could you please split this into one patch per logical change?  That
would make it easier to review, both now, and in the future should
a regression be bisected to it.

Thanks,

Daniel

> * msgqueue limit specifies a number of bytes but was classified as
>   ZLIMTYPE_NUMBER which meant k/m suffixes were not allowed and the
>   default unit for `limit` was 1 (byte) instead of the 1024 (KiB)
>   specified by documentation.
> 
>   -> turns out tcsh's `limit` handled that limit (called `maxmessage`
>      there) the same way and `bash`, `bosh` and `mksh`'s `ulimit` (not
>      ksh93 which takes kibibytes there) also expect a number of bytes
>      there.
> 
>      So, the type was changed to ZLIMTYPE_MEMORY, but the unit remains
>      bytes for both `limit` and `ulimit` as a (now documented) special
>      quirk for backward compatibility.
> 
> * unit suffixes, where not supported (like for ZLIMTYPE_NUMBER) are
>   silently ignored.
> 
>   -> now return an error on unexpected or unrecognised suffixes.
> 
> * limit output using ambigous kB, MB units. These days, those tend to
>   imply decimal scaling factors (1000B, 1000000B).
> 
>   -> changed output to use KiB, MiB, the ISO 80000 unambiguous ones
>      which are now more or less mainstream.
>   -> those, extended to KMGTPE..iB are also accepted on input while
>      KB, MB... are interpreted as decimal. (K, M, G remain binary).
>   -> documentation updated to avoid kilobyte, megabyte and use
>      unambiguous kibibyte, mebibyte... instead.
> 
> -> rt_time limit is now `ulimit -R` like in bash or bosh.
> 
> * "nice" limit description ("max nice") was misleading as it's more
>   linked to the *minimum* niceness the process can get.
> 
>   -> Changed to "max nice priority" matching the Linux kernel's own
>      description.
> 
> * documentation for both `limit` and `ulimit` missing a few limits ->
>   added
> 
> * time limits are output as h:mm:ss but that's not accepted on input.
>   1:00:00 silently truncated to 1:00 (one minute instead of one hour).
> 
>   -> accept [[hh:]mm:]ss[.usec]
> 
> * limit and ulimit output truncate precision (1000B limit represented as
>   0kB and 1 respectively)
> 
>   -> addressed in `limit` but not `ulimit` (where
>      compliance/compatibility with ksh matters). By only using scaling
>      factors when the limit can be represented in an integer number of
>      them (using B, as in 1000B above when none qualify).
> 
> * some limits can't be set to arbitrary values (like that 1000 above for
>   filesize) and it's not always obvious what the default unit should be.
> 
>   -> recognise the B suffix on input for "memory" type limits and
>      "s"/"ms"/"ns" for time/microsecond type units so the user can input
>      values unambiguously.
>   -> those suffixes are now recognised by both limit and ulimit. Parsing
>      code factorised into `zstrtorlimt`.
> 
> * `limit` and `unlimit` are disabled when zsh is started in csh
>   emulation even though those builtins come from there.
> 
>   -> changed the features_emu in rlimits.mdd (only used there) and
>     mkbltnmlst.sh to features_posix for those to only be disabled
>     in sh/ksh emulation.
> 
> * `limit` changes the limits for children, `ulimit` for the shell,
>   but both report limits for children, and it's not possible to
>   retrieve the shell's own limits.
> 
>   -> ulimit not changed as it's probably better that way.
>   -> `limit -s somelimit` changed so it reports the somelimit value
>      for the shell process
> 
> -> documentation improved.
> ---
>  Doc/Zsh/builtins.yo      | 129 +++++++++----
>  Doc/Zsh/expn.yo          |  18 +-
>  Doc/Zsh/mod_zpty.yo      |   4 +-
>  Doc/Zsh/params.yo        |  10 +-
>  NEWS                     |   7 +
>  Src/Builtins/rlimits.c   | 390 +++++++++++++++++++++++++++------------
>  Src/Builtins/rlimits.mdd |   8 +-
>  Src/mkbltnmlst.sh        |  10 +-
>  Test/B12limit.ztst       | 129 +++++++++++++
>  9 files changed, 530 insertions(+), 175 deletions(-)
> 
> Though I think it could be applied as is (I don't think I've
> broken backward compatibility), there are a few things I'm not
> completely happy or sure about so I'd appreciate some input.
> 
> The few remaining issues I've not really addressed here:
> 
> - ulimit output rounds down the values (some of them anyway) so
>   we lose information. Is it worth addressing? (like with a
>   "raw" option)?
> 
> - Some might disapprove the switch to kibibyte/mebibyte KiK/MiB
>   (and the MB meaning 1,000,000).
> 
> - Is it worth accepting floating point values like:
>   limit filesize 1.2G
> 
> - I've only tested it on Ubuntu, FreeBSD and Cygwin. I suspect
>   my test cases will fail on 32bit systems. They're skipped on
>   Cygwin which doesn't let you set any limit AFAICT. I don't
>   have much coverage in those two tests, but with limits being
>   very system specific, I'm not sure how to tackle it.
> 
> - ulimit still outputs the limits set for children processes. I
>   think that's probably best. So it outputs the limits set by
>   limit, ulimit or ulimit -s, even if strictly speaking, it
>   doesn't give you what's returned by getrlimit() like in other
>   shells (that only ever becomes visible if you use "limit"
>   anyway which is not in those other shells.
> 
> - there are some corner case issues that could surprise users
>   and may be worth documenting like:
>   (limit filesize 1k; (print {1..2000} > file)) still creates a
>   8K file because the fork for the (print...) was optimised out
>   so the limits are not applied.
> 
> - I've made it so "limit -s filesize" reports the shell's own
>   limit (-s is for "set" initially, but it could also be "shell"
>   or "self"). But while "limit" outputs all children limits,
>   "limit -s" still copies those children limits to the shell's
>   and there's no way to print *all* self limits. That doesn't
>   make for a very intuitive API.


^ permalink raw reply	[relevance 0%]

* Re: More rabbit-holes with unset variables
  @ 2020-11-25 13:19  4% ` Stephane Chazelas
    0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2020-11-25 13:19 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

2020-11-24 23:02:05 -0800, Bart Schaefer:
> I was experimenting with ksh to look deeper into adding behavior to
> ksh_typeset in zsh, and discovered that this example:
[...]

Sorry I didn't read the full discussion as I just subscribed to
the list, but I did have a look at the differences between local
scope implementations at some point and there are many many
variations between shells.

I did find zsh was far from being the worst one there.

They have been discussed at length on the austin group (POSIX)
mailing list as they've been trying on and off to specify a
local scope for sh variables.

I did write a quick summary of some of those findings at:
https://unix.stackexchange.com/questions/493729/list-of-shells-that-support-local-keyword-for-defining-local-variables/493743#493743
which is very relevant to this discussion.

See https://www.austingroupbugs.net/bug_view_page.php?bug_id=767
for the POSIX attempt. You'll find a number of lengthy related
discussions on their mailing archive.

It's after one of those discussions that bash added the
localvar_inherit and localvar_unset options, and NetBSD sh added
-I and -N options to "local" I beleive (the mantainers of bash,
NetBSD sh, bosh are regulars on the austin group mailing list,
FreeBSD sh maintainer is seen occasionally).

-- 
Stephane


^ permalink raw reply	[relevance 4%]

* Re: More rabbit-holes with unset variables
  @ 2020-11-26  6:10  2%     ` Stephane Chazelas
  2020-11-26 20:41  3%     ` Bart Schaefer
  1 sibling, 0 replies; 200+ results
From: Stephane Chazelas @ 2020-11-26  6:10 UTC (permalink / raw)
  To: Felipe Contreras; +Cc: Bart Schaefer, Zsh hackers list

2020-11-25 16:17:46 -0600, Felipe Contreras:
[...]
>   The variable whose name is specified shall be created as a local
> variable with name "name". It
>   shall inherit its initial value, as well as the exported and
> readonly flags, from the variable
>   with the same name in the surrounding dynamic scope if there is one;
> otherwise, the variable is
>   initially unset. Then, if "=word" is provided, the value of that
> local variable shall then be set
>   to word.
[...]

You'd describing the ash/bosh behaviour. Most other shells
behave differently. As mentioned at that SE Q&A or in that POSIX
issue, there is a strong case for that variable to be a brand
new variable with an initial unset or empty/0/empty-list value
depending on the type.

In reality, depending on the shell you get:

- variable left as is from the parent scope (inherit, value,
  type, attributes), but restored upon return of function.
  Useful in things like "local PATH" where you want to keep the
  current value of PATH, be able to do modifications and restore
  the original value upon return of the function.

  That approach is not acceptable for a shell with other types
  of variables (array, hash, compound) and with variable
  attributes (integer, float, padding, uppercase...). Because
  that would invalidate all the uses like:

  f() { local i; for i do ...; done; }

  Which would be broken if i remained a hash/integer/padded...

  In ash, that function doesn't work in contexts where i has
  been made read-only in a parent scope, and leaks the value of
  i to executed commands if it was exported. NetBSD sh added
  "local -N i" for that. Though you could also do
  "local var; unset var" (not portable as it doesn't work in
  mksh/yash)

- variable created unset but inheriting some of the attributes
  (bash) that's the worst of both world as you don't know what
  you're going to get. (localvar_unset doesn't address that).

- variable created anew initially unset

- variable created anew with initial value (zsh).

As to unset vs initial value, both have merits. While I'd tend
to prefer "initially unset", it makes sense that "local -i i;
echo "$i"" outputs an integer. Or that "local -Z2 v; echo $#v"
outputs 2.

In any case, I don't think zsh can change its default behaviour
as it would break backward compatibility.

It could add -N/-I à la NetBSD sh if people found it was useful
enough.

It could try and emulate ksh in ksh emulation, but which ksh?
ksh88, ksh93 and mksh behave radically differently in that
regard. Also few people use ksh these days, so I'm not sure it's
worth the effort. While the ksh emulation mode can help with
bash compatibility, bash's behaviour in this instent is also
very different.

-- 
Stephane



^ permalink raw reply	[relevance 2%]

* [PATCHv2 1/2] [long] improvements to limit/ulimit API and doc ((un)limit in csh emulation)
  2020-11-25  0:35  0% ` Daniel Shahaf
@ 2020-11-26  6:57  6%   ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2020-11-26  6:57 UTC (permalink / raw)
  To: zsh-workers

2020-11-25 00:35:12 +0000, Daniel Shahaf:
[...]
> Could you please split this into one patch per logical change?  That
> would make it easier to review, both now, and in the future should
> a regression be bisected to it.
[...]

Here is for the csh builtins, I'll send a second one for the
rest addressing Oliver's comments (doc, testcase, _ulimit)
hopefully tonight. I'll add a test case to verify we get errors
upon unexpected suffixes as well.

From: Stephane Chazelas <stephane@chazelas.org>
Date: Thu, 26 Nov 2020 06:34:44 +0000
Subject: [PATCH] Re-enable `limit` and `unlimit` in csh emulation.

Those two builtins do come from csh (4BSD, 1980 along with resource
limits) in the first place, they were disabled when emulating *other*
shells, that should only have been for POSIX/ksh which have ulimit and
not limit.

  -> changed the features_emu in rlimits.mdd (only used there) and
    mkbltnmlst.sh to features_posix for those to only be disabled
    in sh/ksh emulation.
---
 Doc/Zsh/builtins.yo      |  4 ++--
 NEWS                     |  2 ++
 Src/Builtins/rlimits.mdd |  8 +++++++-
 Src/mkbltnmlst.sh        | 10 +++++-----
 4 files changed, 16 insertions(+), 8 deletions(-)

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index ebb29f632..2f1ccd8a5 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -1180,7 +1180,7 @@ sitem([var(mm)tt(:)]var(ss))(minutes and seconds)
 endsitem()
 
 The tt(limit) command is not made available by default when the
-shell starts in a mode emulating another shell.  It can be made available
+shell starts in sh or ksh emulation mode.  It can be made available
 with the command `tt(zmodload -F zsh/rlimits b:limit)'.
 )
 findex(local)
@@ -2343,7 +2343,7 @@ The resources of the shell process are only changed if the tt(-s)
 flag is given.
 
 The tt(unlimit) command is not made available by default when the
-shell starts in a mode emulating another shell.  It can be made available
+shell starts in sh or ksh emulation mode.  It can be made available
 with the command `tt(zmodload -F zsh/rlimits b:unlimit)'.
 )
 findex(unset)
diff --git a/NEWS b/NEWS
index a8e7df80e..d05e8b64f 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,8 @@ The zsh/system module's `zsystem flock` command learnt an -i option to
 set the wait interval used with -t. Additionally, -t now supports
 fractional seconds.
 
+The `limit` and `unlimit` builtins are now available again in csh emulation.
+
 Changes from 5.7.1-test-3 to 5.8
 --------------------------------
 
diff --git a/Src/Builtins/rlimits.mdd b/Src/Builtins/rlimits.mdd
index 06c9e9c7f..248a03e61 100644
--- a/Src/Builtins/rlimits.mdd
+++ b/Src/Builtins/rlimits.mdd
@@ -3,6 +3,12 @@ link=either
 load=yes
 
 autofeatures="b:limit b:ulimit b:unlimit"
-autofeatures_emu="b:ulimit"
+
+# limit is the csh builtin, while ulimit is the ksh/posix one.
+# Autoloading ulimit in csh emulation should be relatively
+# harmless as "ulimit" contrary to "limit" is not otherwise
+# a common English word. So we're only accomodating sh/ksh
+# emulations.
+autofeatures_posix="b:ulimit"
 
 objects="rlimits.o"
diff --git a/Src/mkbltnmlst.sh b/Src/mkbltnmlst.sh
index c4611d8b3..7ebc2a751 100644
--- a/Src/mkbltnmlst.sh
+++ b/Src/mkbltnmlst.sh
@@ -37,10 +37,10 @@ for x_mod in $x_mods; do
         echo "/* non-linked-in known module \`$x_mod' */"
 	linked=no
     esac
-    unset moddeps autofeatures autofeatures_emu
+    unset moddeps autofeatures autofeatures_posix
     . $srcdir/../$modfile
     if test "x$autofeatures" != x; then
-        if test "x$autofeatures_emu" != x; then
+        if test "x$autofeatures_posix" != x; then
             echo "  {"
 	    echo "    char *zsh_features[] = { "
 	    for feature in $autofeatures; do
@@ -48,14 +48,14 @@ for x_mod in $x_mods; do
 	    done
 	    echo "      NULL"
 	    echo "    }; "
-	    echo "    char *emu_features[] = { "
-	    for feature in $autofeatures_emu; do
+	    echo "    char *posix_features[] = { "
+	    for feature in $autofeatures_posix; do
 		echo "      \"$feature\","
 	    done
 	    echo "      NULL"
 	    echo "    }; "
 	    echo "    autofeatures(\"zsh\", \"$x_mod\","
-	    echo "       EMULATION(EMULATE_ZSH) ? zsh_features : emu_features,"
+	    echo "       EMULATION(EMULATE_KSH|EMULATE_SH) ? posix_features : zsh_features,"
 	    echo "       0, 1);"
 	    echo "  }"
         else
-- 
2.25.1




^ permalink raw reply	[relevance 6%]

* Re: [PATCHv1] [long] improvements to limit/ulimit API and doc
  2020-11-23 21:49  6% [PATCHv1] [long] improvements to limit/ulimit API and doc Stephane Chazelas
  2020-11-25  0:35  0% ` Daniel Shahaf
  @ 2020-11-26 11:19  4% ` Jun T
  2020-11-26 13:55  4%   ` Stephane Chazelas
  2 siblings, 1 reply; 200+ results
From: Jun T @ 2020-11-26 11:19 UTC (permalink / raw)
  To: zsh-workers

This is not the problem of the patch, but I noticed limit/ulimit of zsh
has a small problem due to the wrap-around of unsigned integers.
For example, on Linux, either with or without Stephane's patch:

zsh% ulimit 36028797018963967 && ulimit
36028797018963967                          # = 2**55 - 1
zsh% ulimit 36028797018963968 && ulimit
0

This is because 36028797018963968 blocks = 2**64, which wrap around to zero.
With the patch we can more easily see:

zsh% limit filesize 8EiB && limit filesize
filesize        8EiB
zsh% limit filesize 16EiB && limit fillesize
filesize        0B           # 16EiB = 2**64 = 0

It seems bash is doing some check for the wrap around:

zsh% bash --posix            # block=512B in POSIX mode
bash$ ulimit 36028797018963967 && ulimit
36028797018963967
bash$ ulimit 36028797018963968
bash: ulimit: 36028797018963968: limit out of range



In B12limit.ztst

> +  if sh -c 'ulimit 2048' > /dev/null 2>&1; then

Can we assume that there is no hard limit set by the System?
Later in the test we will test up to 1EiB, so maybe it would be safer
to use 'ulimit -f unlimited'?
(All the systems I know do not set any hard limit on filesize
so maybe we need not bother with this).



^ permalink raw reply	[relevance 4%]

* Re: [PATCHv1] [long] improvements to limit/ulimit API and doc
  2020-11-26 11:19  4% ` [PATCHv1] [long] improvements to limit/ulimit API and doc Jun T
@ 2020-11-26 13:55  4%   ` Stephane Chazelas
  2020-11-26 15:22  0%     ` Jun. T
  0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2020-11-26 13:55 UTC (permalink / raw)
  To: Jun T; +Cc: zsh-workers

2020-11-26 20:19:37 +0900, Jun T:
> This is not the problem of the patch, but I noticed limit/ulimit of zsh
> has a small problem due to the wrap-around of unsigned integers.
> For example, on Linux, either with or without Stephane's patch:
> 
> zsh% ulimit 36028797018963967 && ulimit
> 36028797018963967                          # = 2**55 - 1
> zsh% ulimit 36028797018963968 && ulimit
> 0
> 
> This is because 36028797018963968 blocks = 2**64, which wrap around to zero.
> With the patch we can more easily see:
> 
> zsh% limit filesize 8EiB && limit filesize
> filesize        8EiB
> zsh% limit filesize 16EiB && limit fillesize
> filesize        0B           # 16EiB = 2**64 = 0
> 
> It seems bash is doing some check for the wrap around:
> 
> zsh% bash --posix            # block=512B in POSIX mode
> bash$ ulimit 36028797018963967 && ulimit
> 36028797018963967
> bash$ ulimit 36028797018963968
> bash: ulimit: 36028797018963968: limit out of range

But:

$ bash --posix -c 'ulimit 18446744073709551617;  ulimit'
1

There's also the question of the actual value of RLIM_INFINITY,
RLIM_SAVED_MAX, RLIM_SAVED_CUR and whether we can assume
RLIM_INFINITY is the maximum possible value.

POSIX doesn't seem to give us much guarantee there
(https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/basedefs/sys_resource.h.html)


> In B12limit.ztst
> 
> > +  if sh -c 'ulimit 2048' > /dev/null 2>&1; then
> 
> Can we assume that there is no hard limit set by the System?
> Later in the test we will test up to 1EiB, so maybe it would be safer
> to use 'ulimit -f unlimited'?
> (All the systems I know do not set any hard limit on filesize
> so maybe we need not bother with this).

Yes, "make test" is meant for packagers. I'd say it's up to them
to update their environment so the tests can run.

Now, I'd expect those 1EiB tests would fail on some system
whether or not "ulimit -f unlimited" works or not. I haven't
found any yet. But if there are any, it's probably better we be
told about it (and a failing test is a good way to achieve
that).

-- 
Stephane



^ permalink raw reply	[relevance 4%]

* Re: [PATCHv1] [long] improvements to limit/ulimit API and doc
  2020-11-26 13:55  4%   ` Stephane Chazelas
@ 2020-11-26 15:22  0%     ` Jun. T
  2020-11-26 17:23  3%       ` Stephane Chazelas
  0 siblings, 1 reply; 200+ results
From: Jun. T @ 2020-11-26 15:22 UTC (permalink / raw)
  To: zsh-workers


> 2020/11/26 22:55, Stephane Chazelas <stephane@chazelas.org> wrote:
> 
> But:
> 
> $ bash --posix -c 'ulimit 18446744073709551617;  ulimit'
> 1

I think this is a bug of bash.
It seems bash checks the wrap around only for the calculation of
bytes = 512*blocks, and does not check if the number of blocks
is already wrapped around.

> There's also the question of the actual value of RLIM_INFINITY,
> RLIM_SAVED_MAX, RLIM_SAVED_CUR and whether we can assume
> RLIM_INFINITY is the maximum possible value.

I guess zsh need not care about these things.

# I must say I don't know what it means if RLIM_SAVED_XXX is not
# equal to RLIMI_INFINITY.

Zsh only need to check whether the new limit user wants to set is
within the range of rlim_t.

If it is not, then zsh should warn about it instead of passing the
wrap-around value to setrlimit(2).

If it is, then just pass it to setrlimit(2) (need not check whether
it is smaller than RLIM_INFINITY or not).
If setrlimit() returns error, then report it to the user.

Jun




^ permalink raw reply	[relevance 0%]

* Re: [PATCHv1] [long] improvements to limit/ulimit API and doc
  2020-11-26 15:22  0%     ` Jun. T
@ 2020-11-26 17:23  3%       ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2020-11-26 17:23 UTC (permalink / raw)
  To: Jun. T; +Cc: zsh-workers

2020-11-27 00:22:05 +0900, Jun. T:
> 
> > 2020/11/26 22:55, Stephane Chazelas <stephane@chazelas.org> wrote:
> > 
> > But:
> > 
> > $ bash --posix -c 'ulimit 18446744073709551617;  ulimit'
> > 1
> 
> I think this is a bug of bash.
> It seems bash checks the wrap around only for the calculation of
> bytes = 512*blocks, and does not check if the number of blocks
> is already wrapped around.

Yes, looks like it does:

if (limit * factor / factor != limit) error(out of range)

(where limit is converted from string without overflow check).

> 
> > There's also the question of the actual value of RLIM_INFINITY,
> > RLIM_SAVED_MAX, RLIM_SAVED_CUR and whether we can assume
> > RLIM_INFINITY is the maximum possible value.
> 
> I guess zsh need not care about these things.
> 
> # I must say I don't know what it means if RLIM_SAVED_XXX is not
> # equal to RLIM_INFINITY.

I can't say I know anything about those RLIM_SAVED_XXX. I
came across them for the first time today.

> Zsh only need to check whether the new limit user wants to set is
> within the range of rlim_t.

Yes, but how do you determine that range? Should we not also
reject 18446744073709551615 as out-of-range on systems where
it's RLIM_INFINITY since it's not preventing file sizes to get
past 18446744073709551615 for instance.

$ bash --posix -c 'ulimit -t 18446744073709551615 && ulimit -t'
unlimited

yash rejects it:

$ yash -c 'ulimit -t 18446744073709551614 && ulimit -t'
18446744073709551614
$ yash -c 'ulimit -t 18446744073709551615 && ulimit -t'
ulimit: Numerical result out of range
$ yash -c 'ulimit -t 18446744073709551616 && ulimit -t'
ulimit: `18446744073709551616' is not a valid integer

In any case, while I agree they are valid (though minor)
concerns, I won't address them in this round.  I can add a
"BUGS" entry though.

-- 
Stephane



^ permalink raw reply	[relevance 3%]

* Re: More rabbit-holes with unset variables
    2020-11-26  6:10  2%     ` Stephane Chazelas
@ 2020-11-26 20:41  3%     ` Bart Schaefer
    1 sibling, 1 reply; 200+ results
From: Bart Schaefer @ 2020-11-26 20:41 UTC (permalink / raw)
  To: Zsh hackers list

On Wed, Nov 25, 2020 at 2:17 PM Felipe Contreras
<felipe.contreras@gmail.com> wrote:
>
> [quoting an article from the posix tracker]
>   The variable whose name is specified shall be created as a local
> variable with name "name". It
>   shall inherit its initial value, as well as the exported and
> readonly flags, from the variable
>   with the same name in the surrounding dynamic scope if there is one;
> otherwise, the variable is
>   initially unset. Then, if "=word" is provided, the value of that
> local variable shall then be set
>   to word.
>
> So, the variable is initially unset, *unless* "=word" is provided.
>
> Do you think anyone objected to that behavior?

Is there a newer version of the standard than V4 from 2018, which is
the latest I can find online?

"Local variables within a function were considered and included in
another early proposal (controlled by the special built-in local), but
were removed because they do not fit the simple model developed for
functions and because there was some opposition to adding yet another
new special built-in that was not part of historical practice.
Implementations should reserve the identifier local (as well as
typeset, as used in the KornShell) in case this local variable
mechanism is adopted in a future version of this standard."

So the language you're quoting was rejected, though not for that
specific reason.

In fact, unless the notion of typed variables (integer, array, etc.)
is excluded, that language is ambiguous, because (as I've mentioned
elsewhere) the behavior of an "unset" variable in other contexts (such
as assignment) is that it takes on the attributes from the assignment;
so a the only useful "declared but not set" variable is a simple
scalar.

I'll note that posix defines null as "" (double-quoted empty string)
although it requires a careful reading to find that.



^ permalink raw reply	[relevance 3%]

* [PATCHv2 2/2] [long] improvements to limit/ulimit API and doc (the rest)
  @ 2020-11-26 20:58  4%   ` Stephane Chazelas
    0 siblings, 1 reply; 200+ results
From: Stephane Chazelas @ 2020-11-26 20:58 UTC (permalink / raw)
  To: Oliver Kiddle; +Cc: Zsh hackers list

2020-11-26 00:43:33 +0100, Oliver Kiddle:
[...]
> I've got a few comments, mostly on the documentation.
[...]

Thanks for all the corrections. As you might have guessed,
English is not my first language, but a few of them I should
have been able to avoid, sorry about that.

Below is the second iteration, integrating Oliver's comments.
That's the second part, exluding the csh mode change (1/2) which
was separated out (still needs to applied before this one).

I've also added a test case to check that invalid input is
correctly rejected.

The change for _ulimit was addressed in a separate patch sent
earlier.

(there are still a few outstanding questions in my original
message that we may want to discuss/address).


From: Stephane Chazelas <stephane@chazelas.org>
Date: Thu, 26 Nov 2020 20:38:57 +0000
Subject: [PATCH] improve limit/ulimit API and documentation

A few issues addressed:

* msgqueue limit specifies a number of bytes but was classified as
  ZLIMTYPE_NUMBER which meant k/m suffixes were not allowed and the
  default unit for `limit` was 1 (byte) instead of the 1024 (KiB)
  specified by documentation.

  -> turns out tcsh's `limit` handled that limit (called `maxmessage`
     there) the same way and `bash`, `bosh` and `mksh`'s `ulimit` (not
     ksh93 which takes kibibytes there) also expect a number of bytes
     there.

     So, the type was changed to ZLIMTYPE_MEMORY, but the unit remains
     bytes for both `limit` and `ulimit` as a (now documented) special
     quirk for backward compatibility.

* unit suffixes, where not supported (like for ZLIMTYPE_NUMBER) are
  silently ignored.

  -> now return an error on unexpected or unrecognised suffixes.

* limit output using ambigous kB, MB units. These days, those tend to
  imply decimal scaling factors (1000B, 1000000B).

  -> changed output to use KiB, MiB, the ISO 80000 unambiguous ones
     which are now more or less mainstream.
  -> those, extended to KMGTPE..iB are also accepted on input while
     KB, MB... are interpreted as decimal. (K, M, G remain binary).
  -> documentation updated to avoid kilobyte, megabyte and use
     unambiguous kibibyte, mebibyte... instead.

-> rt_time limit is now `ulimit -R` like in bash or bosh.

* "nice" limit description ("max nice") was misleading as it's more
  linked to the *minimum* niceness the process can get.

  -> Changed to "max nice priority" matching the Linux kernel's own
     description.

* documentation for both `limit` and `ulimit` missing a few limits ->
  added

* time limits are output as h:mm:ss but that's not accepted on input.
  1:00:00 silently truncated to 1:00 (one minute instead of one hour).

  -> accept [[hh:]mm:]ss[.usec]

* limit and ulimit output truncate precision (1000B limit represented as
  0kB and 1 respectively)

  -> addressed in `limit` but not `ulimit` (where
     compliance/compatibility with ksh matters). By only using scaling
     factors when the limit can be represented in an integer number of
     them (using B, as in 1000B above when none qualify).

* some limits can't be set to arbitrary values (like that 1000 above for
  filesize) and it's not always obvious what the default unit should be.

  -> recognise the B suffix on input for "memory" type limits and
     "s"/"ms"/"ns" for time/microsecond type units so the user can input
     values unambiguously.
  -> those suffixes are now recognised by both limit and ulimit. Parsing
     code factorised into `zstrtorlimt`.

* `limit` changes the limits for children, `ulimit` for the shell,
  but both report limits for children, and it's not possible to
  retrieve the shell's own limits.

  -> ulimit not changed as it's probably better that way.
  -> `limit -s somelimit` changed so it reports the somelimit value
     for the shell process

-> documentation improved.
---
 Doc/Zsh/builtins.yo    | 125 +++++++++----
 Doc/Zsh/expn.yo        |  18 +-
 Doc/Zsh/mod_zpty.yo    |   4 +-
 Doc/Zsh/params.yo      |  10 +-
 Etc/BUGS               |   4 +
 NEWS                   |   5 +
 Src/Builtins/rlimits.c | 397 +++++++++++++++++++++++++++++------------
 Test/B12limit.ztst     | 157 ++++++++++++++++
 8 files changed, 553 insertions(+), 167 deletions(-)

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 2f1ccd8a5..5a2c51b0b 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -1116,15 +1116,18 @@ cindex(resource limits)
 cindex(limits, resource)
 item(tt(limit) [ tt(-hs) ] [ var(resource) [ var(limit) ] ] ...)(
 Set or display resource limits.  Unless the tt(-s) flag is given,
-the limit applies only the children of the shell.  If tt(-s) is
-given without other arguments, the resource limits of the current
-shell is set to the previously set resource limits of the children.
+the limit applies only to child processes and executed commands, not the
+current shell process itself.
 
-If var(limit) is not specified, print the current limit placed
-on var(resource), otherwise
-set the limit to the specified value.  If the tt(-h) flag
-is given, use hard limits instead of soft limits.
-If no var(resource) is given, print all limits.
+If tt(-s) is given without other arguments, the resource limits of the
+current shell are set to the previously set resource limits of the
+children.
+
+If var(limit) is not specified, print the current limit placed on
+var(resource) (of the shell with tt(-s), of children without), otherwise
+set the limit to the specified value.  If the tt(-h) flag is given, use
+hard limits instead of soft limits. If no var(resource) is given, print
+all limits.
 
 When looping over multiple resources, the shell will abort immediately if
 it detects a badly formed argument.  However, if it fails to set a limit
@@ -1143,18 +1146,23 @@ sitem(tt(datasize))(Maximum data size (including stack) for each process.)
 sitem(tt(descriptors))(Maximum value for a file descriptor.)
 sitem(tt(filesize))(Largest single file allowed.)
 sitem(tt(kqueues))(Maximum number of kqueues allocated.)
+sitem(tt(maxfilelocks))(Maximum number of file locks.)
 sitem(tt(maxproc))(Maximum number of processes.)
-sitem(tt(maxpthreads))(Maximum number of threads per process.)
+sitem(tt(maxpthreads))(Maximum number of threads per uid (NetBSD/OpenBSD) or per process.)
 sitem(tt(memorylocked))(Maximum amount of memory locked in RAM.)
 sitem(tt(memoryuse))(Maximum resident set size.)
 sitem(tt(msgqueue))(Maximum number of bytes in POSIX message queues.)
+sitem(tt(nice))(Maximum nice priority.)
 sitem(tt(posixlocks))(Maximum number of POSIX locks per user.)
 sitem(tt(pseudoterminals))(Maximum number of pseudo-terminals.)
 sitem(tt(resident))(Maximum resident set size.)
+sitem(tt(rt_priority))(Maximum realtime priority.)
+sitem(tt(rt_time))(Maximum realtime CPU time slice.)
 sitem(tt(sigpending))(Maximum number of pending signals.)
 sitem(tt(sockbufsize))(Maximum size of all socket buffers.)
 sitem(tt(stacksize))(Maximum stack size for each process.)
 sitem(tt(swapsize))(Maximum amount of swap used.)
+sitem(tt(umtxp))(Maximum number of umtx shared locks.)
 sitem(tt(vmemorysize))(Maximum amount of virtual memory.)
 endsitem()
 
@@ -1169,14 +1177,42 @@ the limit anyway, and will report an error if this fails.  As the shell
 does not store such resources internally, an attempt to set the limit will
 fail unless the tt(-s) option is present.
 
-var(limit) is a number, with an optional scaling factor, as follows:
+If var(limit) is a decimal integer number without suffix, then for
+historical reasons and compatibility with csh where that command comes from,
+the unit will depend on the type of limit: microseconds for tt(rt_time),
+seconds for all other time limits, kibibytes (1024 bytes) for all limits that
+express a number of bytes (except tt(msgqueue)).
+
+Instead, to avoid confusion, a suffix (case insensitive) may be appended to
+the (integer only) decimal number to specify the unit:
 
 startsitem()
-sitem(var(n)tt(h))(hours)
-sitem(var(n)tt(k))(kilobytes (default))
-sitem(var(n)tt(m))(megabytes or minutes)
-sitem(var(n)tt(g))(gigabytes)
-sitem([var(mm)tt(:)]var(ss))(minutes and seconds)
+sitem(time limits)(
+startsitem()
+sitem(tt(h))(hours)
+sitem(tt(m))(minutes)
+sitem(tt(s))(seconds)
+sitem(tt(ms))(milliseconds)
+sitem(tt(us))(microseconds)
+sitem(tt([[var(h):]var(m):]var(s)[.var(usec)]))(all combined)
+endsitem()
+
+For instance a 2 millisecond limit of tt(rt_time), can be expressed as
+tt(2ms), tt(2000us), tt(0.002), tt(0:0:0.002) or tt(2000) without unit.)
+sitem(memory limits)(
+startsitem()
+sitem(tt(k, kib))(kibibytes (1024 bytes))
+sitem(tt(m, mib))(mibibytes)
+sitem(...)(and so on with tt(g), tt(t), tt(p), tt(e) for gibibytes,
+tebibytes, pebibytes and exbibytes respectively)
+sitem(tt(kb))(kilobytes (1,000 bytes))
+sitem(tt(mb))(megabytes (1,000,000 bytes))
+sitem(...)(and so on with tt(g), tt(t), tt(p), tt(e) for gigabytes,
+terabytes, petabytes and exabytes respectively.)
+sitem(tt(b))(em(byte) to set the limit with arbitrary precisions)
+endsitem())
+sitem(other limits)(other limits are assumed to be numerical and ony
+a decimal integer number is accepted.)
 endsitem()
 
 The tt(limit) command is not made available by default when the
@@ -2250,8 +2286,8 @@ together with the tt(-H) flag set both hard and soft limits.
 If no options are used, the file size limit (tt(-f)) is assumed.
 
 If var(limit) is omitted the current value of the specified resources are
-printed.  When more than one resource value is printed, the limit name and
-unit is printed before each value.
+printed (rounded down to the corresponding unit).  When more than one resource
+value is printed, the limit name and unit is printed before each value.
 
 When looping over multiple resources, the shell will abort immediately if
 it detects a badly formed argument.  However, if it fails to set a limit
@@ -2260,30 +2296,45 @@ for some other reason it will continue trying to set the remaining limits.
 Not all the following resources are supported on all systems.  Running
 tt(ulimit -a) will show which are supported.
 
+Whilst tt(limit) is the BSD/csh-style command, tt(ulimit) is the SysV/Korn
+shell equivalent (added later to zsh for compatibility with ksh). While
+tt(limit) in zsh sets the limits for child processes by default, tt(ulimit)
+sets them for both the shell and child processes, and reports the ones
+set for child processes.
+
+The same suffixes are recognised as for tt(limit), but bear in mind that
+default units when no suffix is specified vary between the two.
+
+Below, the corresponding tt(limit) keyword for each tt(ulimit) option is
+shown in parenthesis for reference:
+
 startsitem()
 sitem(tt(-a))(Lists all of the current resource limits.)
-sitem(tt(-b))(Socket buffer size in bytes LPAR()N.B. not kilobytes+RPAR())
-sitem(tt(-c))(512-byte blocks on the size of core dumps.)
-sitem(tt(-d))(Kilobytes on the size of the data segment.)
-sitem(tt(-f))(512-byte blocks on the size of files written.)
-sitem(tt(-i))(The number of pending signals.)
-sitem(tt(-k))(The number of kqueues allocated.)
-sitem(tt(-l))(Kilobytes on the size of locked-in memory.)
-sitem(tt(-m))(Kilobytes on the size of physical memory.)
-sitem(tt(-n))(open file descriptors.)
-sitem(tt(-p))(The number of pseudo-terminals.)
-sitem(tt(-q))(Bytes in POSIX message queues.)
+sitem(tt(-b))(Socket buffer size in bytes LPAR()N.B. not kibibytes+RPAR() (tt(sockbufsize)).)
+sitem(tt(-c))(512-byte blocks on the size of core dumps (tt(coredumpsize)).)
+sitem(tt(-d))(Kibibytes on the size of the data segment (tt(datasize)).)
+sitem(tt(-e))(nice priority (tt(nice)).)
+sitem(tt(-f))(512-byte blocks on the size of files written (tt(filesize)).)
+sitem(tt(-i))(The number of pending signals (tt(sigpending)).)
+sitem(tt(-k))(The number of kqueues allocated (tt(kqueues)).)
+sitem(tt(-l))(Kibibytes on the size of locked-in memory (tt(memorylocked)).)
+sitem(tt(-m))(Kibibytes on the size of physical memory (tt(resident)).)
+sitem(tt(-n))(open file descriptors (tt(descriptors)).)
+sitem(tt(-o))(umtx shared locks (tt(umtxp)).)
+sitem(tt(-p))(The number of pseudo-terminals (tt(pseudoterminals)).)
+sitem(tt(-q))(Bytes in POSIX message queues (tt(msgqueue)).)
 sitem(tt(-r))(Maximum real time priority.  On some systems where this
 is not available, such as NetBSD, this has the same effect as tt(-T)
-for compatibility with tt(sh).)
-sitem(tt(-s))(Kilobytes on the size of the stack.)
-sitem(tt(-T))(The number of simultaneous threads available to the user.)
-sitem(tt(-t))(CPU seconds to be used.)
-sitem(tt(-u))(The number of processes available to the user.)
-sitem(tt(-v))(Kilobytes on the size of virtual memory.  On some systems this
-refers to the limit called `address space'.)
-sitem(tt(-w))(Kilobytes on the size of swapped out memory.)
-sitem(tt(-x))(The number of locks on files.)
+for compatibility with tt(sh) (tt(rt_priority) / tt(maxpthreads)).)
+sitem(tt(-R))(realtime CPU time slice (tt(rt_time)).)
+sitem(tt(-s))(Kibibytes on the size of the stack (tt(stacksize)).)
+sitem(tt(-T))(The number of simultaneous threads available to the user (tt(maxpthreads)).)
+sitem(tt(-t))(CPU seconds to be used (tt(cputime)).)
+sitem(tt(-u))(The number of processes available to the user (tt(maxproc)).)
+sitem(tt(-v))(Kibibytes on the size of virtual memory.  On some systems this
+refers to the limit called em(address space) (tt(vmemorysize) / tt(addressspace)).)
+sitem(tt(-w))(Kibibytes on the size of swapped out memory (tt(swapsize)).)
+sitem(tt(-x))(The number of locks on files (tt(maxfilelocks)).)
 endsitem()
 
 A resource may also be specified by integer in the form `tt(-N)
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index b3396721f..259c00a6c 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -2837,15 +2837,15 @@ exactly var(n) bytes in length.
 
 If this flag is directly followed by a em(size specifier) `tt(k)' (`tt(K)'),
 `tt(m)' (`tt(M)'), or `tt(p)' (`tt(P)') (e.g. `tt(Lk-50)') the check is
-performed with kilobytes, megabytes, or blocks (of 512 bytes) instead.
-(On some systems additional specifiers are available for gigabytes,
-`tt(g)' or `tt(G)', and terabytes, `tt(t)' or `tt(T)'.) If a size specifier
-is used a file is regarded as "exactly" the size if the file size rounded up
-to the next unit is equal to the test size.  Hence `tt(*LPAR()Lm1+RPAR())'
-matches files from 1 byte up to 1 Megabyte inclusive.  Note also that
-the set of files "less than" the test size only includes files that would
-not match the equality test; hence `tt(*LPAR()Lm-1+RPAR())' only matches
-files of zero size.
+performed with kibibytes, mebibytes, or blocks (of 512 bytes; note: em(not)
+pebibytes) instead. (On some systems additional specifiers are available
+for gibibytes, `tt(g)' or `tt(G)', and tebibytes, `tt(t)' or `tt(T)'.) If a
+size specifier is used, a file is regarded as "exactly" the size if the file
+size rounded up to the next unit is equal to the test size.  Hence
+`tt(*LPAR()Lm1+RPAR())' matches files from 1 byte up to 1 Mebibyte
+inclusive.  Note also that the set of files "less than" the test size only
+includes files that would not match the equality test; hence
+`tt(*LPAR()Lm-1+RPAR())' only matches files of zero size.
 )
 item(tt(^))(
 negates all qualifiers following it
diff --git a/Doc/Zsh/mod_zpty.yo b/Doc/Zsh/mod_zpty.yo
index 3ca031c01..ba59e1783 100644
--- a/Doc/Zsh/mod_zpty.yo
+++ b/Doc/Zsh/mod_zpty.yo
@@ -66,8 +66,8 @@ read matches the var(pattern), even in the non-blocking case.  The return
 status is zero if the string read matches the pattern, or if the command
 has exited but at least one character could still be read.  If the option
 tt(-m) is present, the return status is zero only if the pattern matches.
-As of this writing, a maximum of one megabyte of output can be consumed
-this way; if a full megabyte is read without matching the pattern, the
+As of this writing, a maximum of one mebibyte of output can be consumed
+this way; if a full mebibyte is read without matching the pattern, the
 return status is non-zero.
 
 In all cases, the return status is non-zero if nothing could be read, and
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 36c1ae4c2..6c305da34 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -1473,7 +1473,7 @@ is specified with no command.  Defaults to tt(more).
 vindex(REPORTMEMORY)
 item(tt(REPORTMEMORY))(
 If nonnegative, commands whose maximum resident set size (roughly
-speaking, main memory usage) in kilobytes is greater than this
+speaking, main memory usage) in kibibytes is greater than this
 value have timing statistics reported.  The format used to output
 statistics is the value of the tt(TIMEFMT) parameter, which is the same
 as for the tt(REPORTTIME) variable and the tt(time) builtin; note that
@@ -1603,12 +1603,12 @@ sitem(tt(%E))(Elapsed time in seconds.)
 sitem(tt(%P))(The CPU percentage, computed as
 100*(tt(%U)PLUS()tt(%S))/tt(%E).)
 sitem(tt(%W))(Number of times the process was swapped.)
-sitem(tt(%X))(The average amount in (shared) text space used in kilobytes.)
+sitem(tt(%X))(The average amount in (shared) text space used in kibibytes.)
 sitem(tt(%D))(The average amount in (unshared) data/stack space used in
-kilobytes.)
-sitem(tt(%K))(The total space used (tt(%X)PLUS()tt(%D)) in kilobytes.)
+kibibytes.)
+sitem(tt(%K))(The total space used (tt(%X)PLUS()tt(%D)) in kibibytes.)
 sitem(tt(%M))(The  maximum memory the process had in use at any time in
-kilobytes.)
+kibibytes.)
 sitem(tt(%F))(The number of major page faults (page needed to be brought
 from disk).)
 sitem(tt(%R))(The number of minor page faults.)
diff --git a/Etc/BUGS b/Etc/BUGS
index 49e6a5c34..28e4f2b81 100644
--- a/Etc/BUGS
+++ b/Etc/BUGS
@@ -73,3 +73,7 @@ interactive and the subshell is the foreground job.  The USEZLE option is
 always turned off in subshells, for reasons lost to history.  There is a
 related, probably obsolete, vared special case for $TERM set to "emacs".
 ------------------------------------------------------------------------
+47634: missing rlim_t overflow checks in limit/ulimit limit arguments
+Integer values corresponding to the values of RLIM_INFINITY, RLIM_SAVED_MAX,
+RLIM_SAVED_CUR should probably be rejected as well (47639)
+------------------------------------------------------------------------
diff --git a/NEWS b/NEWS
index d05e8b64f..9d218153b 100644
--- a/NEWS
+++ b/NEWS
@@ -11,7 +11,12 @@ The zsh/system module's `zsystem flock` command learnt an -i option to
 set the wait interval used with -t. Additionally, -t now supports
 fractional seconds.
 
+The `limit` builtin accepts more units for resource limits, now shared
+with the `ulimit` builtin allowing limit values to be specified.
+`limit -s somelimit` now reports the shell process' own value of the limit.
 The `limit` and `unlimit` builtins are now available again in csh emulation.
+A few more improvements to the resource limit handling interfaces and
+documentation have been made.
 
 Changes from 5.7.1-test-3 to 5.8
 --------------------------------
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 5f9c84b0f..b6568d956 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -55,7 +55,8 @@ typedef struct resinfo_T {
  * 1. Add zsh_LIMIT_PRESENT(RLIMIT_XXX) in configure.ac.
  * 2. Add an entry for RLIMIT_XXX to known_resources[].
  *    Make sure the option letter (resinto_T.opt) is unique.
- * 3. Build zsh and run the test B12rlimit.ztst.
+ * 3. Add entry in documentation for the limit and ulimit builtins
+ * 4. Build zsh and run the test B12rlimit.ztst.
  */
 static const resinfo_T known_resources[] = {
     {RLIMIT_CPU, "cputime", ZLIMTYPE_TIME, 1,
@@ -109,12 +110,23 @@ static const resinfo_T known_resources[] = {
 		'i', "pending signals"},
 # endif
 # ifdef HAVE_RLIMIT_MSGQUEUE
-    {RLIMIT_MSGQUEUE, "msgqueue", ZLIMTYPE_NUMBER, 1,
+    {RLIMIT_MSGQUEUE, "msgqueue", ZLIMTYPE_MEMORY, 1,
 		'q', "bytes in POSIX msg queues"},
 # endif
 # ifdef HAVE_RLIMIT_NICE
+    /*
+     * Not max niceness. On Linux ranges [1..40] for that RLIM translates
+     * to [-19..20] meaning that for instance setting it to 20 means
+     * processes can lower their niceness as far down as -1 and with the
+     * default of 0, unprivileged processes can't lower their niceness.
+     *
+     * Increasing niceness is always possible.
+     *
+     * "max nice priority" is the wording used in /proc/self/limits on
+     * Linux.
+     */
     {RLIMIT_NICE, "nice", ZLIMTYPE_NUMBER, 1,
-		'e', "max nice"},
+		'e', "max nice priority"},
 # endif
 # ifdef HAVE_RLIMIT_RTPRIO
     {RLIMIT_RTPRIO, "rt_priority", ZLIMTYPE_NUMBER, 1,
@@ -122,7 +134,7 @@ static const resinfo_T known_resources[] = {
 # endif
 # ifdef HAVE_RLIMIT_RTTIME
     {RLIMIT_RTTIME, "rt_time", ZLIMTYPE_MICROSECONDS, 1,
-		'N', "rt cpu time (microseconds)"},
+		'R', "rt cpu time slice (microseconds)"},
 # endif
     /* BSD */
 # ifdef HAVE_RLIMIT_SBSIZE
@@ -192,9 +204,9 @@ set_resinfo(void)
 	if (!resinfo[i]) {
 	    /* unknown resource */
 	    resinfo_T *info = (resinfo_T *)zshcalloc(sizeof(resinfo_T));
-	    char *buf = (char *)zalloc(12);
+	    char *buf = (char *)zalloc(8 /* UNKNOWN- */ + 3 /* digits */ + 1 /* '\0' */);
 	    snprintf(buf, 12, "UNKNOWN-%d", i);
-	    info->res = - 1;	/* negative value indicates "unknown" */
+	    info->res = -1;	/* negative value indicates "unknown" */
 	    info->name = buf;
 	    info->type = ZLIMTYPE_UNKNOWN;
 	    info->unit = 1;
@@ -255,38 +267,204 @@ printrlim(rlim_t val, const char *unit)
 # endif /* RLIM_T_IS_QUAD_T */
 }
 
+/*
+ * Parse a string into the corresponding value based on the limit type
+ *
+ *   ZLIMTYPE_UNKNOWN / ZLIMTYPE_NUMBER:
+ *     decimal integer without sign only: raw value
+ *
+ *   ZLIMTYPE_TIME / ZLIMTYPE_MICROSECONDS:
+ *     <decimal> only gives seconds or microseconds depending on type
+ *     or:
+ *     [[hour:]min:]sec[.usec]
+ *     or:
+ *     <decimal> with h (hour), m (min), s (sec), ms, us suffix.
+ *
+ *   ZLIMTYPE_MEMORY:
+ *     <decimal> without suffix interpreted as KiB by "limit" (except for
+ *     RLIMIT_MSGQUEUE, see below) and based on resinfo.unit by "ulimit".
+ *
+ *     K/M/G/T/P/E suffix and same with iB suffix use 1024 factor
+ *     KB/MB/GB... use 1000 factor.
+ *
+ *     B for bytes (avoids that mess about default units).
+ *
+ * All suffixes are case insensitive.
+ *
+ */
+
 /**/
 static rlim_t
-zstrtorlimt(const char *s, char **t, int base)
+zstrtorlimt(const char *s, int lim, int ulimit, char **err)
 {
     rlim_t ret = 0;
+    const char *orig = s;
+    enum zlimtype type = resinfo[lim]->type;
+    *err = NULL;
 
-    if (strcmp(s, "unlimited") == 0) {
-	if (t)
-	    *t = (char *) s + 9;
+    if (strcmp(s, "unlimited") == 0)
 	return RLIM_INFINITY;
+
+    for (; *s >= '0' && *s <= '9'; s++)
+	ret = ret * 10 + *s - '0';
+
+    if (s == orig) {
+	*err = "decimal integer expected";
+	return 0;
+    }
+
+    if (lim >= RLIM_NLIMITS ||
+	type == ZLIMTYPE_NUMBER ||
+	type == ZLIMTYPE_UNKNOWN) {
+	/*
+	 * pure numeric resource -- only a straight decimal number is
+	 * permitted.
+	 */
+	if (*s) {
+	    *err = "limit must be a decimal integer";
+	    return 0;
+	}
+    }
+    else if (type == ZLIMTYPE_TIME ||
+	     type == ZLIMTYPE_MICROSECONDS) {
+	if (*s) {
+	    int divisor = 1;
+	    int factor = 1;
+	    switch (*s++) {
+
+	    case 'm': /* m for minute or ms for milisecond */
+	    case 'M':
+		if (*s == 's' || *s == 'S') {
+		    s++;
+		    divisor = 1000;
+		}
+		else {
+		    factor = 60;
+		}
+		break;
+
+	    case 'h':
+	    case 'H':
+		factor = 3600;
+		break;
+
+	    case 's':
+	    case 'S':
+		break;
+
+	    case 'u':
+	    case 'U':
+		divisor = 1000000;
+		if (*s == 's' || *s == 'S')
+		    s++;
+		break;
+
+	    case ':':
+		do {
+		    int more = 0;
+		    while (*s >= '0' && *s <= '9') {
+			more = more * 10 + (*s - '0');
+			s++;
+		    }
+		    ret = ret * 60 + more;
+		} while (*s == ':' && ++s);
+		if (*s == '.')
+		    s++;
+		    /* fallthrough */
+		else
+		    break;
+	    case '.':
+		if (type == ZLIMTYPE_MICROSECONDS) {
+		    int frac;
+		    for (frac = 0; *s >= '0' && *s <= '9'; s++) {
+			if (divisor < 1000000) {
+			    /* ignore digits past the 6th */
+			    divisor *= 10;
+			    frac = frac * 10 + (*s - '0');
+			}
+		    }
+		    ret = ret * divisor + frac;
+		}
+		else {
+		    /* fractional part ignored */
+		    while (*s >= '0' && *s <= '9')
+			s++;
+		}
+		break;
+	    default:
+		*err = "invalid time specification";
+		return 0;
+	    }
+
+	    if (*s) {
+		*err = "invalid time specification";
+		return 0;
+	    }
+
+	    ret *= factor;
+	    if (type == ZLIMTYPE_MICROSECONDS)
+		ret *= 1000000 / divisor;
+	    else
+		ret /= divisor;
+	}
+    }
+    else {
+	/*
+	 * memory-type resource
+	 */
+	if (*s) {
+	    if (*s == 'b' || *s == 'B')
+		s++;
+	    else {
+		const char *suffix = "kKmMgGtTpPeE";
+		char *offset;
+
+		if ((offset = strchr(suffix, *s))) {
+		    s++;
+		    if (*s == 'b' || *s == 'B') {
+			/* KB == 1000 */
+			const char *p;
+			for (p = suffix; p <= offset; p += 2)
+			    ret *= 1000;
+			s++;
+		    }
+		    else {
+			/* K/KiB == 1024 */
+			if ((s[0] == 'i' || s[0] == 'I') &&
+			    (s[1] == 'b' || s[1] == 'B'))
+			    s += 2;
+			ret <<= ((offset - suffix) / 2 + 1) * 10;
+		    }
+		}
+	    }
+	    if (*s) {
+		*err = "invalid unit";
+		return 0;
+	    }
+	}
+	else {
+	    if (ulimit)
+		ret *= resinfo[lim]->unit;
+	    else
+#ifdef HAVE_RLIMIT_MSGQUEUE
+		if (lim != RLIMIT_MSGQUEUE)
+		    /*
+		     * Historical quirk. In tcsh's limit (and bash's and mksh's
+		     * ulimit, but not ksh93), that limit expects bytes instead
+		     * of kibibytes and earlier versions of zsh were treating
+		     * it as a ZLIMTYPE_NUMBER.
+		     *
+		     * We still want to treat it as ZLIMTYPE_MEMORY and accept
+		     * KMG... suffixes as it is a number of bytes.
+		     *
+		     * But we carry on taking the value as a number of *bytes*
+		     * in the "limit" builtin for backward compatibility and
+		     * compatibility with tcsh.
+		     */
+#endif
+		    ret *= 1024;
+	}
     }
-# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_LONG_LONG) || defined(RLIM_T_IS_UNSIGNED)
-    if (!base) {
-	if (*s != '0')
-	    base = 10;
-	else if (*++s == 'x' || *s == 'X')
-	    base = 16, s++;
-	else
-	    base = 8;
-    } 
-    if (base <= 10)
-	for (; *s >= '0' && *s < ('0' + base); s++)
-	    ret = ret * base + *s - '0';
-    else
-	for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
-	     || (*s >= 'A' && *s < ('A' + base - 10)); s++)
-	    ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
-    if (t)
-	*t = (char *)s;
-# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
-    ret = zstrtol(s, t, base);
-# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
     return ret;
 }
 
@@ -317,25 +495,42 @@ showlimitvalue(int lim, rlim_t val)
 	       resinfo[lim]->type == ZLIMTYPE_UNKNOWN)
 	printrlim(val, "\n");	/* pure numeric resource */
     else {
-	/* memory resource -- display with `k' or `M' modifier */
-	if (val >= 1024L * 1024L)
-	    printrlim(val/(1024L * 1024L), "MB\n");
+	/*
+	 * memory resource -- display with KiB/MiB... for exact
+	 * multiples of those units
+	 */
+	const char *units = "KMGTPE";
+	rlim_t v = val;
+	int n = 0;
+	while (units[n] && (v & 1023) == 0 && v >> 10) {
+	    n++;
+	    v >>= 10;
+	}
+	if (n) {
+	    char suffix[] = "XiB\n";
+	    *suffix = units[n-1];
+	    printrlim(v, suffix);
+	}
 	else
-	    printrlim(val/1024L, "kB\n");
+	    printrlim(val, "B\n");
     }
 }
 
-/* Display resource limits.  hard indicates whether `hard' or `soft'  *
- * limits should be displayed.  lim specifies the limit, or may be -1 *
- * to show all.                                                       */
+/*
+ * Display resource limits.  hard indicates whether `hard' or `soft'
+ * limits should be displayed.  lim specifies the limit, or may be -1
+ * to show all.  If `shell' is non-zero, the limits in place for the
+ * shell process retrieved with getrlimits() are shown.
+ */
 
 /**/
 static int
-showlimits(char *nam, int hard, int lim)
+showlimits(char *nam, int hard, int lim, int shell)
 {
     int rt;
+    int ret = 0;
 
-    if (lim >= RLIM_NLIMITS)
+    if (shell || lim >= RLIM_NLIMITS)
     {
 	/*
 	 * Not configured into the shell.  Ask the OS
@@ -357,17 +552,40 @@ showlimits(char *nam, int hard, int lim)
     else
     {
 	/* main loop over resource types */
-	for (rt = 0; rt != RLIM_NLIMITS; rt++)
-	    showlimitvalue(rt, (hard) ? limits[rt].rlim_max :
-			   limits[rt].rlim_cur);
+	for (rt = 0; rt != RLIM_NLIMITS; rt++) {
+	    struct rlimit vals, *plim;
+	    if (shell) {
+		/*
+		 * FIXME: this is dead code as at the moment, there is no API
+		 * for the user can request all limits *of the shell* be
+		 * displayed.
+		 *
+		 * Should we add a "limit -s -a" or "limit -s all"?
+		 */
+		plim = &vals;
+		if (getrlimit(rt, plim) < 0)
+		{
+		    zwarnnam(nam, "can't read \"%s\" limit: %e", resinfo[rt]->name, errno);
+		    ret = 1;
+		    continue;
+		}
+	    }
+	    else
+		plim = &(limits[rt]);
+
+	    showlimitvalue(rt, (hard) ? plim->rlim_max :
+			   plim->rlim_cur);
+	}
     }
 
-    return 0;
+    return ret;
 }
 
-/* Display a resource limit, in ulimit style.  lim specifies which   *
- * limit should be displayed, and hard indicates whether the hard or *
- * soft limit should be displayed.                                   */
+/*
+ * Display a resource limit, in ulimit style.  lim specifies which
+ * limit should be displayed, and hard indicates whether the hard or
+ * soft limit should be displayed.
+ */
 
 /**/
 static int
@@ -394,12 +612,12 @@ printulimit(char *nam, int lim, int hard, int head)
 	if (lim < RLIM_NLIMITS) {
 	    const resinfo_T *info = resinfo[lim];
 	    if (info->opt == 'N')
-		printf("-N %2d: %-29s", lim, info->descr);
+		printf("-N %2d: %-32s ", lim, info->descr);
 	    else
-		printf("-%c: %-32s", info->opt, info->descr);
+		printf("-%c: %-35s ", info->opt, info->descr);
 	}
 	else
-	    printf("-N %2d: %-29s", lim, "");
+	    printf("-N %2d: %-32s ", lim, "");
     }
     /* display the limit */
     if (limit == RLIM_INFINITY)
@@ -511,16 +729,18 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
     rlim_t val;
     int ret = 0;
 
-    hard = OPT_ISSET(ops,'h');
-    if (OPT_ISSET(ops,'s') && !*argv)
+    hard = OPT_ISSET(ops, 'h');
+    if (OPT_ISSET(ops, 's') && !*argv)
 	return setlimits(NULL);
     /* without arguments, display limits */
     if (!*argv)
-	return showlimits(nam, hard, -1);
+	return showlimits(nam, hard, -1, OPT_ISSET(ops, 's'));
     while ((s = *argv++)) {
 	/* Search for the appropriate resource name.  When a name matches (i.e. *
 	 * starts with) the argument, the lim variable changes from -1 to the   *
 	 * number of the resource.  If another match is found, lim goes to -2.  */
+	char *err;
+
 	if (idigit(*s))
 	{
 	    lim = (int)zstrtol(s, NULL, 10);
@@ -543,61 +763,12 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
 	}
 	/* without value for limit, display the current limit */
 	if (!(s = *argv++))
-	    return showlimits(nam, hard, lim);
-	if (lim >= RLIM_NLIMITS)
-	{
-	    val = zstrtorlimt(s, &s, 10);
-	    if (*s)
-	    {
-		/* unknown limit, no idea how to scale */
-		zwarnnam(nam, "unknown scaling factor: %s", s);
-		return 1;
-	    }
-	}
-	else if (resinfo[lim]->type == ZLIMTYPE_TIME) {
-	    /* time-type resource -- may be specified as seconds, or minutes or *
-	     * hours with the `m' and `h' modifiers, and `:' may be used to add *
-	     * together more than one of these.  It's easier to understand from *
-	     * the code:                                                        */
-	    val = zstrtorlimt(s, &s, 10);
-	    if (*s) {
-		if ((*s == 'h' || *s == 'H') && !s[1])
-		    val *= 3600L;
-		else if ((*s == 'm' || *s == 'M') && !s[1])
-		    val *= 60L;
-		else if (*s == ':')
-		    val = val * 60 + zstrtorlimt(s + 1, &s, 10);
-		else {
-		    zwarnnam(nam, "unknown scaling factor: %s", s);
-		    return 1;
-		}
-	    }
-	} else if (resinfo[lim]->type == ZLIMTYPE_NUMBER ||
-		   resinfo[lim]->type == ZLIMTYPE_UNKNOWN ||
-		   resinfo[lim]->type == ZLIMTYPE_MICROSECONDS) {
-	    /* pure numeric resource -- only a straight decimal number is
-	    permitted. */
-	    char *t = s;
-	    val = zstrtorlimt(t, &s, 10);
-	    if (s == t) {
-		zwarnnam(nam, "limit must be a number");
-		return 1;
-	    }
-	} else {
-	    /* memory-type resource -- `k', `M' and `G' modifiers are *
-	     * permitted, meaning (respectively) 2^10, 2^20 and 2^30. */
-	    val = zstrtorlimt(s, &s, 10);
-	    if (!*s || ((*s == 'k' || *s == 'K') && !s[1])) {
-		if (val != RLIM_INFINITY)
-		    val *= 1024L;
-	    } else if ((*s == 'M' || *s == 'm') && !s[1])
-		val *= 1024L * 1024;
-	    else if ((*s == 'G' || *s == 'g') && !s[1])
-		val *= 1024L * 1024 * 1024;
-	    else {
-		zwarnnam(nam, "unknown scaling factor: %s", s);
-		return 1;
-	    }
+	    return showlimits(nam, hard, lim, OPT_ISSET(ops, 's'));
+
+	val = zstrtorlimt(s, lim, 0, &err);
+	if (err) {
+	    zwarnnam(nam, err);
+	    return 1;
 	}
 	if (do_limit(nam, lim, val, hard, !hard, OPT_ISSET(ops, 's')))
 	    ret++;
@@ -821,14 +992,12 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 		    limit = vals.rlim_max;
 		}
 	    } else {
-		limit = zstrtorlimt(*argv, &eptr, 10);
-		if (*eptr) {
-		    zwarnnam(name, "invalid number: %s", *argv);
+		char *err;
+		limit = zstrtorlimt(*argv, res, 1, &err);
+		if (err) {
+		    zwarnnam(name, "%s: %s", *argv, err);
 		    return 1;
 		}
-		/* scale appropriately */
-		if (res < RLIM_NLIMITS)
-		    limit *= resinfo[res]->unit;
 	    }
 	    if (do_limit(name, res, limit, hard, soft, 1))
 		ret++;
diff --git a/Test/B12limit.ztst b/Test/B12limit.ztst
index 48d33e6e3..7c45deb8f 100644
--- a/Test/B12limit.ztst
+++ b/Test/B12limit.ztst
@@ -26,3 +26,160 @@ F:report this to zsh-workers mailing list.
   }
 0:check if limit option letters are unique
 
+  if sh -c 'ulimit -f 2048' > /dev/null 2>&1; then
+    (
+      set -o braceccl -o pipefail
+      list=(1b 1{kmgtpe}{,b,ib})
+      for cmd in "limit filesize" ulimit; do
+	for l in $list $list:u; do
+	  $=cmd $l &&
+	    limit filesize &&
+	    ulimit || exit
+	done
+      done | sed 'N;s/\n/ /;s/  */ /g'
+    )
+  else
+    ZTST_skip='Cannot set the filesize limit on this system'
+  fi
+0:filesize suffixes with limit and ulimit
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+
+  if sh -c 'ulimit -t 3600' > /dev/null 2>&1; then
+  (
+    set -o pipefail
+    list=(1h 30m 20s 30 1:23:45.123456 2:23 56.4)
+    for cmd in "limit cputime" "ulimit -t"; do
+      for l in $list ${(MU)list:#*[a-z]*}; do
+        $=cmd $l &&
+	  limit cputime &&
+	  ulimit -t || exit
+      done
+    done | sed 'N;s/\n/ /;s/  */ /g'
+  )
+  else
+    ZTST_skip='Cannot set the cputime limit on this system'
+  fi
+0:time limit formats
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
+>cputime 0:00:30 30
+>cputime 1:23:45 5025
+>cputime 0:02:23 143
+>cputime 0:00:56 56
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
+>cputime 0:00:30 30
+>cputime 1:23:45 5025
+>cputime 0:02:23 143
+>cputime 0:00:56 56
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
+
+  ulimit 1Kite
+  ulimit 1D
+  ulimit 1s
+  ulimit 1MBA
+  limit cputime 1k
+  limit cputime 1:0s
+  limit cputime 1ss
+  limit cputime 1msx
+  limit cputime 1.0s
+  limit cputime .1
+  limit descriptors 1k
+  limit descriptors 1h
+  limit descriptors 1:0
+1:invalid limit input
+?(eval):ulimit:1: 1Kite: invalid unit
+?(eval):ulimit:2: 1D: invalid unit
+?(eval):ulimit:3: 1s: invalid unit
+?(eval):ulimit:4: 1MBA: invalid unit
+?(eval):limit:5: invalid time specification
+?(eval):limit:6: invalid time specification
+?(eval):limit:7: invalid time specification
+?(eval):limit:8: invalid time specification
+?(eval):limit:9: invalid time specification
+?(eval):limit:10: decimal integer expected
+?(eval):limit:11: limit must be a decimal integer
+?(eval):limit:12: limit must be a decimal integer
+?(eval):limit:13: limit must be a decimal integer
-- 
2.25.1




^ permalink raw reply	[relevance 4%]

* Re: More rabbit-holes with unset variables
  @ 2020-11-27  0:30  4%               ` Felipe Contreras
  2020-11-27  0:51  3%                 ` Bart Schaefer
  0 siblings, 1 reply; 200+ results
From: Felipe Contreras @ 2020-11-27  0:30 UTC (permalink / raw)
  To: Bart Schaefer; +Cc: Zsh hackers list

On Thu, Nov 26, 2020 at 6:09 PM Bart Schaefer <schaefer@brasslantern.com> wrote:

> How strings in C are typically declared and how scalars are
> represented in zsh are not the same thing.
>
> I deliberately chose to write char[] to demonstrate that zsh scalars
> are never null pointers.

But we were not talking about zsh, we were talking about shell in
general. Specifically the POSIX standard.

You are mixing and matching. First speaking about POSIX, then about
scalars, then about "char []", then you say "the shell language
doesn't really deal in anything else."

In the context of shell in general they are strings (scalars and "char
[]" are zsh-specific).

-- 
Felipe Contreras



^ permalink raw reply	[relevance 4%]

* Re: More rabbit-holes with unset variables
  2020-11-27  0:30  4%               ` Felipe Contreras
@ 2020-11-27  0:51  3%                 ` Bart Schaefer
    0 siblings, 1 reply; 200+ results
From: Bart Schaefer @ 2020-11-27  0:51 UTC (permalink / raw)
  To: Felipe Contreras; +Cc: Zsh hackers list

On Thu, Nov 26, 2020 at 4:31 PM Felipe Contreras
<felipe.contreras@gmail.com> wrote:
>
> You are mixing and matching. First speaking about POSIX, then about
> scalars, then about "char []", then you say "the shell language
> doesn't really deal in anything else."

This is now beyond silly, but:

What was or not POSIX is a indirectly-related subtopic.

I mentioned scalars.

You "supposed" that meant strings.

I clarified strings were not what I meant.

When after that I said "anything else", the word "else" was referring
back ("on the other hand") to your reference to strings, not to my
reference to scalars.



^ permalink raw reply	[relevance 3%]

* Re: [PATCHv2 2/2] [long] improvements to limit/ulimit API and doc (the rest)
  @ 2020-11-27 20:13  4%       ` Stephane Chazelas
  0 siblings, 0 replies; 200+ results
From: Stephane Chazelas @ 2020-11-27 20:13 UTC (permalink / raw)
  To: Daniel Shahaf; +Cc: Zsh hackers list

2020-11-27 16:39:49 +0000, Daniel Shahaf:
[...]
> Please specify in the docstring the types, values, and meanings of the
> formal parameters.

I've tried to address that in this v3 patch. Specifying the type
(as in char*, int...) seems redundant though and not generally
done in other functions, so I've left it out for now.

[...]
> Could you arrange for the invalid value to be included in the error
> message, as in «zwarnnam(nam, "invalid time specification: %s", foo)»?
> That tends to be more user-friendly, particularly when the call stack is
> deep.
[...]
> > +	    zwarnnam(nam, err);
> 
> This should be zwarnnam(nam, "%s", err), otherwise any percent signs in
> «err» would cause breakage.
[...]

Thanks. Both very good points. I had done that for ulimit, but
not limit. Should just be a matter of:

--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -767,7 +767,7 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
 
 	val = zstrtorlimt(s, lim, 0, &err);
 	if (err) {
-	    zwarnnam(nam, err);
+	    zwarnnam(nam, "%s: %s", s, err);
 	    return 1;
 	}
 	if (do_limit(nam, lim, val, hard, !hard, OPT_ISSET(ops, 's')))

(along with the test update)

From: Stephane Chazelas <stephane@chazelas.org>
Date: Thu, 26 Nov 2020 20:38:57 +0000
Subject: [PATCH] improve limit/ulimit API and documentation

A few issues addressed:

* msgqueue limit specifies a number of bytes but was classified as
  ZLIMTYPE_NUMBER which meant k/m suffixes were not allowed and the
  default unit for `limit` was 1 (byte) instead of the 1024 (KiB)
  specified by documentation.

  -> turns out tcsh's `limit` handled that limit (called `maxmessage`
     there) the same way and `bash`, `bosh` and `mksh`'s `ulimit` (not
     ksh93 which takes kibibytes there) also expect a number of bytes
     there.

     So, the type was changed to ZLIMTYPE_MEMORY, but the unit remains
     bytes for both `limit` and `ulimit` as a (now documented) special
     quirk for backward compatibility.

* unit suffixes, where not supported (like for ZLIMTYPE_NUMBER) are
  silently ignored.

  -> now return an error on unexpected or unrecognised suffixes.

* limit output using ambigous kB, MB units. These days, those tend to
  imply decimal scaling factors (1000B, 1000000B).

  -> changed output to use KiB, MiB, the ISO 80000 unambiguous ones
     which are now more or less mainstream.
  -> those, extended to KMGTPE..iB are also accepted on input while
     KB, MB... are interpreted as decimal. (K, M, G remain binary).
  -> documentation updated to avoid kilobyte, megabyte and use
     unambiguous kibibyte, mebibyte... instead.

-> rt_time limit is now `ulimit -R` like in bash or bosh.

* "nice" limit description ("max nice") was misleading as it's more
  linked to the *minimum* niceness the process can get.

  -> Changed to "max nice priority" matching the Linux kernel's own
     description.

* documentation for both `limit` and `ulimit` missing a few limits ->
  added

* time limits are output as h:mm:ss but that's not accepted on input.
  1:00:00 silently truncated to 1:00 (one minute instead of one hour).

  -> accept [[hh:]mm:]ss[.usec]

* limit and ulimit output truncate precision (1000B limit represented as
  0kB and 1 respectively)

  -> addressed in `limit` but not `ulimit` (where
     compliance/compatibility with ksh matters). By only using scaling
     factors when the limit can be represented in an integer number of
     them (using B, as in 1000B above when none qualify).

* some limits can't be set to arbitrary values (like that 1000 above for
  filesize) and it's not always obvious what the default unit should be.

  -> recognise the B suffix on input for "memory" type limits and
     "s"/"ms"/"ns" for time/microsecond type units so the user can input
     values unambiguously.
  -> those suffixes are now recognised by both limit and ulimit. Parsing
     code factorised into `zstrtorlimt`.

* `limit` changes the limits for children, `ulimit` for the shell,
  but both report limits for children, and it's not possible to
  retrieve the shell's own limits.

  -> ulimit not changed as it's probably better that way.
  -> `limit -s somelimit` changed so it reports the somelimit value
     for the shell process

-> documentation improved.
---
 Doc/Zsh/builtins.yo    | 125 +++++++++----
 Doc/Zsh/expn.yo        |  18 +-
 Doc/Zsh/mod_zpty.yo    |   4 +-
 Doc/Zsh/params.yo      |  10 +-
 Etc/BUGS               |   4 +
 NEWS                   |   5 +
 Src/Builtins/rlimits.c | 407 +++++++++++++++++++++++++++++------------
 Test/B12limit.ztst     | 157 ++++++++++++++++
 8 files changed, 563 insertions(+), 167 deletions(-)

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 2f1ccd8a5..5a2c51b0b 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -1116,15 +1116,18 @@ cindex(resource limits)
 cindex(limits, resource)
 item(tt(limit) [ tt(-hs) ] [ var(resource) [ var(limit) ] ] ...)(
 Set or display resource limits.  Unless the tt(-s) flag is given,
-the limit applies only the children of the shell.  If tt(-s) is
-given without other arguments, the resource limits of the current
-shell is set to the previously set resource limits of the children.
+the limit applies only to child processes and executed commands, not the
+current shell process itself.
 
-If var(limit) is not specified, print the current limit placed
-on var(resource), otherwise
-set the limit to the specified value.  If the tt(-h) flag
-is given, use hard limits instead of soft limits.
-If no var(resource) is given, print all limits.
+If tt(-s) is given without other arguments, the resource limits of the
+current shell are set to the previously set resource limits of the
+children.
+
+If var(limit) is not specified, print the current limit placed on
+var(resource) (of the shell with tt(-s), of children without), otherwise
+set the limit to the specified value.  If the tt(-h) flag is given, use
+hard limits instead of soft limits. If no var(resource) is given, print
+all limits.
 
 When looping over multiple resources, the shell will abort immediately if
 it detects a badly formed argument.  However, if it fails to set a limit
@@ -1143,18 +1146,23 @@ sitem(tt(datasize))(Maximum data size (including stack) for each process.)
 sitem(tt(descriptors))(Maximum value for a file descriptor.)
 sitem(tt(filesize))(Largest single file allowed.)
 sitem(tt(kqueues))(Maximum number of kqueues allocated.)
+sitem(tt(maxfilelocks))(Maximum number of file locks.)
 sitem(tt(maxproc))(Maximum number of processes.)
-sitem(tt(maxpthreads))(Maximum number of threads per process.)
+sitem(tt(maxpthreads))(Maximum number of threads per uid (NetBSD/OpenBSD) or per process.)
 sitem(tt(memorylocked))(Maximum amount of memory locked in RAM.)
 sitem(tt(memoryuse))(Maximum resident set size.)
 sitem(tt(msgqueue))(Maximum number of bytes in POSIX message queues.)
+sitem(tt(nice))(Maximum nice priority.)
 sitem(tt(posixlocks))(Maximum number of POSIX locks per user.)
 sitem(tt(pseudoterminals))(Maximum number of pseudo-terminals.)
 sitem(tt(resident))(Maximum resident set size.)
+sitem(tt(rt_priority))(Maximum realtime priority.)
+sitem(tt(rt_time))(Maximum realtime CPU time slice.)
 sitem(tt(sigpending))(Maximum number of pending signals.)
 sitem(tt(sockbufsize))(Maximum size of all socket buffers.)
 sitem(tt(stacksize))(Maximum stack size for each process.)
 sitem(tt(swapsize))(Maximum amount of swap used.)
+sitem(tt(umtxp))(Maximum number of umtx shared locks.)
 sitem(tt(vmemorysize))(Maximum amount of virtual memory.)
 endsitem()
 
@@ -1169,14 +1177,42 @@ the limit anyway, and will report an error if this fails.  As the shell
 does not store such resources internally, an attempt to set the limit will
 fail unless the tt(-s) option is present.
 
-var(limit) is a number, with an optional scaling factor, as follows:
+If var(limit) is a decimal integer number without suffix, then for
+historical reasons and compatibility with csh where that command comes from,
+the unit will depend on the type of limit: microseconds for tt(rt_time),
+seconds for all other time limits, kibibytes (1024 bytes) for all limits that
+express a number of bytes (except tt(msgqueue)).
+
+Instead, to avoid confusion, a suffix (case insensitive) may be appended to
+the (integer only) decimal number to specify the unit:
 
 startsitem()
-sitem(var(n)tt(h))(hours)
-sitem(var(n)tt(k))(kilobytes (default))
-sitem(var(n)tt(m))(megabytes or minutes)
-sitem(var(n)tt(g))(gigabytes)
-sitem([var(mm)tt(:)]var(ss))(minutes and seconds)
+sitem(time limits)(
+startsitem()
+sitem(tt(h))(hours)
+sitem(tt(m))(minutes)
+sitem(tt(s))(seconds)
+sitem(tt(ms))(milliseconds)
+sitem(tt(us))(microseconds)
+sitem(tt([[var(h):]var(m):]var(s)[.var(usec)]))(all combined)
+endsitem()
+
+For instance a 2 millisecond limit of tt(rt_time), can be expressed as
+tt(2ms), tt(2000us), tt(0.002), tt(0:0:0.002) or tt(2000) without unit.)
+sitem(memory limits)(
+startsitem()
+sitem(tt(k, kib))(kibibytes (1024 bytes))
+sitem(tt(m, mib))(mibibytes)
+sitem(...)(and so on with tt(g), tt(t), tt(p), tt(e) for gibibytes,
+tebibytes, pebibytes and exbibytes respectively)
+sitem(tt(kb))(kilobytes (1,000 bytes))
+sitem(tt(mb))(megabytes (1,000,000 bytes))
+sitem(...)(and so on with tt(g), tt(t), tt(p), tt(e) for gigabytes,
+terabytes, petabytes and exabytes respectively.)
+sitem(tt(b))(em(byte) to set the limit with arbitrary precisions)
+endsitem())
+sitem(other limits)(other limits are assumed to be numerical and ony
+a decimal integer number is accepted.)
 endsitem()
 
 The tt(limit) command is not made available by default when the
@@ -2250,8 +2286,8 @@ together with the tt(-H) flag set both hard and soft limits.
 If no options are used, the file size limit (tt(-f)) is assumed.
 
 If var(limit) is omitted the current value of the specified resources are
-printed.  When more than one resource value is printed, the limit name and
-unit is printed before each value.
+printed (rounded down to the corresponding unit).  When more than one resource
+value is printed, the limit name and unit is printed before each value.
 
 When looping over multiple resources, the shell will abort immediately if
 it detects a badly formed argument.  However, if it fails to set a limit
@@ -2260,30 +2296,45 @@ for some other reason it will continue trying to set the remaining limits.
 Not all the following resources are supported on all systems.  Running
 tt(ulimit -a) will show which are supported.
 
+Whilst tt(limit) is the BSD/csh-style command, tt(ulimit) is the SysV/Korn
+shell equivalent (added later to zsh for compatibility with ksh). While
+tt(limit) in zsh sets the limits for child processes by default, tt(ulimit)
+sets them for both the shell and child processes, and reports the ones
+set for child processes.
+
+The same suffixes are recognised as for tt(limit), but bear in mind that
+default units when no suffix is specified vary between the two.
+
+Below, the corresponding tt(limit) keyword for each tt(ulimit) option is
+shown in parenthesis for reference:
+
 startsitem()
 sitem(tt(-a))(Lists all of the current resource limits.)
-sitem(tt(-b))(Socket buffer size in bytes LPAR()N.B. not kilobytes+RPAR())
-sitem(tt(-c))(512-byte blocks on the size of core dumps.)
-sitem(tt(-d))(Kilobytes on the size of the data segment.)
-sitem(tt(-f))(512-byte blocks on the size of files written.)
-sitem(tt(-i))(The number of pending signals.)
-sitem(tt(-k))(The number of kqueues allocated.)
-sitem(tt(-l))(Kilobytes on the size of locked-in memory.)
-sitem(tt(-m))(Kilobytes on the size of physical memory.)
-sitem(tt(-n))(open file descriptors.)
-sitem(tt(-p))(The number of pseudo-terminals.)
-sitem(tt(-q))(Bytes in POSIX message queues.)
+sitem(tt(-b))(Socket buffer size in bytes LPAR()N.B. not kibibytes+RPAR() (tt(sockbufsize)).)
+sitem(tt(-c))(512-byte blocks on the size of core dumps (tt(coredumpsize)).)
+sitem(tt(-d))(Kibibytes on the size of the data segment (tt(datasize)).)
+sitem(tt(-e))(nice priority (tt(nice)).)
+sitem(tt(-f))(512-byte blocks on the size of files written (tt(filesize)).)
+sitem(tt(-i))(The number of pending signals (tt(sigpending)).)
+sitem(tt(-k))(The number of kqueues allocated (tt(kqueues)).)
+sitem(tt(-l))(Kibibytes on the size of locked-in memory (tt(memorylocked)).)
+sitem(tt(-m))(Kibibytes on the size of physical memory (tt(resident)).)
+sitem(tt(-n))(open file descriptors (tt(descriptors)).)
+sitem(tt(-o))(umtx shared locks (tt(umtxp)).)
+sitem(tt(-p))(The number of pseudo-terminals (tt(pseudoterminals)).)
+sitem(tt(-q))(Bytes in POSIX message queues (tt(msgqueue)).)
 sitem(tt(-r))(Maximum real time priority.  On some systems where this
 is not available, such as NetBSD, this has the same effect as tt(-T)
-for compatibility with tt(sh).)
-sitem(tt(-s))(Kilobytes on the size of the stack.)
-sitem(tt(-T))(The number of simultaneous threads available to the user.)
-sitem(tt(-t))(CPU seconds to be used.)
-sitem(tt(-u))(The number of processes available to the user.)
-sitem(tt(-v))(Kilobytes on the size of virtual memory.  On some systems this
-refers to the limit called `address space'.)
-sitem(tt(-w))(Kilobytes on the size of swapped out memory.)
-sitem(tt(-x))(The number of locks on files.)
+for compatibility with tt(sh) (tt(rt_priority) / tt(maxpthreads)).)
+sitem(tt(-R))(realtime CPU time slice (tt(rt_time)).)
+sitem(tt(-s))(Kibibytes on the size of the stack (tt(stacksize)).)
+sitem(tt(-T))(The number of simultaneous threads available to the user (tt(maxpthreads)).)
+sitem(tt(-t))(CPU seconds to be used (tt(cputime)).)
+sitem(tt(-u))(The number of processes available to the user (tt(maxproc)).)
+sitem(tt(-v))(Kibibytes on the size of virtual memory.  On some systems this
+refers to the limit called em(address space) (tt(vmemorysize) / tt(addressspace)).)
+sitem(tt(-w))(Kibibytes on the size of swapped out memory (tt(swapsize)).)
+sitem(tt(-x))(The number of locks on files (tt(maxfilelocks)).)
 endsitem()
 
 A resource may also be specified by integer in the form `tt(-N)
diff --git a/Doc/Zsh/expn.yo b/Doc/Zsh/expn.yo
index b3396721f..259c00a6c 100644
--- a/Doc/Zsh/expn.yo
+++ b/Doc/Zsh/expn.yo
@@ -2837,15 +2837,15 @@ exactly var(n) bytes in length.
 
 If this flag is directly followed by a em(size specifier) `tt(k)' (`tt(K)'),
 `tt(m)' (`tt(M)'), or `tt(p)' (`tt(P)') (e.g. `tt(Lk-50)') the check is
-performed with kilobytes, megabytes, or blocks (of 512 bytes) instead.
-(On some systems additional specifiers are available for gigabytes,
-`tt(g)' or `tt(G)', and terabytes, `tt(t)' or `tt(T)'.) If a size specifier
-is used a file is regarded as "exactly" the size if the file size rounded up
-to the next unit is equal to the test size.  Hence `tt(*LPAR()Lm1+RPAR())'
-matches files from 1 byte up to 1 Megabyte inclusive.  Note also that
-the set of files "less than" the test size only includes files that would
-not match the equality test; hence `tt(*LPAR()Lm-1+RPAR())' only matches
-files of zero size.
+performed with kibibytes, mebibytes, or blocks (of 512 bytes; note: em(not)
+pebibytes) instead. (On some systems additional specifiers are available
+for gibibytes, `tt(g)' or `tt(G)', and tebibytes, `tt(t)' or `tt(T)'.) If a
+size specifier is used, a file is regarded as "exactly" the size if the file
+size rounded up to the next unit is equal to the test size.  Hence
+`tt(*LPAR()Lm1+RPAR())' matches files from 1 byte up to 1 Mebibyte
+inclusive.  Note also that the set of files "less than" the test size only
+includes files that would not match the equality test; hence
+`tt(*LPAR()Lm-1+RPAR())' only matches files of zero size.
 )
 item(tt(^))(
 negates all qualifiers following it
diff --git a/Doc/Zsh/mod_zpty.yo b/Doc/Zsh/mod_zpty.yo
index 3ca031c01..ba59e1783 100644
--- a/Doc/Zsh/mod_zpty.yo
+++ b/Doc/Zsh/mod_zpty.yo
@@ -66,8 +66,8 @@ read matches the var(pattern), even in the non-blocking case.  The return
 status is zero if the string read matches the pattern, or if the command
 has exited but at least one character could still be read.  If the option
 tt(-m) is present, the return status is zero only if the pattern matches.
-As of this writing, a maximum of one megabyte of output can be consumed
-this way; if a full megabyte is read without matching the pattern, the
+As of this writing, a maximum of one mebibyte of output can be consumed
+this way; if a full mebibyte is read without matching the pattern, the
 return status is non-zero.
 
 In all cases, the return status is non-zero if nothing could be read, and
diff --git a/Doc/Zsh/params.yo b/Doc/Zsh/params.yo
index 36c1ae4c2..6c305da34 100644
--- a/Doc/Zsh/params.yo
+++ b/Doc/Zsh/params.yo
@@ -1473,7 +1473,7 @@ is specified with no command.  Defaults to tt(more).
 vindex(REPORTMEMORY)
 item(tt(REPORTMEMORY))(
 If nonnegative, commands whose maximum resident set size (roughly
-speaking, main memory usage) in kilobytes is greater than this
+speaking, main memory usage) in kibibytes is greater than this
 value have timing statistics reported.  The format used to output
 statistics is the value of the tt(TIMEFMT) parameter, which is the same
 as for the tt(REPORTTIME) variable and the tt(time) builtin; note that
@@ -1603,12 +1603,12 @@ sitem(tt(%E))(Elapsed time in seconds.)
 sitem(tt(%P))(The CPU percentage, computed as
 100*(tt(%U)PLUS()tt(%S))/tt(%E).)
 sitem(tt(%W))(Number of times the process was swapped.)
-sitem(tt(%X))(The average amount in (shared) text space used in kilobytes.)
+sitem(tt(%X))(The average amount in (shared) text space used in kibibytes.)
 sitem(tt(%D))(The average amount in (unshared) data/stack space used in
-kilobytes.)
-sitem(tt(%K))(The total space used (tt(%X)PLUS()tt(%D)) in kilobytes.)
+kibibytes.)
+sitem(tt(%K))(The total space used (tt(%X)PLUS()tt(%D)) in kibibytes.)
 sitem(tt(%M))(The  maximum memory the process had in use at any time in
-kilobytes.)
+kibibytes.)
 sitem(tt(%F))(The number of major page faults (page needed to be brought
 from disk).)
 sitem(tt(%R))(The number of minor page faults.)
diff --git a/Etc/BUGS b/Etc/BUGS
index 49e6a5c34..28e4f2b81 100644
--- a/Etc/BUGS
+++ b/Etc/BUGS
@@ -73,3 +73,7 @@ interactive and the subshell is the foreground job.  The USEZLE option is
 always turned off in subshells, for reasons lost to history.  There is a
 related, probably obsolete, vared special case for $TERM set to "emacs".
 ------------------------------------------------------------------------
+47634: missing rlim_t overflow checks in limit/ulimit limit arguments
+Integer values corresponding to the values of RLIM_INFINITY, RLIM_SAVED_MAX,
+RLIM_SAVED_CUR should probably be rejected as well (47639)
+------------------------------------------------------------------------
diff --git a/NEWS b/NEWS
index d05e8b64f..9d218153b 100644
--- a/NEWS
+++ b/NEWS
@@ -11,7 +11,12 @@ The zsh/system module's `zsystem flock` command learnt an -i option to
 set the wait interval used with -t. Additionally, -t now supports
 fractional seconds.
 
+The `limit` builtin accepts more units for resource limits, now shared
+with the `ulimit` builtin allowing limit values to be specified.
+`limit -s somelimit` now reports the shell process' own value of the limit.
 The `limit` and `unlimit` builtins are now available again in csh emulation.
+A few more improvements to the resource limit handling interfaces and
+documentation have been made.
 
 Changes from 5.7.1-test-3 to 5.8
 --------------------------------
diff --git a/Src/Builtins/rlimits.c b/Src/Builtins/rlimits.c
index 5f9c84b0f..5fec90e63 100644
--- a/Src/Builtins/rlimits.c
+++ b/Src/Builtins/rlimits.c
@@ -55,7 +55,8 @@ typedef struct resinfo_T {
  * 1. Add zsh_LIMIT_PRESENT(RLIMIT_XXX) in configure.ac.
  * 2. Add an entry for RLIMIT_XXX to known_resources[].
  *    Make sure the option letter (resinto_T.opt) is unique.
- * 3. Build zsh and run the test B12rlimit.ztst.
+ * 3. Add entry in documentation for the limit and ulimit builtins
+ * 4. Build zsh and run the test B12rlimit.ztst.
  */
 static const resinfo_T known_resources[] = {
     {RLIMIT_CPU, "cputime", ZLIMTYPE_TIME, 1,
@@ -109,12 +110,23 @@ static const resinfo_T known_resources[] = {
 		'i', "pending signals"},
 # endif
 # ifdef HAVE_RLIMIT_MSGQUEUE
-    {RLIMIT_MSGQUEUE, "msgqueue", ZLIMTYPE_NUMBER, 1,
+    {RLIMIT_MSGQUEUE, "msgqueue", ZLIMTYPE_MEMORY, 1,
 		'q', "bytes in POSIX msg queues"},
 # endif
 # ifdef HAVE_RLIMIT_NICE
+    /*
+     * Not max niceness. On Linux ranges [1..40] for that RLIM translates
+     * to [-19..20] meaning that for instance setting it to 20 means
+     * processes can lower their niceness as far down as -1 and with the
+     * default of 0, unprivileged processes can't lower their niceness.
+     *
+     * Increasing niceness is always possible.
+     *
+     * "max nice priority" is the wording used in /proc/self/limits on
+     * Linux.
+     */
     {RLIMIT_NICE, "nice", ZLIMTYPE_NUMBER, 1,
-		'e', "max nice"},
+		'e', "max nice priority"},
 # endif
 # ifdef HAVE_RLIMIT_RTPRIO
     {RLIMIT_RTPRIO, "rt_priority", ZLIMTYPE_NUMBER, 1,
@@ -122,7 +134,7 @@ static const resinfo_T known_resources[] = {
 # endif
 # ifdef HAVE_RLIMIT_RTTIME
     {RLIMIT_RTTIME, "rt_time", ZLIMTYPE_MICROSECONDS, 1,
-		'N', "rt cpu time (microseconds)"},
+		'R', "rt cpu time slice (microseconds)"},
 # endif
     /* BSD */
 # ifdef HAVE_RLIMIT_SBSIZE
@@ -192,9 +204,9 @@ set_resinfo(void)
 	if (!resinfo[i]) {
 	    /* unknown resource */
 	    resinfo_T *info = (resinfo_T *)zshcalloc(sizeof(resinfo_T));
-	    char *buf = (char *)zalloc(12);
+	    char *buf = (char *)zalloc(8 /* UNKNOWN- */ + 3 /* digits */ + 1 /* '\0' */);
 	    snprintf(buf, 12, "UNKNOWN-%d", i);
-	    info->res = - 1;	/* negative value indicates "unknown" */
+	    info->res = -1;	/* negative value indicates "unknown" */
 	    info->name = buf;
 	    info->type = ZLIMTYPE_UNKNOWN;
 	    info->unit = 1;
@@ -255,38 +267,214 @@ printrlim(rlim_t val, const char *unit)
 # endif /* RLIM_T_IS_QUAD_T */
 }
 
+/*
+ * Parse a string into the corresponding value based on the limit type
+ *
+ *   ZLIMTYPE_UNKNOWN / ZLIMTYPE_NUMBER:
+ *     decimal integer without sign only: raw value
+ *
+ *   ZLIMTYPE_TIME / ZLIMTYPE_MICROSECONDS:
+ *     <decimal> only gives seconds or microseconds depending on type
+ *     or:
+ *     [[hour:]min:]sec[.usec]
+ *     or:
+ *     <decimal> with h (hour), m (min), s (sec), ms, us suffix.
+ *
+ *   ZLIMTYPE_MEMORY:
+ *     <decimal> without suffix interpreted as KiB by "limit" (except for
+ *     RLIMIT_MSGQUEUE, see below) and based on resinfo.unit by "ulimit".
+ *
+ *     K/M/G/T/P/E suffix and same with iB suffix use 1024 factor
+ *     KB/MB/GB... use 1000 factor.
+ *
+ *     B for bytes (avoids that mess about default units).
+ *
+ * All suffixes are case insensitive.
+ *
+ * Arguments:
+ *   - s: string to parse
+ *   - lim: resource being limited (from which will derive the type and unit)
+ *   - ulimit: to specify whether we're being called by ulimit or not.
+ *             For ulimit, suffix-less limits are multiplied by the limit's
+ *             unit.
+ *   - err: (return value) error to be returned if any. If a non-NULL value is
+ *           stored there, zstrtorlimt failed and the return value is
+ *           irrelevant (though will be 0).
+ *
+ */
+
 /**/
 static rlim_t
-zstrtorlimt(const char *s, char **t, int base)
+zstrtorlimt(const char *s, int lim, int ulimit, char **err)
 {
     rlim_t ret = 0;
+    const char *orig = s;
+    enum zlimtype type = resinfo[lim]->type;
+    *err = NULL;
 
-    if (strcmp(s, "unlimited") == 0) {
-	if (t)
-	    *t = (char *) s + 9;
+    if (strcmp(s, "unlimited") == 0)
 	return RLIM_INFINITY;
+
+    for (; *s >= '0' && *s <= '9'; s++)
+	ret = ret * 10 + *s - '0';
+
+    if (s == orig) {
+	*err = "decimal integer expected";
+	return 0;
+    }
+
+    if (lim >= RLIM_NLIMITS ||
+	type == ZLIMTYPE_NUMBER ||
+	type == ZLIMTYPE_UNKNOWN) {
+	/*
+	 * pure numeric resource -- only a straight decimal number is
+	 * permitted.
+	 */
+	if (*s) {
+	    *err = "limit must be a decimal integer";
+	    return 0;
+	}
+    }
+    else if (type == ZLIMTYPE_TIME ||
+	     type == ZLIMTYPE_MICROSECONDS) {
+	if (*s) {
+	    int divisor = 1;
+	    int factor = 1;
+	    switch (*s++) {
+
+	    case 'm': /* m for minute or ms for milisecond */
+	    case 'M':
+		if (*s == 's' || *s == 'S') {
+		    s++;
+		    divisor = 1000;
+		}
+		else {
+		    factor = 60;
+		}
+		break;
+
+	    case 'h':
+	    case 'H':
+		factor = 3600;
+		break;
+
+	    case 's':
+	    case 'S':
+		break;
+
+	    case 'u':
+	    case 'U':
+		divisor = 1000000;
+		if (*s == 's' || *s == 'S')
+		    s++;
+		break;
+
+	    case ':':
+		do {
+		    int more = 0;
+		    while (*s >= '0' && *s <= '9') {
+			more = more * 10 + (*s - '0');
+			s++;
+		    }
+		    ret = ret * 60 + more;
+		} while (*s == ':' && ++s);
+		if (*s == '.')
+		    s++;
+		    /* fallthrough */
+		else
+		    break;
+	    case '.':
+		if (type == ZLIMTYPE_MICROSECONDS) {
+		    int frac;
+		    for (frac = 0; *s >= '0' && *s <= '9'; s++) {
+			if (divisor < 1000000) {
+			    /* ignore digits past the 6th */
+			    divisor *= 10;
+			    frac = frac * 10 + (*s - '0');
+			}
+		    }
+		    ret = ret * divisor + frac;
+		}
+		else {
+		    /* fractional part ignored */
+		    while (*s >= '0' && *s <= '9')
+			s++;
+		}
+		break;
+	    default:
+		*err = "invalid time specification";
+		return 0;
+	    }
+
+	    if (*s) {
+		*err = "invalid time specification";
+		return 0;
+	    }
+
+	    ret *= factor;
+	    if (type == ZLIMTYPE_MICROSECONDS)
+		ret *= 1000000 / divisor;
+	    else
+		ret /= divisor;
+	}
+    }
+    else {
+	/*
+	 * memory-type resource
+	 */
+	if (*s) {
+	    if (*s == 'b' || *s == 'B')
+		s++;
+	    else {
+		const char *suffix = "kKmMgGtTpPeE";
+		char *offset;
+
+		if ((offset = strchr(suffix, *s))) {
+		    s++;
+		    if (*s == 'b' || *s == 'B') {
+			/* KB == 1000 */
+			const char *p;
+			for (p = suffix; p <= offset; p += 2)
+			    ret *= 1000;
+			s++;
+		    }
+		    else {
+			/* K/KiB == 1024 */
+			if ((s[0] == 'i' || s[0] == 'I') &&
+			    (s[1] == 'b' || s[1] == 'B'))
+			    s += 2;
+			ret <<= ((offset - suffix) / 2 + 1) * 10;
+		    }
+		}
+	    }
+	    if (*s) {
+		*err = "invalid unit";
+		return 0;
+	    }
+	}
+	else {
+	    if (ulimit)
+		ret *= resinfo[lim]->unit;
+	    else
+#ifdef HAVE_RLIMIT_MSGQUEUE
+		if (lim != RLIMIT_MSGQUEUE)
+		    /*
+		     * Historical quirk. In tcsh's limit (and bash's and mksh's
+		     * ulimit, but not ksh93), that limit expects bytes instead
+		     * of kibibytes and earlier versions of zsh were treating
+		     * it as a ZLIMTYPE_NUMBER.
+		     *
+		     * We still want to treat it as ZLIMTYPE_MEMORY and accept
+		     * KMG... suffixes as it is a number of bytes.
+		     *
+		     * But we carry on taking the value as a number of *bytes*
+		     * in the "limit" builtin for backward compatibility and
+		     * compatibility with tcsh.
+		     */
+#endif
+		    ret *= 1024;
+	}
     }
-# if defined(RLIM_T_IS_QUAD_T) || defined(RLIM_T_IS_LONG_LONG) || defined(RLIM_T_IS_UNSIGNED)
-    if (!base) {
-	if (*s != '0')
-	    base = 10;
-	else if (*++s == 'x' || *s == 'X')
-	    base = 16, s++;
-	else
-	    base = 8;
-    } 
-    if (base <= 10)
-	for (; *s >= '0' && *s < ('0' + base); s++)
-	    ret = ret * base + *s - '0';
-    else
-	for (; idigit(*s) || (*s >= 'a' && *s < ('a' + base - 10))
-	     || (*s >= 'A' && *s < ('A' + base - 10)); s++)
-	    ret = ret * base + (idigit(*s) ? (*s - '0') : (*s & 0x1f) + 9);
-    if (t)
-	*t = (char *)s;
-# else /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
-    ret = zstrtol(s, t, base);
-# endif /* !RLIM_T_IS_QUAD_T && !RLIM_T_IS_LONG_LONG && !RLIM_T_IS_UNSIGNED */
     return ret;
 }
 
@@ -317,25 +505,42 @@ showlimitvalue(int lim, rlim_t val)
 	       resinfo[lim]->type == ZLIMTYPE_UNKNOWN)
 	printrlim(val, "\n");	/* pure numeric resource */
     else {
-	/* memory resource -- display with `k' or `M' modifier */
-	if (val >= 1024L * 1024L)
-	    printrlim(val/(1024L * 1024L), "MB\n");
+	/*
+	 * memory resource -- display with KiB/MiB... for exact
+	 * multiples of those units
+	 */
+	const char *units = "KMGTPE";
+	rlim_t v = val;
+	int n = 0;
+	while (units[n] && (v & 1023) == 0 && v >> 10) {
+	    n++;
+	    v >>= 10;
+	}
+	if (n) {
+	    char suffix[] = "XiB\n";
+	    *suffix = units[n-1];
+	    printrlim(v, suffix);
+	}
 	else
-	    printrlim(val/1024L, "kB\n");
+	    printrlim(val, "B\n");
     }
 }
 
-/* Display resource limits.  hard indicates whether `hard' or `soft'  *
- * limits should be displayed.  lim specifies the limit, or may be -1 *
- * to show all.                                                       */
+/*
+ * Display resource limits.  hard indicates whether `hard' or `soft'
+ * limits should be displayed.  lim specifies the limit, or may be -1
+ * to show all.  If `shell' is non-zero, the limits in place for the
+ * shell process retrieved with getrlimits() are shown.
+ */
 
 /**/
 static int
-showlimits(char *nam, int hard, int lim)
+showlimits(char *nam, int hard, int lim, int shell)
 {
     int rt;
+    int ret = 0;
 
-    if (lim >= RLIM_NLIMITS)
+    if (shell || lim >= RLIM_NLIMITS)
     {
 	/*
 	 * Not configured into the shell.  Ask the OS
@@ -357,17 +562,40 @@ showlimits(char *nam, int hard, int lim)
     else
     {
 	/* main loop over resource types */
-	for (rt = 0; rt != RLIM_NLIMITS; rt++)
-	    showlimitvalue(rt, (hard) ? limits[rt].rlim_max :
-			   limits[rt].rlim_cur);
+	for (rt = 0; rt != RLIM_NLIMITS; rt++) {
+	    struct rlimit vals, *plim;
+	    if (shell) {
+		/*
+		 * FIXME: this is dead code as at the moment, there is no API
+		 * for the user can request all limits *of the shell* be
+		 * displayed.
+		 *
+		 * Should we add a "limit -s -a" or "limit -s all"?
+		 */
+		plim = &vals;
+		if (getrlimit(rt, plim) < 0)
+		{
+		    zwarnnam(nam, "can't read \"%s\" limit: %e", resinfo[rt]->name, errno);
+		    ret = 1;
+		    continue;
+		}
+	    }
+	    else
+		plim = &(limits[rt]);
+
+	    showlimitvalue(rt, (hard) ? plim->rlim_max :
+			   plim->rlim_cur);
+	}
     }
 
-    return 0;
+    return ret;
 }
 
-/* Display a resource limit, in ulimit style.  lim specifies which   *
- * limit should be displayed, and hard indicates whether the hard or *
- * soft limit should be displayed.                                   */
+/*
+ * Display a resource limit, in ulimit style.  lim specifies which
+ * limit should be displayed, and hard indicates whether the hard or
+ * soft limit should be displayed.
+ */
 
 /**/
 static int
@@ -394,12 +622,12 @@ printulimit(char *nam, int lim, int hard, int head)
 	if (lim < RLIM_NLIMITS) {
 	    const resinfo_T *info = resinfo[lim];
 	    if (info->opt == 'N')
-		printf("-N %2d: %-29s", lim, info->descr);
+		printf("-N %2d: %-32s ", lim, info->descr);
 	    else
-		printf("-%c: %-32s", info->opt, info->descr);
+		printf("-%c: %-35s ", info->opt, info->descr);
 	}
 	else
-	    printf("-N %2d: %-29s", lim, "");
+	    printf("-N %2d: %-32s ", lim, "");
     }
     /* display the limit */
     if (limit == RLIM_INFINITY)
@@ -511,16 +739,18 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
     rlim_t val;
     int ret = 0;
 
-    hard = OPT_ISSET(ops,'h');
-    if (OPT_ISSET(ops,'s') && !*argv)
+    hard = OPT_ISSET(ops, 'h');
+    if (OPT_ISSET(ops, 's') && !*argv)
 	return setlimits(NULL);
     /* without arguments, display limits */
     if (!*argv)
-	return showlimits(nam, hard, -1);
+	return showlimits(nam, hard, -1, OPT_ISSET(ops, 's'));
     while ((s = *argv++)) {
 	/* Search for the appropriate resource name.  When a name matches (i.e. *
 	 * starts with) the argument, the lim variable changes from -1 to the   *
 	 * number of the resource.  If another match is found, lim goes to -2.  */
+	char *err;
+
 	if (idigit(*s))
 	{
 	    lim = (int)zstrtol(s, NULL, 10);
@@ -543,61 +773,12 @@ bin_limit(char *nam, char **argv, Options ops, UNUSED(int func))
 	}
 	/* without value for limit, display the current limit */
 	if (!(s = *argv++))
-	    return showlimits(nam, hard, lim);
-	if (lim >= RLIM_NLIMITS)
-	{
-	    val = zstrtorlimt(s, &s, 10);
-	    if (*s)
-	    {
-		/* unknown limit, no idea how to scale */
-		zwarnnam(nam, "unknown scaling factor: %s", s);
-		return 1;
-	    }
-	}
-	else if (resinfo[lim]->type == ZLIMTYPE_TIME) {
-	    /* time-type resource -- may be specified as seconds, or minutes or *
-	     * hours with the `m' and `h' modifiers, and `:' may be used to add *
-	     * together more than one of these.  It's easier to understand from *
-	     * the code:                                                        */
-	    val = zstrtorlimt(s, &s, 10);
-	    if (*s) {
-		if ((*s == 'h' || *s == 'H') && !s[1])
-		    val *= 3600L;
-		else if ((*s == 'm' || *s == 'M') && !s[1])
-		    val *= 60L;
-		else if (*s == ':')
-		    val = val * 60 + zstrtorlimt(s + 1, &s, 10);
-		else {
-		    zwarnnam(nam, "unknown scaling factor: %s", s);
-		    return 1;
-		}
-	    }
-	} else if (resinfo[lim]->type == ZLIMTYPE_NUMBER ||
-		   resinfo[lim]->type == ZLIMTYPE_UNKNOWN ||
-		   resinfo[lim]->type == ZLIMTYPE_MICROSECONDS) {
-	    /* pure numeric resource -- only a straight decimal number is
-	    permitted. */
-	    char *t = s;
-	    val = zstrtorlimt(t, &s, 10);
-	    if (s == t) {
-		zwarnnam(nam, "limit must be a number");
-		return 1;
-	    }
-	} else {
-	    /* memory-type resource -- `k', `M' and `G' modifiers are *
-	     * permitted, meaning (respectively) 2^10, 2^20 and 2^30. */
-	    val = zstrtorlimt(s, &s, 10);
-	    if (!*s || ((*s == 'k' || *s == 'K') && !s[1])) {
-		if (val != RLIM_INFINITY)
-		    val *= 1024L;
-	    } else if ((*s == 'M' || *s == 'm') && !s[1])
-		val *= 1024L * 1024;
-	    else if ((*s == 'G' || *s == 'g') && !s[1])
-		val *= 1024L * 1024 * 1024;
-	    else {
-		zwarnnam(nam, "unknown scaling factor: %s", s);
-		return 1;
-	    }
+	    return showlimits(nam, hard, lim, OPT_ISSET(ops, 's'));
+
+	val = zstrtorlimt(s, lim, 0, &err);
+	if (err) {
+	    zwarnnam(nam, "%s: %s", s, err);
+	    return 1;
 	}
 	if (do_limit(nam, lim, val, hard, !hard, OPT_ISSET(ops, 's')))
 	    ret++;
@@ -821,14 +1002,12 @@ bin_ulimit(char *name, char **argv, UNUSED(Options ops), UNUSED(int func))
 		    limit = vals.rlim_max;
 		}
 	    } else {
-		limit = zstrtorlimt(*argv, &eptr, 10);
-		if (*eptr) {
-		    zwarnnam(name, "invalid number: %s", *argv);
+		char *err;
+		limit = zstrtorlimt(*argv, res, 1, &err);
+		if (err) {
+		    zwarnnam(name, "%s: %s", *argv, err);
 		    return 1;
 		}
-		/* scale appropriately */
-		if (res < RLIM_NLIMITS)
-		    limit *= resinfo[res]->unit;
 	    }
 	    if (do_limit(name, res, limit, hard, soft, 1))
 		ret++;
diff --git a/Test/B12limit.ztst b/Test/B12limit.ztst
index 48d33e6e3..c281c523a 100644
--- a/Test/B12limit.ztst
+++ b/Test/B12limit.ztst
@@ -26,3 +26,160 @@ F:report this to zsh-workers mailing list.
   }
 0:check if limit option letters are unique
 
+  if sh -c 'ulimit -f 2048' > /dev/null 2>&1; then
+    (
+      set -o braceccl -o pipefail
+      list=(1b 1{kmgtpe}{,b,ib})
+      for cmd in "limit filesize" ulimit; do
+	for l in $list $list:u; do
+	  $=cmd $l &&
+	    limit filesize &&
+	    ulimit || exit
+	done
+      done | sed 'N;s/\n/ /;s/  */ /g'
+    )
+  else
+    ZTST_skip='Cannot set the filesize limit on this system'
+  fi
+0:filesize suffixes with limit and ulimit
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+>filesize 1B 0
+>filesize 1EiB 2251799813685248
+>filesize 976562500000000KiB 1953125000000000
+>filesize 1EiB 2251799813685248
+>filesize 1GiB 2097152
+>filesize 1000000000B 1953125
+>filesize 1GiB 2097152
+>filesize 1KiB 2
+>filesize 1000B 1
+>filesize 1KiB 2
+>filesize 1MiB 2048
+>filesize 1000000B 1953
+>filesize 1MiB 2048
+>filesize 1PiB 2199023255552
+>filesize 976562500000KiB 1953125000000
+>filesize 1PiB 2199023255552
+>filesize 1TiB 2147483648
+>filesize 976562500KiB 1953125000
+>filesize 1TiB 2147483648
+
+  if sh -c 'ulimit -t 3600' > /dev/null 2>&1; then
+  (
+    set -o pipefail
+    list=(1h 30m 20s 30 1:23:45.123456 2:23 56.4)
+    for cmd in "limit cputime" "ulimit -t"; do
+      for l in $list ${(MU)list:#*[a-z]*}; do
+        $=cmd $l &&
+	  limit cputime &&
+	  ulimit -t || exit
+      done
+    done | sed 'N;s/\n/ /;s/  */ /g'
+  )
+  else
+    ZTST_skip='Cannot set the cputime limit on this system'
+  fi
+0:time limit formats
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
+>cputime 0:00:30 30
+>cputime 1:23:45 5025
+>cputime 0:02:23 143
+>cputime 0:00:56 56
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
+>cputime 0:00:30 30
+>cputime 1:23:45 5025
+>cputime 0:02:23 143
+>cputime 0:00:56 56
+>cputime 1:00:00 3600
+>cputime 0:30:00 1800
+>cputime 0:00:20 20
+
+  ulimit 1Kite
+  ulimit 1D
+  ulimit 1s
+  ulimit 1MBA
+  limit cputime 1k
+  limit cputime 1:0s
+  limit cputime 1ss
+  limit cputime 1msx
+  limit cputime 1.0s
+  limit cputime .1
+  limit descriptors 1k
+  limit descriptors 1h
+  limit descriptors 1:0
+1:invalid limit input
+?(eval):ulimit:1: 1Kite: invalid unit
+?(eval):ulimit:2: 1D: invalid unit
+?(eval):ulimit:3: 1s: invalid unit
+?(eval):ulimit:4: 1MBA: invalid unit
+?(eval):limit:5: 1k: invalid time specification
+?(eval):limit:6: 1:0s: invalid time specification
+?(eval):limit:7: 1ss: invalid time specification
+?(eval):limit:8: 1msx: invalid time specification
+?(eval):limit:9: 1.0s: invalid time specification
+?(eval):limit:10: .1: decimal integer expected
+?(eval):limit:11: 1k: limit must be a decimal integer
+?(eval):limit:12: 1h: limit must be a decimal integer
+?(eval):limit:13: 1:0: limit must be a decimal integer
-- 
2.25.1





^ permalink raw reply	[relevance 4%]

* Re: More rabbit-holes with unset variables
  @ 2020-11-27 20:54  3%                     ` Bart Schaefer
  0 siblings, 0 replies; 200+ results
From: Bart Schaefer @ 2020-11-27 20:54 UTC (permalink / raw)
  To: Felipe Contreras; +Cc: Zsh hackers list

On Thu, Nov 26, 2020 at 5:30 PM Felipe Contreras
<felipe.contreras@gmail.com> wrote:
>
> What zsh refers to as "scalar" internally is a string:
>
>   char *str; /* value if declared string  (PM_SCALAR)  */
>
> From Src/zsh.h (struct param).

This is exactly the discussion I was trying to avoid when I said "in
the abstract that doesn't matter".

You can't just pull one field out of a union inside a struct and
ignore the struct itself and the API for field access that goes with
it.

> So if you didn't mean string, what did you mean?

I meant a struct param, containing the least specific thing so
represented, as interpreted through all the layers of code that
implement a dereference of its value when you write $var or any of its
variations.  Again this doesn't actually matter, which is why I didn't
spell it out.

> And what did you mean by 'so a the only useful "declared but not set"
> variable is a simple scalar'?

As the very first message in this thread demonstrated, in both bash
and ksh (call this "example one", and to be pedantic assume that X is
not inheriting its name or value from somewhere):

typeset -i X
echo ${X-nil}
X="garbage"
echo ${X-nil}

will output

nil
0

However (call this "example two"):

typeset -i X
unset X
X="garbage"
echo ${X-nil}

outputs

garbage

The language you quoted from the posix proposal says "otherwise, the
variable is initially unset".  Given that proposed language, example
one is incorrect, because an "unset" variable should not retain its
(in this example) integer properties when assigned a string.

> What simple scalar other than a string is useful "declared but not set"?

Under this interpretation, there isn't any.  That's what I said.  In
fact the last paragraph of the very first message in this thread:

"Therefore, this isn't as simple as having zsh create an unset
variable when typeset is given no assignment, because subsequent
assignment has to preserve the type of the variable, which normally
does not apply after unset."



^ permalink raw reply	[relevance 3%]

Results 1401-1600 of ~2400   |  | reverse | sort options + mbox downloads above
-- links below jump to the message on this page --
2017-12-07 11:29     $userdirs empty in non-interactive shells Stephane Chazelas
2018-06-17 14:14  0% ` Stephane Chazelas
2017-12-23 22:19  3% Why sourcing a file is not faster than doing a loop with eval, zle -N Joey Pabalinas
2017-12-24  9:43  1% Room for optimization, source "..." vs. eval "$(<...)" Sebastian Gniazdowski
2017-12-26 10:29  5% [PATH] _tar: a few updates for long options Jun T
2018-01-09 16:22     [BUG] getopts OPTIND Francisco de Zuviría Allende
2018-01-09 22:48     ` dana
2018-01-09 22:57       ` Bart Schaefer
2018-01-09 23:58  3%     ` dana
2018-01-10  1:32  2%       ` Francisco de Zuviría Allende
2018-01-22 13:57  2% PATCH: update options in assorted completions Oliver Kiddle
2018-01-27 19:06  3% [patch] Update _ln for [DFNO]BSD and Darwin Matthew Martin
2018-02-03 17:39  5% '<<-' here-documents oddity with line continuation Martijn Dekker
2018-02-09  7:01     ` Martijn Dekker
2018-02-09  7:58  3%   ` Martijn Dekker
2018-02-07 22:30  2% inf and nan in arithmetic expansions Stephane Chazelas
     [not found]     <CGME20180222072350epcas2p3185ca17f5f0e3ad69b0a41dbf743f145@epcas2p3.samsung.com>
2018-02-22  7:23     ` [BUG]builtin echo error doing arguments parsing wumingxwk
2018-02-22  9:37       ` Peter Stephenson
2018-02-22 10:32  7%     ` Peter Stephenson
2018-02-22 16:50           ` Mikael Magnusson
2018-02-22 17:26             ` Peter Stephenson
2018-02-22 19:00               ` Mikael Magnusson
2018-02-22 19:34                 ` Peter Stephenson
2018-02-24  8:20  5%               ` Stephane Chazelas
2019-04-27  6:46  5%                 ` Stephane Chazelas
2018-03-11  9:44     [doc] "sh_word_split nothing to do with word splitting"? Stephane Chazelas
2018-03-11 18:24     ` Bart Schaefer
2018-03-11 20:53  5%   ` Stephane Chazelas
2018-03-11 23:41  5%     ` Martijn Dekker
2018-03-12  7:43  4%       ` Stephane Chazelas
2018-03-12  8:07  5%         ` Stephane Chazelas
2018-03-16 17:26  8%           ` Stephane Chazelas
2018-03-16 18:28                 ` Bart Schaefer
2018-03-16 19:33  5%               ` Stephane Chazelas
2018-03-24 20:17  5%         ` Martijn Dekker
2018-03-25  6:42  5%           ` Stephane Chazelas
2018-03-26 18:11  5%             ` Martijn Dekker
2018-03-14  2:40     zsh/bash behavior variance: regex ERE matching Phil Pennock
2018-03-14 14:37  5% ` Stephane Chazelas
2018-03-24  5:19     "echo | ps -j $(:) | cat | cat | cat" runs components in different process groups Bart Schaefer
     [not found]     ` <CGME20180324221021epcas1p184507a6328dbd505b97db69c1f9d8194@epcas1p1.samsung.com>
2018-03-24 22:09       ` Bart Schaefer
2018-04-10 11:45         ` Peter Stephenson
2018-04-10 13:59           ` Peter Stephenson
2018-04-11 22:10             ` Bart Schaefer
2018-04-12 16:23               ` Peter Stephenson
2018-04-15 16:23                 ` Stephane Chazelas
2018-04-15 17:38                   ` Bart Schaefer
2018-04-15 18:58                     ` Stephane Chazelas
2018-04-17  5:39                       ` Bart Schaefer
2018-04-17  9:19                         ` Peter Stephenson
2018-04-17 16:09                           ` Bart Schaefer
2018-04-17 16:35                             ` Peter Stephenson
2018-04-17 17:52                               ` Bart Schaefer
2018-04-19  9:40                                 ` Peter Stephenson
     [not found]                                   ` <F3A62E38-24E2-4A62-8E19-F54C9B81E9E5@kba.biglobe.ne.jp>
2018-04-23 13:52                                     ` Peter Stephenson
2018-04-23 14:03  3%                                   ` Peter Stephenson
     [not found]                                         ` <CGME20180423140859eucas1p2591bf1422614209979d4890383268c37@eucas1p2.samsung.com>
2018-04-23 14:08  0%                                       ` Peter Stephenson
     [not found]     <CGME20180325222346epcas1p2296ebb2ee08e21c7438b32eabf1fa57a@epcas1p2.samsung.com>
2018-03-25 22:23  3% ` [PATCH] move zsh reserved words out of the way when invoked in sh/ksh emulation Martijn Dekker
2018-04-16  8:32  3%   ` Peter Stephenson
2018-04-16 21:26  3%     ` Bart Schaefer
2018-03-27 13:34     _sh doing _normal completion Leah Neukirchen
2018-03-27 19:33     ` Peter Stephenson
2018-03-28 13:46  3%   ` Leah Neukirchen
2018-03-28 13:59  3%     ` Stephane Chazelas
2018-04-16 14:28  1% PATCH: completion option updates Oliver Kiddle
2018-04-17 22:08  4% PATCH: handle system specific arguments in _dd Oliver Kiddle
2018-04-18 19:58  4% [PATCH] posix_builtins: allow exporting a reaonly Martijn Dekker
2018-04-18 20:07  0% ` Bart Schaefer
2018-04-21 12:57  0%   ` Martijn Dekker
2019-06-20 18:47  0% ` [PATCH] posix_builtins: allow exporting a readonly Martijn Dekker
2019-06-21  8:59  0%   ` Peter Stephenson
2019-06-22 11:54  0%     ` Martijn Dekker
2018-04-28  4:30 10% [patch] update _df Matthew Martin
2018-05-13 21:25     [PATCH] [[:blank:]] only matches on SPC and TAB Stephane Chazelas
2018-05-14  2:27     ` Sebastian Gniazdowski
2018-05-14  6:36  3%   ` Stephane Chazelas
2018-05-14  6:44  5%     ` Stephane Chazelas
2018-05-14  8:47  0%       ` Peter Stephenson
2018-05-14 12:34  4%         ` Stephane Chazelas
2018-05-14 13:50               ` Peter Stephenson
2018-05-14 15:51  4%             ` Stephane Chazelas
2018-05-14 16:31  0%               ` Sebastian Gniazdowski
2018-05-14 16:50  3%                 ` Bart Schaefer
2018-05-14 19:52  3%                   ` Daniel Tameling
2018-05-14 20:42  5%                     ` Stephane Chazelas
2018-05-15 18:12  5%                       ` Stephane Chazelas
2018-05-15 19:06  4%               ` Oliver Kiddle
2018-05-16 13:15  4%                 ` Stephane Chazelas
2018-05-16 13:40  0%                   ` Peter Stephenson
2018-05-16 16:31                         ` Stephane Chazelas
2018-05-16 21:02  4%                       ` [PATCH v4] " Stephane Chazelas
2018-05-14  8:11  0%     ` [PATCH] " Sebastian Gniazdowski
2018-05-27  2:51  3% [PATCH 1/2] autoconf: run autoupdate Eitan Adler
2018-06-09  5:15  5% [PATCH] Completion: Add _bash, update _sh dana
2018-06-10  6:06  5% [PATCH] Completion: Improve _man dana
2018-06-10 13:07     ` Oliver Kiddle
2018-06-10 14:02       ` dana
2018-06-11 10:48         ` [PATCH] Completion: Improve _man (2) dana
2018-06-14  9:50           ` Daniel Shahaf
2018-06-14 10:20             ` dana
2018-06-15 13:59               ` [PATCH] Completion: Improve _man (3) dana
2018-06-15 14:05                 ` Daniel Shahaf
2018-06-15 14:21                   ` dana
2018-06-15 14:39                     ` Mikael Magnusson
2018-06-15 14:55                       ` Daniel Shahaf
2018-06-15 15:14  2%                     ` dana
2018-06-17 21:49  4% [PATCH] Completion: Minor improvements to _comm and _sed dana
2018-06-20 13:06  8% ` Oliver Kiddle
2018-06-19 14:18     zsh/stat: output atime/mtime/ctime with nanoseconds Vincent Lefevre
2018-06-19 19:17     ` Oliver Kiddle
2018-06-20  5:12  5%   ` dana
2018-07-15 12:16  9% POSIX: reserved words should not expand into aliases Ravi (Tom) Hale
2018-07-15 19:04  5% ` dana
     [not found]     <CGME20180716085234eucas1p21c8563e648c24a6e714cc3d24862560a@eucas1p2.samsung.com>
2018-07-16  8:52  5% ` PATCH: document compatibility more clearly Peter Stephenson
2018-09-24 21:05     [PATCH] typeset -p doesn't report -U (unique) attribute Stephane Chazelas
2018-10-07 13:35  5% ` [PATCH] [long] typeset doesn't report tied parameters (and related issues) Stephane Chazelas
2018-10-08  9:05       ` Peter Stephenson
     [not found]         ` <CACeGjnUhNqca7jLAR0KKSxobzDd+xXdAe4BeUeRpxu2CTp_zkA@mail.gmail.com>
2018-10-08 14:10           ` Peter Stephenson
2018-10-08 14:28             ` Daniel Shahaf
2018-10-08 15:24               ` Peter Stephenson
2018-10-08 15:54  3%             ` Bart Schaefer
2018-10-09 10:59  3%   ` local read-only variables (Was:[PATCH] [long] typeset doesn't report tied parameters (and related issues)) Stephane Chazelas
     [not found]     <CGME20180924080113epcas4p4f8f89aa03a2cebc5030fd45dca0f6e84@epcas4p4.samsung.com>
2018-09-24  8:00     ` BUG: Shell builtin `which` prints non-existent commands to stdout Klaus Alexander Seistrup
2018-09-24 10:22       ` Peter Stephenson
2018-09-24 12:29         ` Klaus Alexander Seistrup
2018-09-24 12:51           ` Peter Stephenson
2018-09-24 22:32  3%         ` Stephane Chazelas
2018-10-11  1:33  3% [RFC] Teach getopts to handle -o and +o separately dana
2018-10-11 20:26  0% ` Daniel Shahaf
2018-10-16 23:00  4% PATCH: git 2.19 completion update Oliver Kiddle
2018-10-21 22:22  5% [PATCH] Completion: Change plural descriptions to singular dana
2018-11-07 22:19     [PATCH] Add nanosecond support to strftime (zsh/datetime) dana
2018-11-07 22:26  3% ` Bart Schaefer
2018-11-07 22:40  3%   ` dana
2018-12-08 20:08  3% EXIT trap not executed on error Martijn Dekker
2018-12-15 20:26  3% [PATCH] zshmisc(1): document implicit append of `term` when `in word` is omitted Joey Pabalinas
     [not found]     ` <1544925190.621790.1610325216.065C6F48@webmail.messagingengine.com>
2018-12-16  2:01  0%   ` Joey Pabalinas
2018-12-16 11:54  0%     ` Daniel Shahaf
     [not found]     <d7b0451f90bdfe61f48cc1361690180e07158900.camel@ntlworld.com>
     [not found]     ` <b8851c3a50bd8bceba1961f2f764e1a6869481ac.camel@ntlworld.com>
2018-12-20 22:25  5%   ` The big kre zsh bug report Martijn Dekker
2018-12-21  7:53  3%     ` Bart Schaefer
2018-12-25 20:44  8%       ` 'wait' exit status and warnings [was: The big kre zsh bug report] Martijn Dekker
2018-12-30 18:13             ` Peter Stephenson
2019-01-21 22:53  3%           ` Martijn Dekker
2018-12-21 11:30  2%     ` The big kre zsh bug report Robert Elz
2018-12-21  2:28  5%   ` Robert Elz
2019-01-03 18:47  4% [PATCH/RFC] Document some 5.{4,5,6} features in NEWS dana
2019-02-27 18:30  2% Issues with fcntl() history file locking Philippe Troin
2019-02-27 21:27  0% ` Bart Schaefer
2019-02-28  6:36  3%   ` Philippe Troin
2019-03-01 18:56     zargs: Argument list too long gi1242+zsh
2019-03-01 21:04  0% ` dana
2019-03-21  4:13  2% [PATCH] rm: Accept -R as equivalent to -r Matthew Martin
2019-03-27 21:16     [RFC] adding zmktemp command Clinton Bunch
2019-03-28  9:38     ` Daniel Shahaf
2019-03-28 15:00       ` Clinton Bunch
2019-03-28 21:13  3%     ` Daniel Shahaf
2019-03-29 15:20  0%       ` Clinton Bunch
2019-04-24  8:13     Why does zsh un-ignores SIGQUIT? Stephane Chazelas
2019-04-24 16:13     ` Peter Stephenson
2019-04-25 21:17  5%   ` Stephane Chazelas
2019-05-22  0:01  3% PATCH: completion option updates Oliver Kiddle
     [not found]     <CGME20190617083759epcas1p11383850c11184f54e3a0b822c9ea12e6@epcas1p1.samsung.com>
2019-06-17  8:36     ` Issue with string slices joão marcos pereira bezerra
2019-06-17  8:51       ` Peter Stephenson
2019-06-17  9:05         ` Roman Perepelitsa
2019-06-18 18:12  3%       ` Stephane Chazelas
2019-06-18 20:34  3% $~var and backslash Stephane Chazelas
2019-06-28  9:36  3% */ globs and non-searchable directories Stephane Chazelas
2019-06-28 22:17  3% [PATCH] getopts: fix returned value on missing option argument Martijn Dekker
2019-07-21 15:08     Feature request: ZSH_XTRACEFD variable Timothée Mazzucotelli
2019-07-21 15:22     ` Peter Stephenson
2019-07-31 19:40       ` Timothée Mazzucotelli
2019-08-13 15:38         ` Peter Stephenson
2020-04-19 10:30           ` Timothée Mazzucotelli
2020-04-20 14:09             ` Peter Stephenson
2020-05-02 18:02               ` Timothée Mazzucotelli
2020-05-02 21:15                 ` Bart Schaefer
2020-05-03  0:06                   ` Daniel Shahaf
2020-05-03  6:01                     ` Stephane Chazelas
2020-05-03  7:07  4%                   ` Stephane Chazelas
2019-08-13  6:06     [Feature Request] Adding option to support triple quotes Aryn Starr
2019-08-14  9:54  3% ` Stephane Chazelas
2019-08-15 14:28  0%   ` Aryn Starr
2019-08-19  1:00     [bug] Problem with functions -s (string arg) math function & specific input Sebastian Gniazdowski
2019-08-19  7:01  3% ` Stephane Chazelas
2019-08-19  9:53  0%   ` Sebastian Gniazdowski
2019-08-24 20:16  5% RFC: &&/|| vs. & operator precedence Martijn Dekker
2019-09-05  8:49     arithmetic expression from outside YAMAGUCHI Takanori
2019-09-05 18:22     ` Bart Schaefer
2019-09-05 19:49  3%   ` Stephane Chazelas
2019-09-26 22:10  7% PATCH: completion of SELinux contexts Oliver Kiddle
     [not found]     <CGME20191016162708epcas2p37dc37493509eba8460bca124ffc3c32b@epcas2p3.samsung.com>
2019-10-16 16:26  8% ` [PATCH] POSIX compliant nice error checking _RuRo_ (Андрей Стоцкий)
2019-10-16 16:43  5%   ` Peter Stephenson
2019-10-28 13:34  2% Segfault with terminal width <= 6 Roman Perepelitsa
2019-11-24 23:41  5% [PATCH 0/1] Run final pipeline command in a subshell in sh mode brian m. carlson
2019-11-24 23:41  4% ` [PATCH 1/1] exec: run " brian m. carlson
2019-12-07 19:23  0% ` [PATCH 0/1] Run " brian m. carlson
     [not found]     <CGME20191210014119eucas1p155aa663c9f726ba5ca07a37c54829bec@eucas1p1.samsung.com>
2019-12-09 22:07  5% ` BUG: small posix glitch in ${x%*} Jan Grant
2019-12-10 10:48  4%   ` Peter Stephenson
2019-12-10 19:34  5% [PATCH] more documentation typos Martijn Dekker
     [not found]     <CGME20191212103905eucas1p2c9c79ee9488ab73b48db0de1ac11faf7@eucas1p2.samsung.com>
2019-12-12 10:38  5% ` [PATCH] POSIX_CD: disable stack entries Martijn Dekker
2019-12-12 10:50  3%   ` Peter Stephenson
2019-12-16 21:10     regexp-replace and ^, word boundary or look-behind operators Stephane Chazelas
2019-12-16 21:27  4% ` Stephane Chazelas
2019-12-17  7:38       ` Stephane Chazelas
2019-12-17 11:11         ` [PATCH] " Stephane Chazelas
2019-12-18  0:22  4%       ` Daniel Shahaf
2020-01-01 14:03 10%         ` [PATCH v2] " Stephane Chazelas
2019-12-31 18:39  2% Test failures in --disable-multibyte Daniel Shahaf
2020-01-03 20:08  5% [PATCH] Make --enable-gdbm default to false, rather than default to true with an unavoidable warning Daniel Shahaf
2020-01-08 10:39     [PATCH] find RLIM_NLIMITS correctly on Cygwin Jun T
2020-01-08 21:33     ` Daniel Shahaf
2020-01-09 10:32       ` Jun T
2020-01-09 13:15         ` Daniel Shahaf
2020-01-10 10:24           ` Jun T
2020-01-11 20:15             ` Daniel Shahaf
2020-01-13 11:00               ` Jun T
2020-01-13 16:42                 ` Daniel Shahaf
2020-01-14  4:44                   ` Jun T
2020-01-14 16:25                     ` Daniel Shahaf
2020-02-25  9:38  4%                   ` Jun T
2020-01-11 17:00     [bug] :P modifier and symlink loops Stephane Chazelas
2020-02-01 17:57     ` Stephane Chazelas
2020-02-02  8:10       ` Daniel Shahaf
2020-03-21 19:50         ` Daniel Shahaf
2020-03-26  0:38  3%       ` Daniel Shahaf
2020-01-12 22:40  3% [PATCH] New helper script for listing XFail tests Daniel Shahaf
     [not found]     <CGME20200128152042eucas1p20cbbeeb7cbe5f6abff08128ba8792661@eucas1p2.samsung.com>
2020-01-28 15:19  3% ` [PATCH] sh/ksh init: don't initialise lowercase parameters Martijn Dekker
2020-01-28 15:26  0%   ` Peter Stephenson
2020-01-29  8:49  3%   ` Daniel Shahaf
2020-01-29 18:36  0%     ` Bart Schaefer
2020-01-29 21:35  0%       ` Daniel Shahaf
2020-02-14 15:15     [PATCH] builtins: kill: Do not set signal on current shell when pid is empty Chris Down
2020-02-15 13:52     ` [PATCH ALTERNATE] builtins: kill: Do not set signal on current pgroup " Chris Down
2020-02-15 14:10  3%   ` Daniel Shahaf
2020-02-15 15:15  0%     ` Chris Down
2020-02-17  2:19     [BUG] Issue with set built-in in 5.8 (?) dana
2020-02-17  9:02  4% ` Daniel Shahaf
2020-02-18 20:01  3%   ` dana
2020-02-19  9:37  3%     ` Peter Stephenson
2020-02-19 19:25  0%       ` dana
2020-02-20  9:30  3%         ` Peter Stephenson
2020-04-08 10:39  3% Bug related to single-quoting a String Ronald Fischer
2020-04-11 15:15     glob qualifier '-' doesn't work correctly on dangling symlinks Vincent Lefevre
2020-04-11 17:34     ` Stephane Chazelas
2020-04-11 19:17       ` Vincent Lefevre
2020-04-11 20:37         ` Stephane Chazelas
2020-04-11 23:48           ` Vincent Lefevre
2020-04-12  1:21             ` Daniel Shahaf
2020-04-12  2:17               ` Vincent Lefevre
2020-04-12  7:09  4%             ` Stephane Chazelas
2020-04-12 14:25  0%               ` Vincent Lefevre
2020-04-12 17:34                     ` Stephane Chazelas
2020-04-12 23:38  3%                   ` Vincent Lefevre
2020-04-13 14:22                         ` Stephane Chazelas
2020-04-13 21:41                           ` Vincent Lefevre
2020-04-14  6:18                             ` Stephane Chazelas
2020-04-14 17:59  4%                           ` Vincent Lefevre
2020-04-30 20:40  3% completions for c99 Vincent Lefevre
2020-04-30 22:17  0% ` Daniel Shahaf
2020-05-01  0:15  3%   ` Vincent Lefevre
2020-05-06 13:31     local_traps doesn't restore traps set from functions Roman Perepelitsa
2020-05-07 21:21  5% ` Daniel Shahaf
2020-05-09 15:24       ` Peter Stephenson
2020-05-09 15:42         ` Daniel Shahaf
2020-05-09 19:51  4%       ` Daniel Shahaf
2020-05-08 20:36  2% [PATCH] return status 126 for execution failures other than 'not found' Martijn Dekker
2020-05-08 20:54  3% ` Bart Schaefer
2020-05-09 15:35  0% ` Daniel Shahaf
     [not found]     <CAOoO2vg=-9P7v=ATOzrbh6VF35o_xzK_-yF+EA6OgTHsQBik-A@mail.gmail.com>
     [not found]     ` <CAH+w=7bKpm7GO2ZhbA5Apr0sBDz5=kmX1=WK2X0nTcWxpkHPPQ@mail.gmail.com>
     [not found]       ` <ab8b7655-83e8-45b2-889f-043314faea62@www.fastmail.com>
     [not found]         ` <CAH+w=7aqAO37qpSvtmJXMPWq3zP=pUAZCe3i-BFyn0knRmFjVQ@mail.gmail.com>
2020-05-23 19:33           ` [PATCH?] Re: Autocorrect for commands with a hyphen (dash) in the name Bart Schaefer
2020-05-23 19:44             ` Peter Stephenson
2020-05-23 19:54               ` Bart Schaefer
2020-05-23 20:30                 ` Peter Stephenson
2020-05-23 21:18                   ` Bart Schaefer
2020-05-24 17:11                     ` Peter Stephenson
2020-05-24 18:39                       ` Bart Schaefer
2020-05-25  5:46                         ` Bart Schaefer
2020-05-25 16:35  3%                       ` Peter Stephenson
2020-06-05  1:53  4% [PATCH v2 0/1] Run pipeline command in subshell in sh mode brian m. carlson
2020-06-05  1:53  3% ` [PATCH v2] exec: run final pipeline command in a " brian m. carlson
2020-06-05 10:21  4%   ` Mikael Magnusson
2020-06-05 20:41  5%     ` brian m. carlson
2020-06-06  4:33  5%       ` [PATCH v2] exec: run final pipeline command in a subshell in sh modeZZ Daniel Shahaf
2020-06-06 16:28  5%         ` brian m. carlson
2020-06-07 11:29  4%           ` Daniel Shahaf
2020-06-05 18:18     bad math expression: illegal character: " Artur Renault
2020-06-05 21:01     ` Lawrence Velázquez
2020-06-17 23:21  3%   ` Bart Schaefer
2020-06-18 11:19  3%     ` Mikael Magnusson
2020-06-19  6:38  3%       ` Lawrence Velázquez
2020-06-19 15:35  0%         ` Sebastian Gniazdowski
2020-06-19 15:55  7%         ` Peter Stephenson
2020-06-23 23:51  3%           ` Bart Schaefer
     [not found]     <CGME20200603020919eucas1p13e26ebcbb335784d14bfb97b137f385a@eucas1p1.samsung.com>
2020-06-03  2:08     ` Any way to allow clobbering empty files when noclobber is set? Martin Tournoij
2020-06-03 12:04       ` Peter Stephenson
2020-06-04  5:06         ` Bart Schaefer
2020-06-04  5:41           ` Roman Perepelitsa
2020-06-05  2:07             ` Daniel Shahaf
     [not found]               ` <1941572212.466119.1591360860372@mail2.virginmedia.com>
     [not found]                 ` <e7f7dfe2-eb4a-457b-85fb-091935a74c0e@www.fastmail.com>
2020-06-06 11:57                   ` Peter Stephenson
2020-06-06 12:48  4%                 ` Roman Perepelitsa
2020-06-06 15:24  3%                   ` Bart Schaefer
2020-06-23  1:39  9% support for POSIX job control terminal management in zsh Godmar Back
2020-06-23  1:53 10% ` Bart Schaefer
2020-08-20 11:28  3% command -p should enable builtins not in path Vincent Lefevre
2020-08-21 13:03  0% ` Peter Stephenson
2020-08-21 15:49       ` Martijn Dekker
2020-08-21 16:08         ` Peter Stephenson
2020-08-21 16:35  3%       ` Martijn Dekker
2020-08-24 18:30  3%     ` Vincent Lefevre
2020-08-24 19:59  0%       ` Martijn Dekker
2020-10-02 13:17  3%         ` Vincent Lefevre
2020-08-21 15:47  3% ` Martijn Dekker
2020-10-09 20:07     [patch] Avoid race in zf_mkdir Matthew Martin
2020-10-09 20:24     ` Bart Schaefer
2020-10-09 20:35  4%   ` Roman Perepelitsa
2020-10-09 20:47  0%     ` Bart Schaefer
2020-10-09 20:53  0%       ` Matthew Martin
2020-10-09 21:22             ` Roman Perepelitsa
2020-10-09 21:27  0%           ` Bart Schaefer
2020-10-13 19:30     cannot `cd $_` to $_ containing spaces Soren Roth
2020-10-14  7:25  4% ` Roman Perepelitsa
2020-10-14 20:49  0%   ` Daniel Shahaf
2020-11-09  7:27     [bug] Special parameters $PATH and $path aren't kept in sync after emulating sh Brice Waegeneire
2020-11-09 10:07  3% ` Peter Stephenson
2020-11-12 17:38 12% [PATCH] builtin: trivial cleanup Felipe Contreras
2020-11-23 21:49  6% [PATCHv1] [long] improvements to limit/ulimit API and doc Stephane Chazelas
2020-11-25  0:35  0% ` Daniel Shahaf
2020-11-26  6:57  6%   ` [PATCHv2 1/2] [long] improvements to limit/ulimit API and doc ((un)limit in csh emulation) Stephane Chazelas
2020-11-25 23:43     ` [PATCHv1] [long] improvements to limit/ulimit API and doc Oliver Kiddle
2020-11-26 20:58  4%   ` [PATCHv2 2/2] [long] improvements to limit/ulimit API and doc (the rest) Stephane Chazelas
2020-11-27 16:39         ` Daniel Shahaf
2020-11-27 20:13  4%       ` Stephane Chazelas
2020-11-26 11:19  4% ` [PATCHv1] [long] improvements to limit/ulimit API and doc Jun T
2020-11-26 13:55  4%   ` Stephane Chazelas
2020-11-26 15:22  0%     ` Jun. T
2020-11-26 17:23  3%       ` Stephane Chazelas
2020-11-24 11:51     evallineno not loading Alexander Remus
2020-11-24 11:56     ` Roman Perepelitsa
2020-11-24 12:32  3%   ` Stephane Chazelas
2020-11-25  7:02     More rabbit-holes with unset variables Bart Schaefer
2020-11-25 13:19  4% ` Stephane Chazelas
2020-11-25 22:17       ` Felipe Contreras
2020-11-26  6:10  2%     ` Stephane Chazelas
2020-11-26 20:41  3%     ` Bart Schaefer
2020-11-26 21:20           ` Felipe Contreras
2020-11-26 22:41             ` Bart Schaefer
2020-11-26 23:45               ` Felipe Contreras
2020-11-27  0:09                 ` Bart Schaefer
2020-11-27  0:30  4%               ` Felipe Contreras
2020-11-27  0:51  3%                 ` Bart Schaefer
2020-11-27  1:30                       ` Felipe Contreras
2020-11-27 20:54  3%                     ` Bart Schaefer

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/zsh/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).