docs: def extract_captions(tex_file, sdir, image_list, primary=True):
      """Extract captions.
  
      Take the TeX file and the list of images in the tarball (which all,
      presumably, are used in the TeX file) and figure out which captions
      in the text are associated with which images
      :param: lines (list): list of lines of the TeX file
  
      :param: tex_file (string): the name of the TeX file which mentions
          the images
      :param: sdir (string): path to current sub-directory
      :param: image_list (list): list of images in tarball
      :param: primary (bool): is this the primary call to extract_caption?
  
      :return: images_and_captions_and_labels ([(string, string, list),
          (string, string, list), ...]):
          a list of tuples representing the names of images and their
          corresponding figure labels from the TeX file
      """
      if os.path.isdir(tex_file) or not os.path.exists(tex_file):
          return []
  
      lines = get_lines_from_file(tex_file)
  
      # possible figure lead-ins
      figure_head = u'\\begin{figure'  # also matches figure*
      figure_wrap_head = u'\\begin{wrapfigure'
      figure_tail = u'\\end{figure'  # also matches figure*
      figure_wrap_tail = u'\\end{wrapfigure'
      picture_head = u'\\begin{picture}'
      displaymath_head = u'\\begin{displaymath}'
      subfloat_head = u'\\subfloat'
      subfig_head = u'\\subfigure'
      includegraphics_head = u'\\includegraphics'
      epsfig_head = u'\\epsfig'
      input_head = u'\\input'
      # possible caption lead-ins
      caption_head = u'\\caption'
      figcaption_head = u'\\figcaption'
      label_head = u'\\label'
      rotate = u'rotate='
      angle = u'angle='
      eps_tail = u'.eps'
      ps_tail = u'.ps'
  
      doc_head = u'\\begin{document}'
      doc_tail = u'\\end{document}'
  
      extracted_image_data = []
      cur_image = ''
      caption = ''
      labels = []
      active_label = ""
  
      # cut out shit before the doc head
      if primary:
          for line_index in range(len(lines)):
              if lines[line_index].find(doc_head) < 0:
                  lines[line_index] = ''
              else:
                  break
  
      # are we using commas in filenames here?
      commas_okay = False
  
      for dummy1, dummy2, filenames in \
              os.walk(os.path.split(os.path.split(tex_file)[0])[0]):
          for filename in filenames:
              if filename.find(',') > -1:
                  commas_okay = True
                  break
  
      # a comment is a % not preceded by a \
      comment = re.compile("(?<!\\\\)%")
  
      for line_index in range(len(lines)):
          # get rid of pesky comments by splitting where the comment is
          # and keeping only the part before the %
          line = comment.split(lines[line_index])[0]
          line = line.strip()
          lines[line_index] = line
  
      in_figure_tag = 0
  
      for line_index in range(len(lines)):
          line = lines[line_index]
  
          if line == '':
              continue
          if line.find(doc_tail) > -1:
              break
  
          """
          FIGURE -
          structure of a figure:
          \begin{figure}
          \formatting...
          \includegraphics[someoptions]{FILENAME}
          \caption{CAPTION}  %caption and includegraphics may be switched!
          \end{figure}
          """
  
          index = max([line.find(figure_head), line.find(figure_wrap_head)])
          if index > -1:
              in_figure_tag = 1
              # some punks don't like to put things in the figure tag.  so we
              # just want to see if there is anything that is sitting outside
              # of it when we find it
              cur_image, caption, extracted_image_data = put_it_together(
                  cur_image, caption,
                  active_label, extracted_image_data,
                  line_index, lines)
  
          # here, you jerks, just make it so that it's fecking impossible to
          # figure out your damn inclusion types
  
          index = max([line.find(eps_tail), line.find(ps_tail),
                       line.find(epsfig_head)])
          if index > -1:
              if line.find(eps_tail) > -1 or line.find(ps_tail) > -1:
                  ext = True
              else:
                  ext = False
              filenames = intelligently_find_filenames(line, ext=ext,
                                                       commas_okay=commas_okay)
  
              # try to look ahead!  sometimes there are better matches after
              if line_index < len(lines) - 1:
                  filenames.extend(intelligently_find_filenames(
                      lines[line_index + 1],
                      commas_okay=commas_okay))
              if line_index < len(lines) - 2:
                  filenames.extend(intelligently_find_filenames(
                      lines[line_index + 2],
                      commas_okay=commas_okay))
  
              for filename in filenames:
                  filename = filename.encode('utf-8', 'ignore')
                  if cur_image == '':
                      cur_image = filename
                  elif type(cur_image) == list:
                      if type(cur_image[SUB_CAPTION_OR_IMAGE]) == list:
                          cur_image[SUB_CAPTION_OR_IMAGE].append(filename)
                      else:
                          cur_image[SUB_CAPTION_OR_IMAGE] = [filename]
                  else:
                      cur_image = ['', [cur_image, filename]]
  
          """
          Rotate and angle
          """
          index = max(line.find(rotate), line.find(angle))
          if index > -1:
              # which is the image associated to it?
              filenames = intelligently_find_filenames(line,
                                                       commas_okay=commas_okay)
              # try the line after and the line before
              if line_index + 1 < len(lines):
                  filenames.extend(intelligently_find_filenames(
                      lines[line_index + 1],
                      commas_okay=commas_okay))
              if line_index > 1:
                  filenames.extend(intelligently_find_filenames(
                      lines[line_index - 1],
                      commas_okay=commas_okay))
              already_tried = []
              for filename in filenames:
                  if filename != 'ERROR' and filename not in already_tried:
                      if rotate_image(filename, line, sdir, image_list):
                          break
                      already_tried.append(filename)
  
          """
          INCLUDEGRAPHICS -
          structure of includegraphics:
          \includegraphics[someoptions]{FILENAME}
          """
          index = line.find(includegraphics_head)
          if index > -1:
              open_curly, open_curly_line, close_curly, dummy = \
                  find_open_and_close_braces(line_index, index, '{', lines)
              filename = lines[open_curly_line][open_curly + 1:close_curly]
              if cur_image == '':
                  cur_image = filename
              elif type(cur_image) == list:
                  if type(cur_image[SUB_CAPTION_OR_IMAGE]) == list:
                      cur_image[SUB_CAPTION_OR_IMAGE].append(filename)
                  else:
                      cur_image[SUB_CAPTION_OR_IMAGE] = [filename]
              else:
                  cur_image = ['', [cur_image, filename]]
  
          """
          {\input{FILENAME}}
          \caption{CAPTION}
  
          This input is ambiguous, since input is also used for things like
          inclusion of data from other LaTeX files directly.
          """
          index = line.find(input_head)
          if index > -1:
              new_tex_names = intelligently_find_filenames(
                  line, TeX=True,
                  commas_okay=commas_okay)
              for new_tex_name in new_tex_names:
                  if new_tex_name != 'ERROR':
                      new_tex_file = get_tex_location(new_tex_name, tex_file)
                      if new_tex_file and primary:  # to kill recursion
                          extracted_image_data.extend(extract_captions(
                              new_tex_file, sdir,
                              image_list,
                              primary=False
                          ))
  
          """PICTURE"""
  
          index = line.find(picture_head)
          if index > -1:
              # structure of a picture:
              # \begin{picture}
              # ....not worrying about this now
              # print('found picture tag')
              # FIXME
              pass
  
          """DISPLAYMATH"""
  
          index = line.find(displaymath_head)
          if index > -1:
              # structure of a displaymath:
              # \begin{displaymath}
              # ....not worrying about this now
              # print('found displaymath tag')
              # FIXME
              pass
  
          """
          CAPTIONS -
          structure of a caption:
          \caption[someoptions]{CAPTION}
          or
          \caption{CAPTION}
          or
          \caption{{options}{CAPTION}}
          """
  
          index = max([line.find(caption_head), line.find(figcaption_head)])
          if index > -1:
              open_curly, open_curly_line, close_curly, close_curly_line = \
                  find_open_and_close_braces(line_index, index, '{', lines)
  
              cap_begin = open_curly + 1
  
              cur_caption = assemble_caption(
                  open_curly_line, cap_begin,
                  close_curly_line, close_curly, lines)
  
              if caption == '':
                  caption = cur_caption
              elif type(caption) == list:
                  if type(caption[SUB_CAPTION_OR_IMAGE]) == list:
                      caption[SUB_CAPTION_OR_IMAGE].append(cur_caption)
                  else:
                      caption[SUB_CAPTION_OR_IMAGE] = [cur_caption]
              elif caption != cur_caption:
                  caption = ['', [caption, cur_caption]]
  
          """
          SUBFLOATS -
          structure of a subfloat (inside of a figure tag):
          \subfloat[CAPTION]{options{FILENAME}}
  
          also associated with the overall caption of the enclosing figure
          """
  
          index = line.find(subfloat_head)
          if index > -1:
              # if we are dealing with subfloats, we need a different
              # sort of structure to keep track of captions and subcaptions
              if not isinstance(cur_image, list):
                  cur_image = [cur_image, []]
              if not isinstance(caption, list):
                  caption = [caption, []]
  
              open_square, open_square_line, close_square, close_square_line = \
                  find_open_and_close_braces(line_index, index, '[', lines)
              cap_begin = open_square + 1
  
              sub_caption = assemble_caption(
                  open_square_line,
                  cap_begin, close_square_line, close_square, lines)
              caption[SUB_CAPTION_OR_IMAGE].append(sub_caption)
  
              open_curly, open_curly_line, close_curly, dummy = \
                  find_open_and_close_braces(close_square_line,
                                             close_square, '{', lines)
              sub_image = lines[open_curly_line][open_curly + 1:close_curly]
  
              cur_image[SUB_CAPTION_OR_IMAGE].append(sub_image)
  
          """
          SUBFIGURES -
          structure of a subfigure (inside a figure tag):
          \subfigure[CAPTION]{
          \includegraphics[options]{FILENAME}}
  
          also associated with the overall caption of the enclosing figure
          """
  
          index = line.find(subfig_head)
          if index > -1:
              # like with subfloats, we need a different structure for keepin
              # track of this stuff
              if type(cur_image) != list:
                  cur_image = [cur_image, []]
              if type(caption) != list:
                  caption = [caption, []]
  
              open_square, open_square_line, close_square, close_square_line = \
                  find_open_and_close_braces(line_index, index, '[', lines)
              cap_begin = open_square + 1
  
              sub_caption = assemble_caption(open_square_line,
                                             cap_begin, close_square_line,
                                             close_square, lines)
              caption[SUB_CAPTION_OR_IMAGE].append(sub_caption)
  
              index_cpy = index
  
              # find the graphics tag to get the filename
              # it is okay if we eat lines here
              index = line.find(includegraphics_head)
              while index == -1 and (line_index + 1) < len(lines):
                  line_index += 1
                  line = lines[line_index]
                  index = line.find(includegraphics_head)
              if line_index == len(lines):
                  # didn't find the image name on line
                  line_index = index_cpy
  
              open_curly, open_curly_line, close_curly, dummy = \
                  find_open_and_close_braces(line_index,
                                             index, '{', lines)
              sub_image = lines[open_curly_line][open_curly + 1:close_curly]
  
              cur_image[SUB_CAPTION_OR_IMAGE].append(sub_image)
  
          """
          LABELS -
          structure of a label:
          \label{somelabelnamewhichprobablyincludesacolon}
  
          Labels are used to tag images and will later be used in ref tags
          to reference them.  This is interesting because in effect the refs
          to a plot are additional caption for it.
  
          Notes: labels can be used for many more things than just plots.
          We'll have to experiment with how to best associate a label with an
          image.. if it's in the caption, it's easy.  If it's in a figure, it's
          still okay... but the images that aren't in figure tags are numerous.
          """
          index = line.find(label_head)
          if index > -1 and in_figure_tag:
              open_curly, open_curly_line, close_curly, dummy =\
                  find_open_and_close_braces(line_index,
                                             index, '{', lines)
              label = lines[open_curly_line][open_curly + 1:close_curly]
              if label not in labels:
                  active_label = label
              labels.append(label)
  
          """
          FIGURE
  
          important: we put the check for the end of the figure at the end
          of the loop in case some pathological person puts everything in one
          line
          """
          index = max([
              line.find(figure_tail),
              line.find(figure_wrap_tail),
              line.find(doc_tail)
          ])
          if index > -1:
              in_figure_tag = 0
              cur_image, caption, extracted_image_data = \
                  put_it_together(cur_image, caption, active_label,
                                  extracted_image_data,
                                  line_index, lines)
          """
          END DOCUMENT
  
          we shouldn't look at anything after the end document tag is found
          """
  
          index = line.find(doc_tail)
          if index > -1:
              break
  
      return extracted_image_data